org.mbte.groovypp.compiler.transformers.PropertyExpressionTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.mbte.groovypp.compiler.transformers.PropertyExpressionTransformer.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.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.mbte.groovypp.compiler.bytecode.BytecodeExpr;
import org.mbte.groovypp.compiler.bytecode.InnerThisBytecodeExpr;
import org.mbte.groovypp.compiler.bytecode.PropertyUtil;
import org.mbte.groovypp.compiler.bytecode.ResolvedMethodBytecodeExpr;
import org.mbte.groovypp.compiler.bytecode.UnresolvedLeftExpr;
import org.mbte.groovypp.compiler.transformers.ExprTransformer;
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 {
            if (compiler.policy == TypePolicy.STATIC) {
                compiler.addError("Non-static property name", originalProperty);
                return null;
            } else {
                return new UnresolvedPropertyBytecodeExpr(exp,
                        (BytecodeExpr) compiler.transform(exp.getObjectExpression()),
                        (BytecodeExpr) compiler.transform(originalProperty), compiler);
            }
        } 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 BytecodeExpr 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 (thisType != null && thisType.getName().endsWith("$TraitImpl")) {
                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);
                }
            };
        }
    }
}