/* * 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.CastExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.classgen.BytecodeHelper; import org.codehaus.groovy.syntax.Token; import org.codehaus.groovy.syntax.Types; import org.mbte.groovypp.compiler.AccessibilityCheck; 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 ResolvedFieldBytecodeExpr extends ResolvedLeftExpr { private final FieldNode fieldNode; private final BytecodeExpr object; private CompilerTransformer compiler; private ASTNode parent; private final BytecodeExpr value; public ResolvedFieldBytecodeExpr(ASTNode parent, FieldNode fieldNode, BytecodeExpr object, BytecodeExpr value, CompilerTransformer compiler) { super(parent, getType(object, fieldNode)); this.parent = parent; this.fieldNode = fieldNode; this.object = object; this.compiler = compiler; this.value = value != null ? compiler.cast(value, fieldNode.getType() ): null; } private void checkAssignment() { if (fieldNode.isFinal() && !compiler.methodNode.getName().equals(fieldNode.isStatic() ? "<clinit>" : "<init>")) { compiler.addError("Cannot modify final field " + formatFieldName(), parent); } } public BytecodeExpr getObject() { return object; } public FieldNode getFieldNode() { return fieldNode; } private String formatFieldName() { return fieldNode.getDeclaringClass().getName() + "." + fieldNode.getName(); } private static ClassNode getType(BytecodeExpr object, FieldNode fieldNode) { ClassNode type = fieldNode.getType(); return object != null ? TypeUtil.getSubstitutedType(type, fieldNode.getDeclaringClass(), object.getType()) : type; } public void compile(MethodVisitor mv) { if (!AccessibilityCheck.isAccessible(fieldNode.getModifiers(), fieldNode.getDeclaringClass(), compiler.classNode, object == null ? null : object.getType())) { compiler.addError("Cannot access field " + formatFieldName(), parent); } int op; if (object != null) { object.visit(mv); if (fieldNode.isStatic()) { pop(object.getType(), mv); } else { box(object.getType(), mv); } } if (value == null) { op = fieldNode.isStatic() ? GETSTATIC : GETFIELD; } else { op = fieldNode.isStatic() ? PUTSTATIC : PUTFIELD; value.visit(mv); if (object != null && !fieldNode.isStatic()) dup_x1(value.getType(), mv); else dup(value.getType(), mv); } mv.visitFieldInsn(op, BytecodeHelper.getClassInternalName(fieldNode.getDeclaringClass()), fieldNode.getName(), BytecodeHelper.getTypeDescription(fieldNode.getType())); cast(TypeUtil.wrapSafely(fieldNode.getType()), TypeUtil.wrapSafely(getType()), mv); } public boolean isThis() { return object == null || object.isThis(); } public BytecodeExpr createAssign(ASTNode parent, BytecodeExpr right, CompilerTransformer compiler) { checkAssignment(); String propName = fieldNode.getName(); Object prop = PropertyUtil.resolveSetProperty(object != null ? object.getType() : fieldNode.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 BytecodeExpr createBinopAssign(final ASTNode parent, Token method, BytecodeExpr right, final CompilerTransformer compiler) { checkAssignment(); final BytecodeExpr fakeObject = object == null ? null : new BytecodeExpr(object, object.getType()) { @Override protected void compile(MethodVisitor mv) { } }; BytecodeExpr get = new ResolvedFieldBytecodeExpr(this, fieldNode, fakeObject, null, compiler); final BinaryExpression op = new BinaryExpression(get, method, right); op.setSourcePosition(parent); final BytecodeExpr transformedOp = (BytecodeExpr) compiler.transform(op); Object prop = PropertyUtil.resolveSetProperty(object != null ? object.getType() : fieldNode.getDeclaringClass(), fieldNode.getName(), getType(), compiler, isThis()); final CastExpression cast = new CastExpression(getType(), transformedOp); cast.setSourcePosition(right); right = (BytecodeExpr) compiler.transform(cast); final BytecodeExpr propExpr = PropertyUtil.createSetProperty(parent, compiler, fieldNode.getName(), fakeObject, right, 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) { checkAssignment(); 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 ResolvedFieldBytecodeExpr( exp, fieldNode, 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() )); } Object prop = PropertyUtil.resolveSetProperty(object != null ? object.getType() : fieldNode.getDeclaringClass(), fieldNode.getName(), getType(), compiler, isThis()); final CastExpression cast = new CastExpression(getType(), incDec); cast.setSourcePosition(exp); final BytecodeExpr put = PropertyUtil.createSetProperty(parent, compiler, fieldNode.getName(), dupObject, (BytecodeExpr) compiler.transform(cast), prop); return new BytecodeExpr(exp, getType()) { protected void compile(MethodVisitor mv) { put.visit(mv); } }; } public BytecodeExpr createPostfixOp(ASTNode exp, final int type, CompilerTransformer compiler) { checkAssignment(); 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 ResolvedFieldBytecodeExpr( exp, fieldNode, 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 (ResolvedFieldBytecodeExpr.this.object != null && !fieldNode.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 (ResolvedFieldBytecodeExpr.this.object != null && !fieldNode.isStatic()) dup_x1(get.getType(), mv); else dup(get.getType(), mv); } }, methodName, new ArgumentListExpression() )); } Object prop = PropertyUtil.resolveSetProperty(object != null ? object.getType() : fieldNode.getDeclaringClass(), fieldNode.getName(), getType(), compiler, isThis()); final CastExpression cast = new CastExpression(getType(), incDec); cast.setSourcePosition(exp); final BytecodeExpr put = PropertyUtil.createSetProperty(parent, compiler, fieldNode.getName(), dupObject, (BytecodeExpr) compiler.transform(cast), prop); /*final BytecodeExpr put = new ResolvedFieldBytecodeExpr( exp, fieldNode, dupObject, incDec, compiler );*/ return new BytecodeExpr(exp, getType()) { protected void compile(MethodVisitor mv) { put.visit(mv); pop(put.getType(), mv); } }; } }