/* * 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.transformers; import groovy.lang.TypePolicy; import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.*; import org.codehaus.groovy.ast.stmt.ReturnStatement; import org.mbte.groovypp.compiler.CompilerTransformer; import org.mbte.groovypp.compiler.PresentationUtil; import org.mbte.groovypp.compiler.TypeUtil; import org.mbte.groovypp.compiler.bytecode.*; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.text.MessageFormat; public class PropertyExpressionTransformer extends ExprTransformer<PropertyExpression> { public Expression transform(PropertyExpression exp, CompilerTransformer compiler) { Expression originalProperty = exp.getProperty(); if (exp.isSpreadSafe()) { Parameter param = new Parameter(ClassHelper.OBJECT_TYPE, "$it"); VariableExpression ve = new VariableExpression(param); ve.setSourcePosition(originalProperty); PropertyExpression prop = new PropertyExpression(ve, originalProperty); prop.setSourcePosition(originalProperty); ReturnStatement retStat = new ReturnStatement(prop); retStat.setSourcePosition(originalProperty); ClosureExpression ce = new ClosureExpression(new Parameter[]{param}, retStat); ce.setVariableScope(new VariableScope(compiler.compileStack.getScope())); MethodCallExpression mce = new MethodCallExpression(exp.getObjectExpression(), "map", new ArgumentListExpression(ce)); mce.setSourcePosition(exp); return compiler.transform(mce); } if (exp.isSafe()) { return transformSafe(exp, compiler); } String propName; if (!(originalProperty instanceof ConstantExpression) || !(((ConstantExpression) originalProperty).getValue() instanceof String)) { if (originalProperty instanceof ClosureExpression) { final MethodCallExpression mce = new MethodCallExpression(exp.getObjectExpression(), "apply", new ArgumentListExpression(originalProperty)); mce.setSourcePosition(exp); return compiler.transform(mce); } else { compiler.addError("Non-static property name", originalProperty); return null; } } else { propName = (String) ((ConstantExpression) originalProperty).getValue(); } BytecodeExpr object; final ClassNode type; if (exp.getObjectExpression() instanceof ClassExpression) { Object prop = PropertyUtil.resolveGetProperty(ClassHelper.CLASS_Type, propName, compiler, false, false); if (prop != null) { type = ClassHelper.CLASS_Type; object = (BytecodeExpr) compiler.transform(exp.getObjectExpression()); return PropertyUtil.createGetProperty(exp, compiler, propName, type, object, prop); } else { type = TypeUtil.wrapSafely(exp.getObjectExpression().getType()); prop = PropertyUtil.resolveGetProperty(type, propName, compiler, true, false); object = null; return PropertyUtil.createGetProperty(exp, compiler, propName, type, object, prop); } } else { if (exp.getObjectExpression() instanceof VariableExpression && ((VariableExpression) exp.getObjectExpression()).getName().equals("this")) { if ((compiler.classNode instanceof InnerClassNode)) { return inCaseOfInner(exp, compiler, propName); } else { if (compiler.methodNode.isStatic()) { Parameter[] pp = compiler.methodNode.getParameters(); if (pp.length > 0 && "$self".equals(pp[0].getName())) { object = new BytecodeExpr(exp, pp[0].getType()) { protected void compile(MethodVisitor mv) { mv.visitVarInsn(ALOAD, 0); } }; type = object.getType(); } else { object = null; type = compiler.classNode; } } else { object = (BytecodeExpr) compiler.transform(exp.getObjectExpression()); type = TypeUtil.wrapSafely(object.getType()); } Object prop = PropertyUtil.resolveGetProperty(type, propName, compiler, false, object == null || object.isThis()); return PropertyUtil.createGetProperty(exp, compiler, propName, type, object, prop); } } else { object = (BytecodeExpr) compiler.transformToGround(exp.getObjectExpression()); type = object.getType(); Object prop = PropertyUtil.resolveGetProperty(type, propName, compiler, false, object.isThis()); if (prop == null) { MethodNode unboxing = TypeUtil.getReferenceUnboxingMethod(type); if (unboxing != null) { ClassNode t = TypeUtil.getSubstitutedType(unboxing.getReturnType(), unboxing.getDeclaringClass(), type); prop = PropertyUtil.resolveGetProperty(t, propName, compiler, false, false); if (prop != null) { object = ResolvedMethodBytecodeExpr.create(exp, unboxing, object, new ArgumentListExpression(), compiler); } } } return PropertyUtil.createGetProperty(exp, compiler, propName, type, object, prop); } } } private Expression inCaseOfInner(final PropertyExpression exp, final CompilerTransformer compiler, String propName) { BytecodeExpr object; ClassNode thisType = compiler.classNode; boolean isThis = true; boolean onlyStatic = false; while (thisType != null) { Object prop = PropertyUtil.resolveGetProperty(thisType, propName, compiler, onlyStatic, isThis); if (prop != null) { boolean isStatic = PropertyUtil.isStatic(prop); if (!isStatic && exp.isStatic()) return null; object = isStatic ? null : new InnerThisBytecodeExpr(exp, thisType, compiler); return PropertyUtil.createGetProperty(exp, compiler, propName, thisType, object, prop); } isThis = false; if (thisType.implementsInterface(TypeUtil.DELEGATING)) { final MethodNode gd = compiler.findMethod(thisType, "getDelegate", ClassNode.EMPTY_ARRAY, false); if (gd != null) { final InnerThisBytecodeExpr innerThis = new InnerThisBytecodeExpr(exp, thisType, compiler); final ResolvedMethodBytecodeExpr delegate = ResolvedMethodBytecodeExpr.create(exp, gd, innerThis, ArgumentListExpression.EMPTY_ARGUMENTS, compiler); prop = PropertyUtil.resolveGetProperty(delegate.getType(), propName, compiler, onlyStatic, false); if (prop != null) { boolean isStatic = PropertyUtil.isStatic(prop); if (!isStatic && exp.isStatic()) return null; object = isStatic ? null : delegate; return PropertyUtil.createGetProperty(exp, compiler, propName, delegate.getType(), object, prop); } } } onlyStatic |= (thisType.getModifiers() & Opcodes.ACC_STATIC) != 0; thisType = thisType.getOuterClass(); } if (compiler.policy == TypePolicy.STATIC) { compiler.addError(MessageFormat.format("Cannot resolve property {0}.{1}", PresentationUtil.getText(compiler.classNode), propName), exp); return null; } else { object = new InnerThisBytecodeExpr(exp, compiler.classNode, compiler); return new UnresolvedLeftExpr(exp, null, object, propName); } } private Expression transformSafe(final PropertyExpression exp, CompilerTransformer compiler) { final BytecodeExpr object = (BytecodeExpr) compiler.transform(exp.getObjectExpression()); ClassNode type = TypeUtil.wrapSafely(object.getType()); final PropertyExpression newExp = new PropertyExpression(new BytecodeExpr(object, type) { protected void compile(MethodVisitor mv) { // nothing to do // expect parent on stack } }, exp.getProperty()); newExp.setSourcePosition(exp); final BytecodeExpr call = (BytecodeExpr) compiler.transform(newExp); if (ClassHelper.isPrimitiveType(call.getType())) { return new BytecodeExpr(exp,call.getType()) { protected void compile(MethodVisitor mv) { Label nullLabel = new Label(), endLabel = new Label (); object.visit(mv); mv.visitInsn(DUP); mv.visitJumpInsn(IFNULL, nullLabel); call.visit(mv); mv.visitJumpInsn(GOTO, endLabel); mv.visitLabel(nullLabel); mv.visitInsn(POP); if (call.getType() == ClassHelper.long_TYPE) { mv.visitInsn(LCONST_0); } else if (call.getType() == ClassHelper.float_TYPE) { mv.visitInsn(FCONST_0); } else if (call.getType() == ClassHelper.double_TYPE) { mv.visitInsn(DCONST_0); } else mv.visitInsn(ICONST_0); mv.visitLabel(endLabel); } }; } else { return new BytecodeExpr(exp,call.getType()) { protected void compile(MethodVisitor mv) { object.visit(mv); Label nullLabel = new Label(); mv.visitInsn(DUP); mv.visitJumpInsn(IFNULL, nullLabel); call.visit(mv); box(call.getType(), mv); mv.visitLabel(nullLabel); checkCast(getType(), mv); } }; } } }