/* * 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 groovy.lang.TypePolicy; import org.codehaus.groovy.ast.*; import org.codehaus.groovy.ast.expr.ArgumentListExpression; import org.codehaus.groovy.ast.expr.Expression; import org.codehaus.groovy.ast.expr.PropertyExpression; import org.codehaus.groovy.ast.stmt.EmptyStatement; import org.codehaus.groovy.classgen.BytecodeHelper; import org.codehaus.groovy.classgen.Verifier; 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; import org.objectweb.asm.Opcodes; public class PropertyUtil { public static final Object GET_MAP = new Object (); public static BytecodeExpr createGetProperty(final PropertyExpression exp, final CompilerTransformer compiler, String propName, ClassNode type, final BytecodeExpr object, Object prop) { if (prop instanceof MethodNode) { MethodNode method = (MethodNode) prop; if ((method.getModifiers() & Opcodes.ACC_PRIVATE) != 0 && method.getDeclaringClass() != compiler.classNode) { MethodNode delegate = compiler.context.getMethodDelegate(method); new ResolvedGetterBytecodeExpr(exp, delegate, object, compiler, propName, type); } return new ResolvedGetterBytecodeExpr(exp, method, object, compiler, propName, type); } if (prop instanceof PropertyNode) { return new ResolvedPropertyBytecodeExpr(exp, (PropertyNode) prop, object, null, compiler); } if (prop instanceof FieldNode) { FieldNode field = (FieldNode) prop; if ((field.getModifiers() & Opcodes.ACC_PRIVATE) != 0 && field.getDeclaringClass() != compiler.classNode && AccessibilityCheck.isAccessible(field.getModifiers(), field.getDeclaringClass(), compiler.classNode, null)) { MethodNode getter = compiler.context.getFieldGetter(field); return new ResolvedGetterBytecodeExpr.Accessor(field, exp, getter, object, compiler, type); } return new ResolvedFieldBytecodeExpr(exp, field, object, null, compiler); } if (object == null && "this".equals(propName)) { ClassNode curr = compiler.classNode; while (curr != null) { final FieldNode field = curr.getDeclaredField("this$0"); if (field == null) break; compiler.context.setOuterClassInstanceUsed(curr); curr = field.getType(); if (curr.equals(exp.getObjectExpression().getType())) { return new BytecodeExpr(exp, curr){ protected void compile(MethodVisitor mv) { ClassNode cur = compiler.classNode; mv.visitVarInsn(ALOAD, 0); while (!cur.equals(exp.getObjectExpression().getType())) { final FieldNode field = cur.getDeclaredField("this$0"); mv.visitFieldInsn(GETFIELD, BytecodeHelper.getClassInternalName(cur), "this$0", BytecodeHelper.getTypeDescription(field.getType())); cur = field.getType(); } } }; } } return null; } if (object != null && object.getType().isArray() && "length".equals(propName)) { return new BytecodeExpr(exp, ClassHelper.int_TYPE) { protected void compile(MethodVisitor mv) { object.visit(mv); mv.visitInsn(ARRAYLENGTH); } }; } if (prop == GET_MAP) { return new ResolvedLeftMapExpr(exp, object, propName); } final Expression anchor = exp.isImplicitThis() ? exp : exp.getProperty(); return dynamicOrFail(anchor, compiler, propName, type, object, null, "find"); } public static BytecodeExpr createSetProperty(ASTNode parent, CompilerTransformer compiler, String propName, BytecodeExpr object, BytecodeExpr value, Object prop) { if (prop instanceof MethodNode) { return new ResolvedMethodBytecodeExpr.Setter(parent, (MethodNode) prop, object, new ArgumentListExpression(value), compiler); } if (prop instanceof PropertyNode) { final PropertyNode propertyNode = (PropertyNode) prop; if ((propertyNode.getModifiers() & Opcodes.ACC_FINAL) != 0) { final FieldNode fieldNode = compiler.findField(propertyNode.getDeclaringClass(), propName); return new ResolvedFieldBytecodeExpr(parent, fieldNode, object, value, compiler); } return new ResolvedPropertyBytecodeExpr(parent, propertyNode, object, value, compiler); } if (prop instanceof FieldNode) { final FieldNode field = (FieldNode) prop; if ((field.getModifiers() & Opcodes.ACC_PRIVATE) != 0 && field.getDeclaringClass() != compiler.classNode) { MethodNode setter = compiler.context.getFieldSetter(field); return new ResolvedMethodBytecodeExpr.Setter(parent, setter, object, new ArgumentListExpression(value), compiler); } return new ResolvedFieldBytecodeExpr(parent, field, object, value, compiler); } final ClassNode type = object != null ? object.getType() : compiler.classNode; return dynamicOrFail(parent, compiler, propName, type, object, value, "assign"); } public static EmptyStatement NO_CODE = new EmptyStatement(); public static Object resolveGetProperty(ClassNode type, String name, CompilerTransformer compiler, boolean onlyStatic, boolean isSameObject) { final FieldNode field = compiler.findField(type, name); isSameObject &= !isTraitImpl(type); if (field != null && field.getDeclaringClass() == compiler.classNode && isSameObject) return field; String getterName = "get" + Verifier.capitalize(name); MethodNode mn = compiler.findMethod(type, getterName, ClassNode.EMPTY_ARRAY, false); if (mn != null && !mn.isAbstract() && (!onlyStatic || mn.isStatic())) { return mn; } if (mn == null) { getterName = "is" + Verifier.capitalize(name); mn = compiler.findMethod(type, getterName, ClassNode.EMPTY_ARRAY, false); if (mn != null && !mn.isAbstract() && mn.getReturnType().equals(ClassHelper.boolean_TYPE) && (!onlyStatic || mn.isStatic())) { return mn; } } final PropertyNode pnode = compiler.findProperty(type, name); if (pnode != null && (!onlyStatic || pnode.isStatic())) { return pnode; } if (mn != null && (!onlyStatic || mn.isStatic())) return mn; if (field != null && (!onlyStatic || field.isStatic())) return field; final String setterName = "set" + Verifier.capitalize(name); mn = compiler.findMethod(type, setterName, new ClassNode[]{TypeUtil.NULL_TYPE}, false); if (mn != null && (!onlyStatic || mn.isStatic()) && mn.getReturnType() == ClassHelper.VOID_TYPE) { final PropertyNode res = new PropertyNode(name, mn.getModifiers(), mn.getParameters()[0].getType(), mn.getDeclaringClass(), null, NO_CODE, null); res.setDeclaringClass(mn.getDeclaringClass()); return res; } if (!onlyStatic && (type.implementsInterface(ClassHelper.MAP_TYPE) || type.equals(ClassHelper.MAP_TYPE)) ) { return GET_MAP; } return null; } public static Object resolveSetProperty(ClassNode type, String name, ClassNode arg, CompilerTransformer compiler, boolean isSameObject) { FieldNode field = compiler.findField(type, name); isSameObject &= !isTraitImpl(type); if (field != null && field.getDeclaringClass() == compiler.classNode && isSameObject) return field; final String setterName = "set" + Verifier.capitalize(name); MethodNode mn = compiler.findMethod(type, setterName, new ClassNode[]{arg}, false); if (mn != null && mn.getReturnType() == ClassHelper.VOID_TYPE) { return mn; } final PropertyNode pnode = type.getProperty(name); if (pnode != null && (pnode.getModifiers() & Opcodes.ACC_FINAL) == 0) { return pnode; } if (field != null && (field.getModifiers() & Opcodes.ACC_FINAL) != 0) { if (field.getDeclaringClass() != compiler.classNode && !isFieldInitializer(compiler.methodNode)) return null; } return field; } private static boolean isFieldInitializer(MethodNode methodNode) { return methodNode instanceof ConstructorNode || methodNode.isStaticConstructor(); } private static boolean isTraitImpl(ClassNode type) { return type instanceof InnerClassNode && type.getName().endsWith("$TraitImpl"); } private static BytecodeExpr dynamicOrFail(ASTNode exp, CompilerTransformer compiler, String propName, ClassNode type, BytecodeExpr object, BytecodeExpr value, String cause) { if (compiler.policy == TypePolicy.STATIC) { compiler.addError("Cannot " + cause + " property " + propName + " of class " + PresentationUtil.getText(type), exp); return null; } else return createDynamicCall(exp, propName, object, value); } private static BytecodeExpr createDynamicCall(ASTNode exp, final String propName, final BytecodeExpr object, final BytecodeExpr value) { return new UnresolvedLeftExpr(exp, value, object, propName); } public static boolean isStatic(Object prop) { if (prop instanceof MethodNode) return ((MethodNode) prop).isStatic(); if (prop instanceof PropertyNode) return ((PropertyNode) prop).isStatic(); if (prop instanceof FieldNode) return ((FieldNode) prop).isStatic(); return false; } public static ClassNode getPropertyType(Object prop) { if (prop == GET_MAP) return ClassHelper.OBJECT_TYPE; if (prop instanceof FieldNode) return ((FieldNode) prop).getType(); if(prop instanceof PropertyNode) return ((PropertyNode) prop).getType(); return ((MethodNode) prop).getReturnType(); } }