/*
* Copyright 2009-2010 MBTE Sweden AB.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mbte.groovypp.compiler.bytecode;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.*;
import org.codehaus.groovy.syntax.Token;
import org.codehaus.groovy.syntax.Types;
import org.mbte.groovypp.compiler.CompilerTransformer;
import org.mbte.groovypp.compiler.PresentationUtil;
import org.mbte.groovypp.compiler.TypeUtil;
import org.objectweb.asm.MethodVisitor;
public class ResolvedArrayLikeBytecodeExpr extends ResolvedLeftExpr {
private final BytecodeExpr array;
private final BytecodeExpr index;
private MethodNode getter;
private final BytecodeExpr getterExpr;
private final MethodNode setter;
public ResolvedArrayLikeBytecodeExpr(ASTNode parent, BytecodeExpr array, BytecodeExpr index, MethodNode getter, CompilerTransformer compiler) {
super(parent, getter.getReturnType());
this.array = array;
this.index = index;
this.getter = getter;
this.getterExpr = ResolvedMethodBytecodeExpr.create(parent, getter, array, new ArgumentListExpression(index), compiler);
setType(getterExpr.getType());
this.setter = compiler.findMethod(array.getType(), "putAt", new ClassNode[]{index.getType(), getType()}, false);
}
protected void compile(MethodVisitor mv) {
getterExpr.visit(mv);
}
public BytecodeExpr createAssign(ASTNode parent, BytecodeExpr right, final CompilerTransformer compiler) {
if (!checkSetter(parent, compiler)) return null;
if (setter.getReturnType().equals(ClassHelper.VOID_TYPE)) {
final ClassNode type = setter.getParameters()[1].getType();
Expression cast = new CastExpression(type, right);
cast.setSourcePosition(right);
final BytecodeExpr finalCast = (BytecodeExpr) compiler.transform(cast);
final int [] v = new int [1];
BytecodeExpr value = new BytecodeExpr(right, type) {
protected void compile(MethodVisitor mv) {
finalCast.visit(mv);
dup(type, mv);
v [0] = compiler.compileStack.defineTemporaryVariable("$result", finalCast.getType(), true);
}
};
final ResolvedMethodBytecodeExpr call = ResolvedMethodBytecodeExpr.create(parent, setter, array, new ArgumentListExpression(index, value), compiler);
return new BytecodeExpr(parent, type) {
protected void compile(MethodVisitor mv) {
call.visit(mv);
load(type, v[0], mv);
compiler.compileStack.removeVar(v[0]);
}
};
}
else {
return ResolvedMethodBytecodeExpr.create(parent, setter, array, new ArgumentListExpression(index, right), compiler);
}
}
private boolean checkSetter(ASTNode exp, CompilerTransformer compiler) {
if (setter == null) {
compiler.addError("Cannot find method 'putAt' for type: " + PresentationUtil.getText(getType()), exp);
return false;
}
return true;
}
public BytecodeExpr createBinopAssign(ASTNode parent, Token method, final BytecodeExpr right, CompilerTransformer compiler) {
if (!checkSetter(parent, compiler)) return null;
final BytecodeExpr loadArr = new BytecodeExpr(this, array.getType()) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final BytecodeExpr loadIndex = new BytecodeExpr(this, ClassHelper.int_TYPE) {
@Override
protected void compile(MethodVisitor mv) {
}
};
ResolvedMethodBytecodeExpr load = ResolvedMethodBytecodeExpr.create(parent, getter, loadArr, new ArgumentListExpression(loadIndex), compiler);
final BinaryExpression op = new BinaryExpression(load, method, right);
op.setSourcePosition(parent);
final BytecodeExpr transformedOp = compiler.cast((BytecodeExpr) compiler.transform(op), getType());
final BytecodeExpr result = new BytecodeExpr(this, TypeUtil.wrapSafely(transformedOp.getType())) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final ResolvedMethodBytecodeExpr store = ResolvedMethodBytecodeExpr.create(parent, setter, loadArr, new ArgumentListExpression(loadIndex, result), compiler);
return new BytecodeExpr(parent, getType()) {
@Override
protected void compile(MethodVisitor mv) {
array.visit(mv);
index.visit(mv);
mv.visitInsn(DUP2);
transformedOp.visit(mv);
box(transformedOp.getType(), mv);
dup_x2(getType(), mv);
store.visit(mv);
if (!setter.getReturnType().equals(ClassHelper.VOID_TYPE)) {
pop(setter.getReturnType(), mv);
}
}
};
}
public BytecodeExpr createPrefixOp(ASTNode exp, final int type, CompilerTransformer compiler) {
if (!checkSetter(exp, compiler)) return null;
ClassNode vtype = getType();
final BytecodeExpr incDec;
if (TypeUtil.isNumericalType(vtype) && !vtype.equals(TypeUtil.Number_TYPE)) {
incDec = new BytecodeExpr(exp, vtype) {
protected void compile(MethodVisitor mv) {
final ClassNode primType = ClassHelper.getUnwrapper(getType());
if (getType() != primType)
unbox(primType, mv);
incOrDecPrimitive(primType, type, mv);
if (getType() != primType)
box(primType, mv);
}
};
}
else {
if (ClassHelper.isPrimitiveType(vtype))
vtype = TypeUtil.wrapSafely(vtype);
String methodName = type == Types.PLUS_PLUS ? "next" : "previous";
final MethodNode methodNode = compiler.findMethod(vtype, methodName, ClassNode.EMPTY_ARRAY, false);
if (methodNode == null) {
compiler.addError("Cannot find method " + methodName + "() for type " + PresentationUtil.getText(vtype), exp);
return null;
}
incDec = (BytecodeExpr) compiler.transform(new MethodCallExpression(
new BytecodeExpr(exp, vtype) {
protected void compile(MethodVisitor mv) {
}
},
methodName,
new ArgumentListExpression()
));
}
final BytecodeExpr loadArr = new BytecodeExpr(this, array.getType()) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final BytecodeExpr loadIndex = new BytecodeExpr(this, ClassHelper.int_TYPE) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final ResolvedMethodBytecodeExpr load = ResolvedMethodBytecodeExpr.create(exp, getter, loadArr, new ArgumentListExpression(loadIndex), compiler);
final BytecodeExpr result = new BytecodeExpr(this, TypeUtil.wrapSafely(incDec.getType())) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final ResolvedMethodBytecodeExpr store = ResolvedMethodBytecodeExpr.create(exp, setter, loadArr, new ArgumentListExpression(loadIndex, result), compiler);
return new BytecodeExpr(exp, getType()) {
@Override
protected void compile(MethodVisitor mv) {
array.visit(mv);
index.visit(mv);
mv.visitInsn(DUP2);
load.visit(mv);
incDec.visit(mv);
dup_x2(getType(), mv);
store.visit(mv);
}
};
}
public BytecodeExpr createPostfixOp(ASTNode exp, final int type, CompilerTransformer compiler) {
if (!checkSetter(exp, compiler)) return null;
ClassNode vtype = getType();
final BytecodeExpr incDec;
if (TypeUtil.isNumericalType(vtype) && !vtype.equals(TypeUtil.Number_TYPE)) {
incDec = new BytecodeExpr(exp, vtype) {
protected void compile(MethodVisitor mv) {
final ClassNode primType = ClassHelper.getUnwrapper(getType());
if (getType() != primType)
unbox(primType, mv);
incOrDecPrimitive(primType, type, mv);
if (getType() != primType)
box(primType, mv);
}
};
}
else {
if (ClassHelper.isPrimitiveType(vtype))
vtype = TypeUtil.wrapSafely(vtype);
String methodName = type == Types.PLUS_PLUS ? "next" : "previous";
final MethodNode methodNode = compiler.findMethod(vtype, methodName, ClassNode.EMPTY_ARRAY, false);
if (methodNode == null) {
compiler.addError("Cannot find method " + methodName + "() for type " + PresentationUtil.getText(vtype), exp);
return null;
}
incDec = (BytecodeExpr) compiler.transform(new MethodCallExpression(
new BytecodeExpr(exp, vtype) {
protected void compile(MethodVisitor mv) {
}
},
methodName,
new ArgumentListExpression()
));
}
final BytecodeExpr loadArr = new BytecodeExpr(this, array.getType()) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final BytecodeExpr loadIndex = new BytecodeExpr(this, ClassHelper.int_TYPE) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final ResolvedMethodBytecodeExpr load = ResolvedMethodBytecodeExpr.create(exp, getter, loadArr, new ArgumentListExpression(loadIndex), compiler);
final BytecodeExpr result = new BytecodeExpr(this, TypeUtil.wrapSafely(incDec.getType())) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final ResolvedMethodBytecodeExpr store = ResolvedMethodBytecodeExpr.create(exp, setter, loadArr, new ArgumentListExpression(loadIndex, result), compiler);
return new BytecodeExpr(exp, getType()) {
@Override
protected void compile(MethodVisitor mv) {
array.visit(mv);
index.visit(mv);
mv.visitInsn(DUP2);
load.visit(mv);
dup_x2(getType(), mv);
incDec.visit(mv);
store.visit(mv);
}
};
}
}