/*
* 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.*;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.classgen.BytecodeHelper;
import org.codehaus.groovy.classgen.Verifier;
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 ResolvedPropertyBytecodeExpr extends ResolvedLeftExpr {
private final PropertyNode propertyNode;
private final BytecodeExpr object;
private CompilerTransformer compiler;
private final String methodName;
private final BytecodeExpr bargs;
public ResolvedPropertyBytecodeExpr(ASTNode parent, PropertyNode propertyNode, BytecodeExpr object, BytecodeExpr bargs, CompilerTransformer compiler) {
super(parent, getType(object, propertyNode));
this.propertyNode = propertyNode;
this.object = object;
this.compiler = compiler;
this.bargs = bargs == null ? null : compiler.cast(bargs, getType());
if (bargs != null) {
methodName = "set" + Verifier.capitalize(propertyNode.getName());
} else {
methodName = "get" + Verifier.capitalize(propertyNode.getName());
}
}
private static ClassNode getType(BytecodeExpr object, PropertyNode propertyNode) {
ClassNode type = propertyNode.getType();
return object != null ? TypeUtil.getSubstitutedType(type,
propertyNode.getDeclaringClass(), object.getType()) : type;
}
public void compile(MethodVisitor mv) {
final String classInternalName;
final String methodDescriptor;
int op = INVOKEVIRTUAL;
if (propertyNode.getDeclaringClass().isInterface())
op = INVOKEINTERFACE;
if (propertyNode.isStatic())
op = INVOKESTATIC;
if (object != null) {
object.visit(mv);
box(object.getType(), mv);
}
if (propertyNode.isStatic() && object != null) {
pop(object.getType(), mv);
}
classInternalName = BytecodeHelper.getClassInternalName(propertyNode.getDeclaringClass());
if (methodName.startsWith("set")) {
bargs.visit(mv);
final ClassNode paramType = propertyNode.getType();
final ClassNode type = bargs.getType();
box(type, mv);
cast(TypeUtil.wrapSafely(type), TypeUtil.wrapSafely(paramType), mv);
unbox(paramType, mv);
if (!propertyNode.isStatic())
dup_x1(paramType, mv);
else
dup(paramType, mv);
methodDescriptor = BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, new Parameter[]{new Parameter(paramType, "")});
mv.visitMethodInsn(op, classInternalName, methodName, methodDescriptor);
} else {
if (propertyNode.getGetterBlock() == PropertyUtil.NO_CODE) {
compiler.addError("Cannot find property '" + propertyNode.getName() + "'", this);
}
methodDescriptor = BytecodeHelper.getMethodDescriptor(propertyNode.getType(), Parameter.EMPTY_ARRAY);
mv.visitMethodInsn(op, classInternalName, methodName, methodDescriptor);
cast(TypeUtil.wrapSafely(propertyNode.getType()), TypeUtil.wrapSafely(getType()), mv);
}
}
public BytecodeExpr createAssign(ASTNode parent, final BytecodeExpr right, CompilerTransformer compiler) {
return new ResolvedPropertyBytecodeExpr(parent, propertyNode, object, right, compiler);
}
public BytecodeExpr createBinopAssign(ASTNode parent, Token method, BytecodeExpr right, CompilerTransformer compiler) {
final BytecodeExpr fakeObject = new BytecodeExpr(object, object.getType()) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final BytecodeExpr dupObject = new BytecodeExpr(object, object.getType()) {
@Override
protected void compile(MethodVisitor mv) {
object.visit(mv);
dup(object.getType(), mv);
}
};
BytecodeExpr get = new ResolvedPropertyBytecodeExpr(
parent,
propertyNode,
dupObject,
null,
compiler);
final BinaryExpression op = new BinaryExpression(get, method, right);
op.setSourcePosition(parent);
final BytecodeExpr transformedOp = (BytecodeExpr) compiler.transform(op);
return new ResolvedPropertyBytecodeExpr(
parent,
propertyNode,
fakeObject,
transformedOp,
compiler);
}
public BytecodeExpr createPrefixOp(ASTNode exp, final int type, CompilerTransformer compiler) {
ClassNode vtype = getType();
final BytecodeExpr fakeObject = new BytecodeExpr(object, object.getType()) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final BytecodeExpr dupObject = new BytecodeExpr(object, object.getType()) {
@Override
protected void compile(MethodVisitor mv) {
if (object != null) {
object.visit(mv);
dup(object.getType(), mv);
}
}
};
final BytecodeExpr get = new ResolvedPropertyBytecodeExpr(
exp,
propertyNode,
fakeObject,
null,
compiler);
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());
get.visit(mv);
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, get.getType()) {
protected void compile(MethodVisitor mv) {
get.visit(mv);
}
},
methodName,
new ArgumentListExpression()
));
}
final BytecodeExpr put = new ResolvedPropertyBytecodeExpr(
exp,
propertyNode,
dupObject,
incDec,
compiler);
return new BytecodeExpr(exp, getType()) {
protected void compile(MethodVisitor mv) {
put.visit(mv);
}
};
}
public BytecodeExpr createPostfixOp(ASTNode exp, final int type, CompilerTransformer compiler) {
ClassNode vtype = getType();
final BytecodeExpr fakeObject = new BytecodeExpr(object, object.getType()) {
@Override
protected void compile(MethodVisitor mv) {
}
};
final BytecodeExpr dupObject = new BytecodeExpr(object, object.getType()) {
@Override
protected void compile(MethodVisitor mv) {
if (object != null) {
object.visit(mv);
dup(object.getType(), mv);
}
}
};
final BytecodeExpr get = new ResolvedPropertyBytecodeExpr(
exp,
propertyNode,
fakeObject,
null,
compiler);
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());
get.visit(mv);
if (ResolvedPropertyBytecodeExpr.this.object != null && !propertyNode.isStatic())
dup_x1(get.getType(), mv);
else
dup(get.getType(), mv);
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, get.getType()) {
protected void compile(MethodVisitor mv) {
get.visit(mv);
if (ResolvedPropertyBytecodeExpr.this.object != null && !propertyNode.isStatic())
dup_x1(get.getType(), mv);
else
dup(get.getType(), mv);
}
},
methodName,
new ArgumentListExpression()
));
}
final BytecodeExpr put = new ResolvedPropertyBytecodeExpr(
exp,
propertyNode,
dupObject,
incDec,
compiler);
return new BytecodeExpr(exp, getType()) {
protected void compile(MethodVisitor mv) {
put.visit(mv);
pop(put.getType(), mv);
}
};
}
}