org.mbte.groovypp.compiler.bytecode.PropertyUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.mbte.groovypp.compiler.bytecode.PropertyUtil.java

Source

/*
 * Copyright 2009-2011 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.*;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.classgen.Verifier;
import org.mbte.groovypp.compiler.*;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

public class PropertyUtil {
    public static final Object GET_MAP = new Object();

    public static class GetUnresolved {
        public MethodNode method;

        public GetUnresolved(MethodNode method) {
            this.method = method;
        }
    }

    public static class SetUnresolved extends GetUnresolved {
        public SetUnresolved(MethodNode method) {
            super(method);
        }
    }

    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);
        }

        if (prop instanceof GetUnresolved) {
            return new ResolvedLeftUnresolvedPropExpr(exp, object, propName, compiler, (GetUnresolved) prop);
        }

        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);
        }

        if (prop instanceof GetUnresolved) {
            final MethodCallExpression setUnresolvedProperty = new MethodCallExpression(object,
                    "setUnresolvedProperty", new ArgumentListExpression(new ConstantExpression(propName), value));
            setUnresolvedProperty.setSourcePosition(parent);
            return (BytecodeExpr) compiler.transform(setUnresolvedProperty);
        }

        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) {
            if ((type.implementsInterface(ClassHelper.MAP_TYPE) || type.equals(ClassHelper.MAP_TYPE))) {
                return GET_MAP;
            }

            MethodNode method = compiler.findMethod(type, "getUnresolvedProperty",
                    new ClassNode[] { ClassHelper.STRING_TYPE }, false);
            if (method != null) {
                return new GetUnresolved(method);
            }

            method = compiler.findMethod(type, "setUnresolvedProperty",
                    new ClassNode[] { ClassHelper.STRING_TYPE, TypeUtil.NULL_TYPE }, false);
            if (method != null) {
                return new SetUnresolved(method);
            }
        }

        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();
    }
}