/* * 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.ArgumentListExpression; import org.codehaus.groovy.ast.expr.BinaryExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.CastExpression; 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 ResolvedGetterBytecodeExpr extends ResolvedLeftExpr { private final MethodNode methodNode; private final BytecodeExpr object; private CompilerTransformer compiler; private String propName; private ClassNode qualType; private final BytecodeExpr getter; private static final ArgumentListExpression EMPTY_ARGS = new ArgumentListExpression(); public ResolvedGetterBytecodeExpr(ASTNode parent, MethodNode methodNode, BytecodeExpr object, CompilerTransformer compiler, String propName, ClassNode qualType) { super(parent, ResolvedMethodBytecodeExpr.getReturnType(methodNode, object, EMPTY_ARGS, compiler)); this.methodNode = methodNode; this.object = object; this.compiler = compiler; this.propName = propName; this.qualType = qualType; getter = ResolvedMethodBytecodeExpr.create( parent, methodNode, object, EMPTY_ARGS, compiler); setType(getter.getType()); } public FieldNode getFieldNode() { return compiler.findField(methodNode.getDeclaringClass(), propName); } protected void compile(MethodVisitor mv) { getter.visit(mv); } public BytecodeExpr createAssign(ASTNode parent, BytecodeExpr right, CompilerTransformer compiler) { Object prop = PropertyUtil.resolveSetProperty(object != null ? object.getType() : methodNode.getDeclaringClass(), propName, getType(), compiler, isThis()); final CastExpression cast = new CastExpression(getType(), right); cast.setSourcePosition(right); right = (BytecodeExpr) compiler.transform(cast); return PropertyUtil.createSetProperty(parent, compiler, propName, object, right, prop); } public boolean isThis() { return object == null || object.isThis(); } public BytecodeExpr createBinopAssign(ASTNode parent, Token method, BytecodeExpr right, CompilerTransformer compiler) { final BytecodeExpr fakeObject = object == null ? null : new BytecodeExpr(object, object.getType()) { @Override protected void compile(MethodVisitor mv) { } }; BytecodeExpr get = ResolvedMethodBytecodeExpr.create( parent, methodNode, fakeObject, EMPTY_ARGS, compiler); final BinaryExpression op = new BinaryExpression(get, method, right); op.setSourcePosition(parent); final BytecodeExpr transformedOp = (BytecodeExpr) compiler.transform(op); Object prop = PropertyUtil.resolveSetProperty(qualType, propName, transformedOp.getType(), compiler, isThis()); final BytecodeExpr propExpr = PropertyUtil.createSetProperty(parent, compiler, propName, fakeObject, transformedOp, prop); return object == null ? propExpr : new BytecodeExpr(parent, propExpr.getType()) { protected void compile(MethodVisitor mv) { object.visit(mv); mv.visitInsn(DUP); propExpr.visit(mv); } }; } public BytecodeExpr createPrefixOp(ASTNode exp, final int type, CompilerTransformer compiler) { ClassNode vtype = getType(); final BytecodeExpr fakeObject = object == null ? null : new BytecodeExpr(object, object.getType()) { @Override protected void compile(MethodVisitor mv) { } }; final BytecodeExpr dupObject = object == null ? null : new BytecodeExpr(object, object.getType()) { @Override protected void compile(MethodVisitor mv) { object.visit(mv); dup(object.getType(), mv); } }; final BytecodeExpr get = new ResolvedGetterBytecodeExpr( exp, methodNode, fakeObject, compiler, propName, qualType); 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() )); } Object prop = PropertyUtil.resolveSetProperty(methodNode.getDeclaringClass(), propName, incDec.getType(), compiler, isThis()); return PropertyUtil.createSetProperty(exp, compiler, propName, dupObject, incDec, prop); } public BytecodeExpr createPostfixOp(ASTNode exp, final int type, CompilerTransformer compiler) { ClassNode vtype = getType(); final BytecodeExpr fakeObject = object == null ? null : new BytecodeExpr(object, object.getType()) { @Override protected void compile(MethodVisitor mv) { } }; final BytecodeExpr dupObject = object == null ? null : new BytecodeExpr(object, object.getType()) { @Override protected void compile(MethodVisitor mv) { object.visit(mv); dup(object.getType(), mv); } }; final BytecodeExpr get = new ResolvedGetterBytecodeExpr( exp, methodNode, fakeObject, compiler, propName, qualType); 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 (object != null && !methodNode.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 (object != null && !ResolvedGetterBytecodeExpr.this.methodNode.isStatic()) dup_x1(get.getType(), mv); else dup(get.getType(), mv); } }, methodName, new ArgumentListExpression() )); } Object prop = PropertyUtil.resolveSetProperty(methodNode.getDeclaringClass(), propName, incDec.getType(), compiler, isThis()); final BytecodeExpr put = PropertyUtil.createSetProperty(exp, compiler, propName, dupObject, incDec, prop); return new BytecodeExpr(exp, getType()) { protected void compile(MethodVisitor mv) { put.visit(mv); pop(put.getType(), mv); } }; } public BytecodeExpr getObject() { return object; } public static class Accessor extends ResolvedGetterBytecodeExpr { private FieldNode fieldNode; public Accessor(FieldNode fieldNode, ASTNode parent, MethodNode methodNode, BytecodeExpr object, CompilerTransformer compiler, ClassNode qualType) { super(parent, methodNode, object, compiler, fieldNode.getName(), qualType); this.fieldNode = fieldNode; } public FieldNode getFieldNode() { return fieldNode; } } }