You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:29 UTC

[18/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
new file mode 100644
index 0000000..1e434cb
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -0,0 +1,764 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.decompiled.DecompiledClassNode;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ArrayExpression;
+import org.codehaus.groovy.ast.expr.AttributeExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.classgen.asm.CallSiteWriter;
+import org.codehaus.groovy.classgen.asm.CompileStack;
+import org.codehaus.groovy.classgen.asm.ExpressionAsVariableSlot;
+import org.codehaus.groovy.classgen.asm.InvocationWriter;
+import org.codehaus.groovy.classgen.asm.MethodCallerMultiAdapter;
+import org.codehaus.groovy.classgen.asm.OperandStack;
+import org.codehaus.groovy.classgen.asm.TypeChooser;
+import org.codehaus.groovy.classgen.asm.VariableSlotLoader;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
+import org.codehaus.groovy.transform.sc.StaticCompilationVisitor;
+import org.codehaus.groovy.transform.sc.TemporaryVariableExpression;
+import org.codehaus.groovy.transform.stc.ExtensionMethodNode;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.OBJECT_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.getWrapper;
+import static org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS;
+import static org.objectweb.asm.Opcodes.ACONST_NULL;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.IFNULL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+
+public class StaticInvocationWriter extends InvocationWriter {
+    private static final ClassNode INVOKERHELPER_CLASSNODE = ClassHelper.make(InvokerHelper.class);
+    private static final Expression INVOKERHELER_RECEIVER = new ClassExpression(INVOKERHELPER_CLASSNODE);
+    private static final MethodNode INVOKERHELPER_INVOKEMETHOD = INVOKERHELPER_CLASSNODE.getMethod(
+            "invokeMethodSafe",
+            new Parameter[]{
+                    new Parameter(ClassHelper.OBJECT_TYPE, "object"),
+                    new Parameter(ClassHelper.STRING_TYPE, "name"),
+                    new Parameter(ClassHelper.OBJECT_TYPE, "args")
+            }
+    );
+
+    private static final MethodNode INVOKERHELPER_INVOKESTATICMETHOD = INVOKERHELPER_CLASSNODE.getMethod(
+            "invokeStaticMethod",
+            new Parameter[]{
+                    new Parameter(ClassHelper.CLASS_Type, "clazz"),
+                    new Parameter(ClassHelper.STRING_TYPE, "name"),
+                    new Parameter(ClassHelper.OBJECT_TYPE, "args")
+            }
+    );
+
+    private final AtomicInteger labelCounter = new AtomicInteger();
+
+    final WriterController controller;
+
+    private MethodCallExpression currentCall;
+
+    public StaticInvocationWriter(WriterController wc) {
+        super(wc);
+        controller = wc;
+    }
+
+    @Override
+    protected boolean makeDirectCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean implicitThis, final boolean containsSpreadExpression) {
+        if (origin instanceof MethodCallExpression &&
+                receiver instanceof VariableExpression &&
+                ((VariableExpression) receiver).isSuperExpression()) {
+            ClassNode superClass = receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER);
+            if (superClass!=null && !controller.getCompileStack().isLHS()) {
+                // GROOVY-7300
+                MethodCallExpression mce = (MethodCallExpression) origin;
+                MethodNode node = superClass.getDeclaredMethod(mce.getMethodAsString(), Parameter.EMPTY_ARRAY);
+                mce.setMethodTarget(node);
+            }
+        }
+        return super.makeDirectCall(origin, receiver, message, arguments, adapter, implicitThis, containsSpreadExpression);
+    }
+
+    @Override
+    public void writeInvokeMethod(final MethodCallExpression call) {
+        MethodCallExpression old = currentCall;
+        currentCall = call;
+        super.writeInvokeMethod(call);
+        currentCall = old;
+    }
+
+    @Override
+    public void writeInvokeConstructor(final ConstructorCallExpression call) {
+        MethodNode mn = call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+        if (mn == null) {
+            super.writeInvokeConstructor(call);
+            return;
+        }
+        if (writeAICCall(call)) return;
+        ConstructorNode cn;
+        if (mn instanceof ConstructorNode) {
+            cn = (ConstructorNode) mn;
+        } else {
+            cn = new ConstructorNode(mn.getModifiers(), mn.getParameters(), mn.getExceptions(), mn.getCode());
+            cn.setDeclaringClass(mn.getDeclaringClass());
+        }
+        TupleExpression args = makeArgumentList(call.getArguments());
+        if (cn.isPrivate()) {
+            ClassNode classNode = controller.getClassNode();
+            ClassNode declaringClass = cn.getDeclaringClass();
+            if (declaringClass != classNode) {
+                MethodNode bridge = null;
+                if (call.getNodeMetaData(StaticTypesMarker.PV_METHODS_ACCESS) != null) {
+                    Map<MethodNode, MethodNode> bridgeMethods = declaringClass.getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS);
+                    bridge = bridgeMethods != null ? bridgeMethods.get(cn) : null;
+                }
+                if (bridge != null && bridge instanceof ConstructorNode) {
+                    ArgumentListExpression newArgs = new ArgumentListExpression(new ConstantExpression(null));
+                    for (Expression arg: args) {
+                        newArgs.addExpression(arg);
+                    }
+                    cn = (ConstructorNode) bridge;
+                    args = newArgs;
+                } else {
+                    controller.getSourceUnit().addError(new SyntaxException("Cannot call private constructor for " + declaringClass.toString(false) +
+                            " from class " + classNode.toString(false), call.getLineNumber(), call.getColumnNumber(), mn.getLastLineNumber(), call.getLastColumnNumber()));
+                }
+            }
+        }
+
+        String ownerDescriptor = prepareConstructorCall(cn);
+        int before = controller.getOperandStack().getStackLength();
+        loadArguments(args.getExpressions(), cn.getParameters());
+        finnishConstructorCall(cn, ownerDescriptor, controller.getOperandStack().getStackLength() - before);
+    }
+
+    @Override
+    public void writeSpecialConstructorCall(final ConstructorCallExpression call) {
+        MethodNode mn = call.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+        if (mn==null) {
+            super.writeSpecialConstructorCall(call);
+            return;
+        }
+        controller.getCompileStack().pushInSpecialConstructorCall();
+        ConstructorNode cn;
+        if (mn instanceof ConstructorNode) {
+            cn = (ConstructorNode) mn;
+        } else {
+            cn = new ConstructorNode(mn.getModifiers(), mn.getParameters(), mn.getExceptions(), mn.getCode());
+            cn.setDeclaringClass(mn.getDeclaringClass());
+        }
+        // load "this"
+        controller.getMethodVisitor().visitVarInsn(ALOAD, 0);
+        String ownerDescriptor = BytecodeHelper.getClassInternalName(cn.getDeclaringClass());
+        TupleExpression args = makeArgumentList(call.getArguments());
+        int before = controller.getOperandStack().getStackLength();
+        loadArguments(args.getExpressions(), cn.getParameters());
+        finnishConstructorCall(cn, ownerDescriptor, controller.getOperandStack().getStackLength() - before);
+        // on a special call, there's no object on stack
+        controller.getOperandStack().remove(1);
+        controller.getCompileStack().pop();
+    }
+
+    /**
+     * Attempts to make a direct method call on a bridge method, if it exists.
+     */
+    @Deprecated
+    protected boolean tryBridgeMethod(MethodNode target, Expression receiver, boolean implicitThis, TupleExpression args) {
+        return tryBridgeMethod(target, receiver, implicitThis, args, null);
+    }
+
+    /**
+     * Attempts to make a direct method call on a bridge method, if it exists.
+     */
+    protected boolean tryBridgeMethod(MethodNode target, Expression receiver, boolean implicitThis,
+                                      TupleExpression args, ClassNode thisClass) {
+        ClassNode lookupClassNode;
+        if (target.isProtected()) {
+            lookupClassNode = controller.getClassNode();
+            while (lookupClassNode != null && !lookupClassNode.isDerivedFrom(target.getDeclaringClass())) {
+                lookupClassNode = lookupClassNode.getOuterClass();
+            }
+            if (lookupClassNode == null) {
+                return false;
+            }
+        } else {
+            lookupClassNode = target.getDeclaringClass().redirect();
+        }
+        Map<MethodNode, MethodNode> bridges = lookupClassNode.getNodeMetaData(PRIVATE_BRIDGE_METHODS);
+        MethodNode bridge = bridges==null?null:bridges.get(target);
+        if (bridge != null) {
+            Expression fixedReceiver = receiver;
+            if (implicitThis) {
+                if (!controller.isInClosure()) {
+                    fixedReceiver = new PropertyExpression(new ClassExpression(lookupClassNode), "this");
+                } else if (thisClass != null) {
+                    ClassNode current = thisClass.getOuterClass();
+                    fixedReceiver = new VariableExpression("thisObject", current);
+                    // adjust for multiple levels of nesting if needed
+                    while (current != null && current instanceof InnerClassNode && !lookupClassNode.equals(current)) {
+                        FieldNode thisField = current.getField("this$0");
+                        current = current.getOuterClass();
+                        if (thisField != null) {
+                            fixedReceiver = new PropertyExpression(fixedReceiver, "this$0");
+                            fixedReceiver.setType(current);
+                        }
+                    }
+                }
+            }
+            ArgumentListExpression newArgs = new ArgumentListExpression(target.isStatic()?new ConstantExpression(null):fixedReceiver);
+            for (Expression expression : args.getExpressions()) {
+                newArgs.addExpression(expression);
+            }
+            return writeDirectMethodCall(bridge, implicitThis, fixedReceiver, newArgs);
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean writeDirectMethodCall(final MethodNode target, final boolean implicitThis, final Expression receiver, final TupleExpression args) {
+        if (target==null) return false;
+
+        if (target instanceof ExtensionMethodNode) {
+            ExtensionMethodNode emn = (ExtensionMethodNode) target;
+            MethodNode node = emn.getExtensionMethodNode();
+            String methodName = target.getName();
+
+            MethodVisitor mv = controller.getMethodVisitor();
+            int argumentsToRemove = 0;
+            List<Expression> argumentList = new LinkedList<Expression>(args.getExpressions());
+
+            if (emn.isStaticExtension()) {
+                // it's a static extension method
+                argumentList.add(0, ConstantExpression.NULL);
+            } else {
+                argumentList.add(0, receiver);
+            }
+
+            Parameter[] parameters = node.getParameters();
+            loadArguments(argumentList, parameters);
+
+            String owner = BytecodeHelper.getClassInternalName(node.getDeclaringClass());
+            String desc = BytecodeHelper.getMethodDescriptor(target.getReturnType(), parameters);
+            mv.visitMethodInsn(INVOKESTATIC, owner, methodName, desc, false);
+            ClassNode ret = target.getReturnType().redirect();
+            if (ret == ClassHelper.VOID_TYPE) {
+                ret = ClassHelper.OBJECT_TYPE;
+                mv.visitInsn(ACONST_NULL);
+            }
+            argumentsToRemove += argumentList.size();
+            controller.getOperandStack().remove(argumentsToRemove);
+            controller.getOperandStack().push(ret);
+            return true;
+        } else {
+            if (target == StaticTypeCheckingVisitor.CLOSURE_CALL_VARGS) {
+                // wrap arguments into an array
+                ArrayExpression arr = new ArrayExpression(ClassHelper.OBJECT_TYPE, args.getExpressions());
+                return super.writeDirectMethodCall(target, implicitThis, receiver, new ArgumentListExpression(arr));
+            }
+            ClassNode classNode = controller.getClassNode();
+            if (classNode.isDerivedFrom(ClassHelper.CLOSURE_TYPE)
+                    && controller.isInClosure()
+                    && !target.isPublic()
+                    && target.getDeclaringClass() != classNode) {
+                if (!tryBridgeMethod(target, receiver, implicitThis, args, classNode)) {
+                    // replace call with an invoker helper call
+                    ArrayExpression arr = new ArrayExpression(ClassHelper.OBJECT_TYPE, args.getExpressions());
+                    MethodCallExpression mce = new MethodCallExpression(
+                            INVOKERHELER_RECEIVER,
+                            target.isStatic() ? "invokeStaticMethod" : "invokeMethodSafe",
+                            new ArgumentListExpression(
+                                    target.isStatic() ?
+                                            new ClassExpression(target.getDeclaringClass()) :
+                                            receiver,
+                                    new ConstantExpression(target.getName()),
+                                    arr
+                            )
+                    );
+                    mce.setMethodTarget(target.isStatic() ? INVOKERHELPER_INVOKESTATICMETHOD : INVOKERHELPER_INVOKEMETHOD);
+                    mce.visit(controller.getAcg());
+                    return true;
+                }
+                return true;
+            }
+            Expression fixedReceiver = null;
+            boolean fixedImplicitThis = implicitThis;
+            if (target.isPrivate()) {
+                if (tryPrivateMethod(target, implicitThis, receiver, args, classNode)) return true;
+            } else if (target.isProtected()) {
+                ClassNode node = receiver==null?ClassHelper.OBJECT_TYPE:controller.getTypeChooser().resolveType(receiver, controller.getClassNode());
+                boolean isThisOrSuper = false;
+                if (receiver instanceof VariableExpression) {
+                    isThisOrSuper = ((VariableExpression) receiver).isThisExpression() || ((VariableExpression) receiver).isSuperExpression();
+                }
+                if (!implicitThis && !isThisOrSuper
+                        && StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(node,target.getDeclaringClass())) {
+                    ASTNode src = receiver==null?args:receiver;
+                    controller.getSourceUnit().addError(
+                            new SyntaxException("Method " + target.getName() + " is protected in " + target.getDeclaringClass().toString(false),
+                                    src.getLineNumber(), src.getColumnNumber(), src.getLastLineNumber(), src.getLastColumnNumber()));
+                } else if (!node.isDerivedFrom(target.getDeclaringClass()) && tryBridgeMethod(target, receiver, implicitThis, args, classNode)) {
+                    return true;
+                }
+            } else if (target.isPublic() && receiver != null) {
+                if (implicitThis
+                        && !classNode.isDerivedFrom(target.getDeclaringClass())
+                        && !classNode.implementsInterface(target.getDeclaringClass())
+                        && classNode instanceof InnerClassNode && controller.isInClosure()) {
+                    ClassNode current = classNode.getOuterClass();
+                    fixedReceiver = new VariableExpression("thisObject", current);
+                    // adjust for multiple levels of nesting if needed
+                    while (current != null && current instanceof InnerClassNode && !classNode.equals(current)) {
+                        FieldNode thisField = current.getField("this$0");
+                        current = current.getOuterClass();
+                        if (thisField != null) {
+                            fixedReceiver = new PropertyExpression(fixedReceiver, "this$0");
+                            fixedReceiver.setType(current);
+                            fixedImplicitThis = false;
+                        }
+                    }
+                }
+            }
+            if (receiver != null) {
+                boolean callToSuper = receiver instanceof VariableExpression && ((VariableExpression) receiver).isSuperExpression();
+                if (!callToSuper) {
+                    fixedReceiver = fixedReceiver == null ? receiver : fixedReceiver;
+                    // in order to avoid calls to castToType, which is the dynamic behaviour, we make sure that we call CHECKCAST instead
+                    // then replace the top operand type
+                    Expression checkCastReceiver = new CheckcastReceiverExpression(fixedReceiver, target);
+                    return super.writeDirectMethodCall(target, fixedImplicitThis, checkCastReceiver, args);
+                }
+            }
+            return super.writeDirectMethodCall(target, implicitThis, receiver, args);
+        }
+    }
+
+    private boolean tryPrivateMethod(final MethodNode target, final boolean implicitThis, final Expression receiver, final TupleExpression args, final ClassNode classNode) {
+        ClassNode declaringClass = target.getDeclaringClass();
+        if ((isPrivateBridgeMethodsCallAllowed(declaringClass, classNode) || isPrivateBridgeMethodsCallAllowed(classNode, declaringClass))
+                && declaringClass.getNodeMetaData(PRIVATE_BRIDGE_METHODS) != null
+                && !declaringClass.equals(classNode)) {
+            if (tryBridgeMethod(target, receiver, implicitThis, args, classNode)) {
+                return true;
+            } else if (declaringClass != classNode) {
+                controller.getSourceUnit().addError(new SyntaxException("Cannot call private method " + (target.isStatic() ? "static " : "") +
+                        declaringClass.toString(false) + "#" + target.getName() + " from class " + classNode.toString(false), receiver.getLineNumber(), receiver.getColumnNumber(), receiver.getLastLineNumber(), receiver.getLastColumnNumber()));
+            }
+        }
+        if (declaringClass != classNode) {
+            controller.getSourceUnit().addError(new SyntaxException("Cannot call private method " + (target.isStatic() ? "static " : "") +
+                                                declaringClass.toString(false) + "#" + target.getName() + " from class " + classNode.toString(false), receiver.getLineNumber(), receiver.getColumnNumber(), receiver.getLastLineNumber(), receiver.getLastColumnNumber()));
+        }
+        return false;
+    }
+
+    protected static boolean isPrivateBridgeMethodsCallAllowed(ClassNode receiver, ClassNode caller) {
+        if (receiver == null) return false;
+        if (receiver.redirect() == caller) return true;
+        if (caller.redirect() instanceof InnerClassNode) return
+                isPrivateBridgeMethodsCallAllowed(receiver, caller.redirect().getOuterClass()) ||
+                        isPrivateBridgeMethodsCallAllowed(receiver.getOuterClass(), caller);
+        return false;
+    }
+
+    protected void loadArguments(List<Expression> argumentList, Parameter[] para) {
+        if (para.length == 0) return;
+        ClassNode lastParaType = para[para.length - 1].getOriginType();
+        AsmClassGenerator acg = controller.getAcg();
+        TypeChooser typeChooser = controller.getTypeChooser();
+        OperandStack operandStack = controller.getOperandStack();
+        ClassNode lastArgType = !argumentList.isEmpty() ?
+                typeChooser.resolveType(argumentList.get(argumentList.size()-1), controller.getClassNode()):null;
+        if (lastParaType.isArray()
+                && ((argumentList.size() > para.length)
+                || ((argumentList.size() == (para.length - 1)) && !lastParaType.equals(lastArgType))
+                || ((argumentList.size() == para.length && lastArgType!=null && !lastArgType.isArray())
+                    && (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(lastArgType,lastParaType.getComponentType())))
+                        || ClassHelper.GSTRING_TYPE.equals(lastArgType) && ClassHelper.STRING_TYPE.equals(lastParaType.getComponentType()))
+                ) {
+            int stackLen = operandStack.getStackLength() + argumentList.size();
+            MethodVisitor mv = controller.getMethodVisitor();
+            //mv = new org.objectweb.asm.util.TraceMethodVisitor(mv);
+            controller.setMethodVisitor(mv);
+            // varg call
+            // first parameters as usual
+            for (int i = 0; i < para.length - 1; i++) {
+                Expression expression = argumentList.get(i);
+                expression.visit(acg);
+                if (!isNullConstant(expression)) {
+                    operandStack.doGroovyCast(para[i].getType());
+                }
+            }
+            // last parameters wrapped in an array
+            List<Expression> lastParams = new LinkedList<Expression>();
+            for (int i = para.length - 1; i < argumentList.size(); i++) {
+                lastParams.add(argumentList.get(i));
+            }
+            ArrayExpression array = new ArrayExpression(
+                    lastParaType.getComponentType(),
+                    lastParams
+            );
+            array.visit(acg);
+            // adjust stack length
+            while (operandStack.getStackLength() < stackLen) {
+                operandStack.push(ClassHelper.OBJECT_TYPE);
+            }
+            if (argumentList.size() == para.length - 1) {
+                operandStack.remove(1);
+            }
+        } else if (argumentList.size() == para.length) {
+            for (int i = 0; i < argumentList.size(); i++) {
+                Expression expression = argumentList.get(i);
+                expression.visit(acg);
+                if (!isNullConstant(expression)) {
+                    operandStack.doGroovyCast(para[i].getType());
+                }
+            }
+        } else {
+            // method call with default arguments
+            ClassNode classNode = controller.getClassNode();
+            Expression[] arguments = new Expression[para.length];
+            for (int i = 0, j = 0; i < para.length; i++) {
+                Parameter curParam = para[i];
+                ClassNode curParamType = curParam.getType();
+                Expression curArg = j < argumentList.size() ? argumentList.get(j) : null;
+                Expression initialExpression = curParam.getNodeMetaData(StaticTypesMarker.INITIAL_EXPRESSION);
+                if (initialExpression == null && curParam.hasInitialExpression())
+                    initialExpression = curParam.getInitialExpression();
+                if (initialExpression == null && curParam.getNodeMetaData(Verifier.INITIAL_EXPRESSION)!=null) {
+                    initialExpression = curParam.getNodeMetaData(Verifier.INITIAL_EXPRESSION);
+                }
+                ClassNode curArgType = curArg == null ? null : typeChooser.resolveType(curArg, classNode);
+
+                if (initialExpression != null && !compatibleArgumentType(curArgType, curParamType)) {
+                    // use default expression
+                    arguments[i] = initialExpression;
+                } else {
+                    arguments[i] = curArg;
+                    j++;
+                }
+            }
+            for (int i = 0; i < arguments.length; i++) {
+                Expression expression = arguments[i];
+                expression.visit(acg);
+                if (!isNullConstant(expression)) {
+                    operandStack.doGroovyCast(para[i].getType());
+                }
+            }
+        }
+    }
+
+    private static boolean isNullConstant(final Expression expression) {
+        return (expression instanceof ConstantExpression && ((ConstantExpression) expression).getValue() == null);
+    }
+
+    private boolean compatibleArgumentType(ClassNode argumentType, ClassNode paramType) {
+        if (argumentType == null) return false;
+        if (ClassHelper.getWrapper(argumentType).equals(ClassHelper.getWrapper(paramType))) return true;
+        if (paramType.isInterface()) return argumentType.implementsInterface(paramType);
+        if (paramType.isArray() && argumentType.isArray())
+            return compatibleArgumentType(argumentType.getComponentType(), paramType.getComponentType());
+        return ClassHelper.getWrapper(argumentType).isDerivedFrom(ClassHelper.getWrapper(paramType));
+    }
+
+    @Override
+    public void makeCall(final Expression origin, final Expression receiver, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean safe, final boolean spreadSafe, final boolean implicitThis) {
+        ClassNode dynamicCallReturnType = origin.getNodeMetaData(StaticTypesMarker.DYNAMIC_RESOLUTION);
+        if (dynamicCallReturnType !=null) {
+            StaticTypesWriterController staticController = (StaticTypesWriterController) controller;
+            if (origin instanceof MethodCallExpression) {
+                ((MethodCallExpression) origin).setMethodTarget(null);
+            }
+            InvocationWriter dynamicInvocationWriter = staticController.getRegularInvocationWriter();
+            dynamicInvocationWriter.
+                    makeCall(origin, receiver, message, arguments, adapter, safe, spreadSafe, implicitThis);
+            return;
+        }
+        if (tryImplicitReceiver(origin, message, arguments, adapter, safe, spreadSafe, implicitThis)) {
+            return;
+        }
+        // if call is spread safe, replace it with a for in loop
+        if (spreadSafe && origin instanceof MethodCallExpression) {
+            // receiver expressions with side effects should not be visited twice, avoid by using a temporary variable
+            Expression tmpReceiver = receiver;
+            if (!(receiver instanceof VariableExpression) && !(receiver instanceof ConstantExpression)) {
+                tmpReceiver = new TemporaryVariableExpression(receiver);
+            }
+            MethodVisitor mv = controller.getMethodVisitor();
+            CompileStack compileStack = controller.getCompileStack();
+            TypeChooser typeChooser = controller.getTypeChooser();
+            OperandStack operandStack = controller.getOperandStack();
+            ClassNode classNode = controller.getClassNode();
+            int counter = labelCounter.incrementAndGet();
+
+            // use a temporary variable for the arraylist in which the results of the spread call will be stored
+            ConstructorCallExpression cce = new ConstructorCallExpression(StaticCompilationVisitor.ARRAYLIST_CLASSNODE, ArgumentListExpression.EMPTY_ARGUMENTS);
+            cce.setNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, StaticCompilationVisitor.ARRAYLIST_CONSTRUCTOR);
+            TemporaryVariableExpression result = new TemporaryVariableExpression(cce);
+            result.visit(controller.getAcg());
+            operandStack.pop();
+            // if (receiver != null)
+            tmpReceiver.visit(controller.getAcg());
+            Label ifnull = compileStack.createLocalLabel("ifnull_" + counter);
+            mv.visitJumpInsn(IFNULL, ifnull);
+            operandStack.remove(1); // receiver consumed by if()
+            Label nonull = compileStack.createLocalLabel("nonull_" + counter);
+            mv.visitLabel(nonull);
+            ClassNode componentType = StaticTypeCheckingVisitor.inferLoopElementType(typeChooser.resolveType(tmpReceiver, classNode));
+            Parameter iterator = new Parameter(componentType, "for$it$" + counter);
+            VariableExpression iteratorAsVar = new VariableExpression(iterator);
+            MethodCallExpression origMCE = (MethodCallExpression) origin;
+            MethodCallExpression newMCE = new MethodCallExpression(
+                    iteratorAsVar,
+                    origMCE.getMethodAsString(),
+                    origMCE.getArguments()
+            );
+            newMCE.setImplicitThis(false);
+            newMCE.setMethodTarget(origMCE.getMethodTarget());
+            newMCE.setSafe(true);
+            MethodCallExpression add = new MethodCallExpression(
+                    result,
+                    "add",
+                    newMCE
+            );
+            add.setImplicitThis(false);
+            add.setMethodTarget(StaticCompilationVisitor.ARRAYLIST_ADD_METHOD);
+            // for (e in receiver) { result.add(e?.method(arguments) }
+            ForStatement stmt = new ForStatement(
+                    iterator,
+                    tmpReceiver,
+                    new ExpressionStatement(add)
+            );
+            stmt.visit(controller.getAcg());
+            // else { empty list }
+            mv.visitLabel(ifnull);
+
+            // end of if/else
+            // return result list
+            result.visit(controller.getAcg());
+
+            // cleanup temporary variables
+            if (tmpReceiver instanceof TemporaryVariableExpression) {
+                ((TemporaryVariableExpression) tmpReceiver).remove(controller);
+            }
+            result.remove(controller);
+        } else if (safe && origin instanceof MethodCallExpression) {
+            // wrap call in an IFNULL check
+            MethodVisitor mv = controller.getMethodVisitor();
+            CompileStack compileStack = controller.getCompileStack();
+            OperandStack operandStack = controller.getOperandStack();
+            int counter = labelCounter.incrementAndGet();
+            // if (receiver != null)
+            ExpressionAsVariableSlot slot = new ExpressionAsVariableSlot(controller, receiver);
+            slot.visit(controller.getAcg());
+            operandStack.box();
+            Label ifnull = compileStack.createLocalLabel("ifnull_" + counter);
+            mv.visitJumpInsn(IFNULL, ifnull);
+            operandStack.remove(1); // receiver consumed by if()
+            Label nonull = compileStack.createLocalLabel("nonull_" + counter);
+            mv.visitLabel(nonull);
+            MethodCallExpression origMCE = (MethodCallExpression) origin;
+            MethodCallExpression newMCE = new MethodCallExpression(
+                    new VariableSlotLoader(slot.getType(), slot.getIndex(), controller.getOperandStack()),
+                    origMCE.getMethodAsString(),
+                    origMCE.getArguments()
+            );
+            MethodNode methodTarget = origMCE.getMethodTarget();
+            newMCE.setMethodTarget(methodTarget);
+            newMCE.setSafe(false);
+            newMCE.setImplicitThis(origMCE.isImplicitThis());
+            newMCE.setSourcePosition(origMCE);
+            newMCE.visit(controller.getAcg());
+            compileStack.removeVar(slot.getIndex());
+            ClassNode returnType = operandStack.getTopOperand();
+            if (ClassHelper.isPrimitiveType(returnType) && !ClassHelper.VOID_TYPE.equals(returnType)) {
+                operandStack.box();
+            }
+            Label endof = compileStack.createLocalLabel("endof_" + counter);
+            mv.visitJumpInsn(GOTO, endof);
+            mv.visitLabel(ifnull);
+            // else { null }
+            mv.visitInsn(ACONST_NULL);
+            mv.visitLabel(endof);
+        } else {
+            if ((adapter == AsmClassGenerator.getGroovyObjectField
+                    || adapter == AsmClassGenerator.getField ) && origin instanceof AttributeExpression) {
+                String pname = ((PropertyExpression) origin).getPropertyAsString();
+                CallSiteWriter callSiteWriter = controller.getCallSiteWriter();
+                if (pname!=null && callSiteWriter instanceof StaticTypesCallSiteWriter) {
+                    StaticTypesCallSiteWriter stcsw = (StaticTypesCallSiteWriter) callSiteWriter;
+                    TypeChooser typeChooser = controller.getTypeChooser();
+                    if (stcsw.makeGetField(receiver, typeChooser.resolveType(receiver, controller.getClassNode()), pname, safe, false, true)) {
+                        return;
+                    }
+                }
+            }
+            super.makeCall(origin, receiver, message, arguments, adapter, safe, spreadSafe, implicitThis);
+        }
+    }
+
+    boolean tryImplicitReceiver(final Expression origin, final Expression message, final Expression arguments, final MethodCallerMultiAdapter adapter, final boolean safe, final boolean spreadSafe, final boolean implicitThis) {
+        Object implicitReceiver = origin.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
+        if (implicitThis && implicitReceiver==null && origin instanceof MethodCallExpression) {
+            implicitReceiver = ((MethodCallExpression) origin).getObjectExpression().getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
+        }
+        if (implicitReceiver !=null && implicitThis) {
+            String[] propertyPath = ((String) implicitReceiver).split("\\.");
+            // GROOVY-6021
+            PropertyExpression pexp = new PropertyExpression(new VariableExpression("this", CLOSURE_TYPE), propertyPath[0]);
+            pexp.setImplicitThis(true);
+            for (int i=1; i<propertyPath.length;i++) {
+                pexp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, CLOSURE_TYPE);
+                pexp = new PropertyExpression(pexp, propertyPath[i]);
+            }
+            pexp.putNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER, implicitReceiver);
+            origin.removeNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER);
+            if (origin instanceof PropertyExpression) {
+                PropertyExpression rewritten = new PropertyExpression(
+                        pexp,
+                        ((PropertyExpression) origin).getProperty(),
+                        ((PropertyExpression) origin).isSafe()
+                );
+                rewritten.setSpreadSafe(((PropertyExpression) origin).isSpreadSafe());
+                rewritten.setImplicitThis(false);
+                rewritten.visit(controller.getAcg());
+                rewritten.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, origin.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE));
+                return true;
+            }
+            makeCall(origin, pexp, message, arguments, adapter, safe, spreadSafe, false);
+            return true;
+        }
+        return false;
+    }
+
+    private class CheckcastReceiverExpression extends Expression {
+        private final Expression receiver;
+        private final MethodNode target;
+
+        private ClassNode resolvedType;
+
+        public CheckcastReceiverExpression(final Expression receiver, final MethodNode target) {
+            this.receiver = receiver;
+            this.target = target;
+        }
+
+        @Override
+        public Expression transformExpression(final ExpressionTransformer transformer) {
+            return this;
+        }
+
+        @Override
+        public void visit(final GroovyCodeVisitor visitor) {
+            receiver.visit(visitor);
+            if (visitor instanceof AsmClassGenerator) {
+                ClassNode topOperand = controller.getOperandStack().getTopOperand();
+                ClassNode type = getType();
+                if (ClassHelper.GSTRING_TYPE.equals(topOperand) && ClassHelper.STRING_TYPE.equals(type)) {
+                    // perform regular type conversion
+                    controller.getOperandStack().doGroovyCast(type);
+                    return;
+                }
+                if (ClassHelper.isPrimitiveType(topOperand) && !ClassHelper.isPrimitiveType(type)) {
+                    controller.getOperandStack().box();
+                } else if (!ClassHelper.isPrimitiveType(topOperand) && ClassHelper.isPrimitiveType(type)) {
+                    controller.getOperandStack().doGroovyCast(type);
+                }
+                if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(topOperand, type)) return;
+                controller.getMethodVisitor().visitTypeInsn(CHECKCAST, type.isArray() ?
+                        BytecodeHelper.getTypeDescription(type) :
+                        BytecodeHelper.getClassInternalName(type.getName()));
+                controller.getOperandStack().replace(type);
+            }
+        }
+
+        @Override
+        public ClassNode getType() {
+            if (resolvedType!=null) {
+                return resolvedType;
+            }
+            ClassNode type;
+            if (target instanceof ExtensionMethodNode) {
+                type = ((ExtensionMethodNode) target).getExtensionMethodNode().getDeclaringClass();
+            } else {
+                type = getWrapper(controller.getTypeChooser().resolveType(receiver, controller.getClassNode()));
+                ClassNode declaringClass = target.getDeclaringClass();
+                if (type.getClass() != ClassNode.class
+                        && type.getClass() != InnerClassNode.class
+                        && type.getClass() != DecompiledClassNode.class) {
+                    type = declaringClass; // ex: LUB type
+                }
+                if (OBJECT_TYPE.equals(type) && !OBJECT_TYPE.equals(declaringClass)) {
+                    // can happen for compiler rewritten code, where type information is missing
+                    type = declaringClass;
+                }
+                if (OBJECT_TYPE.equals(declaringClass)) {
+                    // check cast not necessary because Object never evolves
+                    // and it prevents a potential ClassCastException if the delegate of a closure
+                    // is changed in a statically compiled closure
+                    type = OBJECT_TYPE;
+                }
+            }
+            resolvedType = type;
+            return type;
+        }
+    }
+
+    public MethodCallExpression getCurrentCall() {
+        return currentCall;
+    }
+
+    @Override
+    protected boolean makeCachedCall(Expression origin, ClassExpression sender, Expression receiver, Expression message, Expression arguments, MethodCallerMultiAdapter adapter, boolean safe, boolean spreadSafe, boolean implicitThis, boolean containsSpreadExpression) {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticPropertyAccessHelper.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticPropertyAccessHelper.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticPropertyAccessHelper.java
new file mode 100644
index 0000000..6027a0c
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticPropertyAccessHelper.java
@@ -0,0 +1,133 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.transform.sc.ListOfExpressionsExpression;
+import org.codehaus.groovy.transform.sc.TemporaryVariableExpression;
+
+import java.util.Arrays;
+
+/**
+ * Contains helper methods aimed at facilitating the generation of statically compiled bytecode for property access.
+ *
+ * @author Cédric Champeau
+ * @since 2.4.0
+ */
+public abstract class StaticPropertyAccessHelper {
+    public static Expression transformToSetterCall(
+            Expression receiver,
+            MethodNode setterMethod,
+            final Expression arguments,
+            boolean implicitThis,
+            boolean safe,
+            boolean spreadSafe,
+            boolean requiresReturnValue,
+            Expression location) {
+        if (requiresReturnValue) {
+            TemporaryVariableExpression tmp = new TemporaryVariableExpression(arguments);
+            PoppingMethodCallExpression call = new PoppingMethodCallExpression(receiver, setterMethod, tmp);
+            call.setImplicitThis(implicitThis);
+            call.setSafe(safe);
+            call.setSpreadSafe(spreadSafe);
+            call.setSourcePosition(location);
+            PoppingListOfExpressionsExpression result = new PoppingListOfExpressionsExpression(tmp, call);
+            result.setSourcePosition(location);
+            return result;
+        } else {
+            MethodCallExpression call = new MethodCallExpression(
+                    receiver,
+                    setterMethod.getName(),
+                    arguments
+            );
+            call.setImplicitThis(implicitThis);
+            call.setSafe(safe);
+            call.setSpreadSafe(spreadSafe);
+            call.setMethodTarget(setterMethod);
+            call.setSourcePosition(location);
+            return call;
+        }
+    }
+
+    private static class PoppingListOfExpressionsExpression extends ListOfExpressionsExpression {
+        private final TemporaryVariableExpression tmp;
+        private final PoppingMethodCallExpression call;
+
+        public PoppingListOfExpressionsExpression(final TemporaryVariableExpression tmp, final PoppingMethodCallExpression call) {
+            super(Arrays.asList(
+                    tmp,
+                    call
+            ));
+            this.tmp = tmp;
+            this.call = call;
+        }
+
+        @Override
+        public Expression transformExpression(final ExpressionTransformer transformer) {
+            PoppingMethodCallExpression tcall = (PoppingMethodCallExpression) call.transformExpression(transformer);
+            return new PoppingListOfExpressionsExpression(tcall.tmp, tcall);
+        }
+
+        @Override
+        public void visit(final GroovyCodeVisitor visitor) {
+            super.visit(visitor);
+            if (visitor instanceof AsmClassGenerator) {
+                tmp.remove(((AsmClassGenerator) visitor).getController());
+            }
+        }
+    }
+
+    private static class PoppingMethodCallExpression extends MethodCallExpression {
+        private final Expression receiver;
+        private final MethodNode setter;
+        private final TemporaryVariableExpression tmp;
+
+        public PoppingMethodCallExpression(final Expression receiver, final MethodNode setterMethod, final TemporaryVariableExpression tmp) {
+            super(receiver, setterMethod.getName(), tmp);
+            this.receiver = receiver;
+            this.setter = setterMethod;
+            this.tmp = tmp;
+            setMethodTarget(setterMethod);
+        }
+
+        @Override
+        public Expression transformExpression(final ExpressionTransformer transformer) {
+            PoppingMethodCallExpression trn = new PoppingMethodCallExpression(receiver.transformExpression(transformer), setter, (TemporaryVariableExpression) tmp.transformExpression(transformer));
+            trn.copyNodeMetaData(this);
+            trn.setImplicitThis(isImplicitThis());
+            trn.setSafe(isSafe());
+            trn.setSpreadSafe(isSpreadSafe());
+            return trn;
+        }
+
+        @Override
+        public void visit(final GroovyCodeVisitor visitor) {
+            super.visit(visitor);
+            if (visitor instanceof AsmClassGenerator) {
+                // ignore the return of the call
+                ((AsmClassGenerator) visitor).getController().getOperandStack().pop();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
new file mode 100644
index 0000000..116fd16
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesBinaryExpressionMultiTypeDispatcher.java
@@ -0,0 +1,426 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.codehaus.groovy.classgen.asm.sc;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.AttributeExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.DeclarationExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.tools.WideningCategories;
+import org.codehaus.groovy.classgen.asm.BinaryExpressionMultiTypeDispatcher;
+import org.codehaus.groovy.classgen.asm.BinaryExpressionWriter;
+import org.codehaus.groovy.classgen.asm.BytecodeHelper;
+import org.codehaus.groovy.classgen.asm.CompileStack;
+import org.codehaus.groovy.classgen.asm.OperandStack;
+import org.codehaus.groovy.classgen.asm.TypeChooser;
+import org.codehaus.groovy.classgen.asm.VariableSlotLoader;
+import org.codehaus.groovy.classgen.asm.WriterController;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.transform.sc.StaticCompilationMetadataKeys;
+import org.codehaus.groovy.transform.sc.StaticCompilationVisitor;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport;
+import org.codehaus.groovy.transform.stc.StaticTypeCheckingVisitor;
+import org.codehaus.groovy.transform.stc.StaticTypesMarker;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Modifier;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.codehaus.groovy.ast.ClassHelper.CLOSURE_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.char_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.double_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.float_TYPE;
+import static org.codehaus.groovy.ast.ClassHelper.long_TYPE;
+import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_ADD_METHOD;
+import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_CLASSNODE;
+import static org.codehaus.groovy.transform.sc.StaticCompilationVisitor.ARRAYLIST_CONSTRUCTOR;
+
+/**
+ * A specialized version of the multi type binary expression dispatcher which is aware of static compilation.
+ * It is able to generate optimized bytecode for some operations using JVM instructions when available.
+ *
+ * @author Cedric Champeau
+ * @author Jochen Theodorou
+ */
+public class StaticTypesBinaryExpressionMultiTypeDispatcher extends BinaryExpressionMultiTypeDispatcher implements Opcodes {
+
+    private final AtomicInteger labelCounter = new AtomicInteger();
+    private static final MethodNode CLOSURE_GETTHISOBJECT_METHOD = CLOSURE_TYPE.getMethod("getThisObject", new Parameter[0]);
+
+
+    public StaticTypesBinaryExpressionMultiTypeDispatcher(WriterController wc) {
+        super(wc);
+    }
+
+    @Override
+    protected void writePostOrPrefixMethod(int op, String method, Expression expression, Expression orig) {
+        MethodNode mn = orig.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+        WriterController controller = getController();
+        OperandStack operandStack = controller.getOperandStack();
+        if (mn!=null) {
+            operandStack.pop();
+            MethodCallExpression call = new MethodCallExpression(
+                    expression,
+                    method,
+                    ArgumentListExpression.EMPTY_ARGUMENTS
+            );
+            call.setMethodTarget(mn);
+            call.visit(controller.getAcg());
+            return;
+        }
+
+        ClassNode top = operandStack.getTopOperand();
+        if (ClassHelper.isPrimitiveType(top) && (ClassHelper.isNumberType(top)||char_TYPE.equals(top))) {
+            MethodVisitor mv = controller.getMethodVisitor();
+            if (WideningCategories.isIntCategory(top) || char_TYPE.equals(top)) {
+                mv.visitInsn(ICONST_1);
+            } else if (long_TYPE.equals(top)) {
+                mv.visitInsn(LCONST_1);
+            } else if (float_TYPE.equals(top)) {
+                mv.visitInsn(FCONST_1);
+            } else if (double_TYPE.equals(top)) {
+                mv.visitInsn(DCONST_1);
+            }
+            if ("next".equals(method)) {
+                if (WideningCategories.isIntCategory(top) || char_TYPE.equals(top)) {
+                    mv.visitInsn(IADD);
+                } else if (long_TYPE.equals(top)) {
+                    mv.visitInsn(LADD);
+                } else if (float_TYPE.equals(top)) {
+                    mv.visitInsn(FADD);
+                } else if (double_TYPE.equals(top)) {
+                    mv.visitInsn(DADD);
+                }
+            } else {
+                if (WideningCategories.isIntCategory(top) || char_TYPE.equals(top)) {
+                    mv.visitInsn(ISUB);
+                } else if (long_TYPE.equals(top)) {
+                    mv.visitInsn(LSUB);
+                } else if (float_TYPE.equals(top)) {
+                    mv.visitInsn(FSUB);
+                } else if (double_TYPE.equals(top)) {
+                    mv.visitInsn(DSUB);
+                }
+            }
+            return;
+        }
+        super.writePostOrPrefixMethod(op, method, expression, orig);
+    }
+
+    @Override
+    public void evaluateEqual(final BinaryExpression expression, final boolean defineVariable) {
+        if (!defineVariable) {
+            Expression leftExpression = expression.getLeftExpression();
+            if (leftExpression instanceof PropertyExpression) {
+                PropertyExpression pexp = (PropertyExpression) leftExpression;
+                if (makeSetProperty(
+                        pexp.getObjectExpression(),
+                        pexp.getProperty(),
+                        expression.getRightExpression(),
+                        pexp.isSafe(),
+                        pexp.isSpreadSafe(),
+                        pexp.isImplicitThis(),
+                        pexp instanceof AttributeExpression)) return;
+            }
+        }
+        // GROOVY-5620: Spread safe/Null safe operator on LHS is not supported
+        if (expression.getLeftExpression() instanceof PropertyExpression
+                && ((PropertyExpression) expression.getLeftExpression()).isSpreadSafe()
+                && StaticTypeCheckingSupport.isAssignment(expression.getOperation().getType())) {
+            // rewrite it so that it can be statically compiled
+            transformSpreadOnLHS(expression);
+            return;
+        }
+        super.evaluateEqual(expression, defineVariable);
+    }
+
+    private void transformSpreadOnLHS(BinaryExpression origin) {
+        PropertyExpression spreadExpression = (PropertyExpression) origin.getLeftExpression();
+        Expression value = origin.getRightExpression();
+        WriterController controller = getController();
+        MethodVisitor mv = controller.getMethodVisitor();
+        CompileStack compileStack = controller.getCompileStack();
+        TypeChooser typeChooser = controller.getTypeChooser();
+        OperandStack operandStack = controller.getOperandStack();
+        ClassNode classNode = controller.getClassNode();
+        int counter = labelCounter.incrementAndGet();
+        Expression receiver = spreadExpression.getObjectExpression();
+
+        // create an empty arraylist
+        VariableExpression result = new VariableExpression(
+                this.getClass().getSimpleName()+"$spreadresult" + counter,
+                ARRAYLIST_CLASSNODE
+        );
+        ConstructorCallExpression cce = new ConstructorCallExpression(ARRAYLIST_CLASSNODE, ArgumentListExpression.EMPTY_ARGUMENTS);
+        cce.setNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, ARRAYLIST_CONSTRUCTOR);
+        DeclarationExpression declr = new DeclarationExpression(
+                result,
+                Token.newSymbol("=", spreadExpression.getLineNumber(), spreadExpression.getColumnNumber()),
+                cce
+        );
+        declr.visit(controller.getAcg());
+        // if (receiver != null)
+        receiver.visit(controller.getAcg());
+        Label ifnull = compileStack.createLocalLabel("ifnull_" + counter);
+        mv.visitJumpInsn(IFNULL, ifnull);
+        operandStack.remove(1); // receiver consumed by if()
+        Label nonull = compileStack.createLocalLabel("nonull_" + counter);
+        mv.visitLabel(nonull);
+        ClassNode componentType = StaticTypeCheckingVisitor.inferLoopElementType(typeChooser.resolveType(receiver, classNode));
+        Parameter iterator = new Parameter(componentType, "for$it$" + counter);
+        VariableExpression iteratorAsVar = new VariableExpression(iterator);
+        PropertyExpression pexp = spreadExpression instanceof AttributeExpression ?
+                new AttributeExpression(iteratorAsVar, spreadExpression.getProperty(), true):
+                new PropertyExpression(iteratorAsVar, spreadExpression.getProperty(), true);
+        pexp.setImplicitThis(spreadExpression.isImplicitThis());
+        pexp.setSourcePosition(spreadExpression);
+        BinaryExpression assignment = new BinaryExpression(
+                pexp,
+                origin.getOperation(),
+                value
+        );
+        MethodCallExpression add = new MethodCallExpression(
+                result,
+                "add",
+                assignment
+        );
+        add.setMethodTarget(ARRAYLIST_ADD_METHOD);
+        // for (e in receiver) { result.add(e?.method(arguments) }
+        ForStatement stmt = new ForStatement(
+                iterator,
+                receiver,
+                new ExpressionStatement(add)
+        );
+        stmt.visit(controller.getAcg());
+        // else { empty list }
+        mv.visitLabel(ifnull);
+
+        // end of if/else
+        // return result list
+        result.visit(controller.getAcg());
+
+    }
+
+    private boolean makeSetProperty(final Expression receiver, final Expression message, final Expression arguments, final boolean safe, final boolean spreadSafe, final boolean implicitThis, final boolean isAttribute) {
+        WriterController controller = getController();
+        TypeChooser typeChooser = controller.getTypeChooser();
+        ClassNode receiverType = typeChooser.resolveType(receiver, controller.getClassNode());
+        String property = message.getText();
+        boolean isThisExpression = receiver instanceof VariableExpression && ((VariableExpression) receiver).isThisExpression();
+        if (isAttribute
+                || (isThisExpression &&
+                    receiverType.getDeclaredField(property)!=null)) {
+            ClassNode current = receiverType;
+            FieldNode fn = null;
+            while (fn==null && current!=null) {
+                fn = current.getDeclaredField(property);
+                if (fn==null){
+                    current = current.getSuperClass();
+                }
+            }
+            if (fn!=null && receiverType!=current && !fn.isPublic()) {
+                // check that direct access is allowed
+                if (!fn.isProtected()) {
+                    return false;
+                }
+                String pkg1 = receiverType.getPackageName();
+                String pkg2 = current.getPackageName();
+                if (pkg1!=pkg2 && !pkg1.equals(pkg2)) {
+                    return false;
+                }
+                OperandStack operandStack = controller.getOperandStack();
+                MethodVisitor mv = controller.getMethodVisitor();
+                if (!fn.isStatic()) {
+                    receiver.visit(controller.getAcg());
+                }
+                arguments.visit(controller.getAcg());
+                operandStack.doGroovyCast(fn.getOriginType());
+                mv.visitFieldInsn(fn.isStatic() ? PUTSTATIC : PUTFIELD,
+                        BytecodeHelper.getClassInternalName(fn.getOwner()),
+                        property,
+                        BytecodeHelper.getTypeDescription(fn.getOriginType()));
+                operandStack.remove(fn.isStatic()?1:2);
+                return true;
+            }
+        }
+        if (!isAttribute) {
+            String setter = "set" + MetaClassHelper.capitalize(property);
+            MethodNode setterMethod = receiverType.getSetterMethod(setter, false);
+            ClassNode declaringClass = setterMethod!=null?setterMethod.getDeclaringClass():null;
+            if (isThisExpression && declaringClass!=null && declaringClass.equals(controller.getClassNode())) {
+                // this.x = ... shouldn't use a setter if in the same class
+                setterMethod = null;
+            } else if (setterMethod == null) {
+                PropertyNode propertyNode = receiverType.getProperty(property);
+                if (propertyNode != null) {
+                    int mods = propertyNode.getModifiers();
+                    if (!Modifier.isFinal(mods)) {
+                        setterMethod = new MethodNode(
+                                setter,
+                                ACC_PUBLIC,
+                                ClassHelper.VOID_TYPE,
+                                new Parameter[]{new Parameter(propertyNode.getOriginType(), "value")},
+                                ClassNode.EMPTY_ARRAY,
+                                EmptyStatement.INSTANCE
+                        );
+                        setterMethod.setDeclaringClass(receiverType);
+                    }
+                }
+            }
+            if (setterMethod != null) {
+                Expression call = StaticPropertyAccessHelper.transformToSetterCall(
+                        receiver,
+                        setterMethod,
+                        arguments,
+                        implicitThis,
+                        safe,
+                        spreadSafe,
+                        true, // to be replaced with a proper test whether a return value should be used or not
+                        message
+                );
+                call.visit(controller.getAcg());
+                return true;
+            }
+            if (isThisExpression && !controller.isInClosure()) {
+                receiverType = controller.getClassNode();
+            }
+            if (makeSetPrivateFieldWithBridgeMethod(receiver, receiverType, property, arguments, safe, spreadSafe, implicitThis)) return true;
+        }
+        return false;
+    }
+
+    @SuppressWarnings("unchecked")
+    private boolean makeSetPrivateFieldWithBridgeMethod(final Expression receiver, final ClassNode receiverType, final String fieldName, final Expression arguments, final boolean safe, final boolean spreadSafe, final boolean implicitThis) {
+        WriterController controller = getController();
+        FieldNode field = receiverType.getField(fieldName);
+        ClassNode outerClass = receiverType.getOuterClass();
+        if (field == null && implicitThis && outerClass != null && !receiverType.isStaticClass()) {
+            Expression pexp;
+            if (controller.isInClosure()) {
+                MethodCallExpression mce = new MethodCallExpression(
+                    new VariableExpression("this"),
+                    "getThisObject",
+                    ArgumentListExpression.EMPTY_ARGUMENTS
+                );
+                mce.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, controller.getOutermostClass());
+                mce.setImplicitThis(true);
+                mce.setMethodTarget(CLOSURE_GETTHISOBJECT_METHOD);
+                pexp = new CastExpression(controller.getOutermostClass(),mce);
+            } else {
+                pexp = new PropertyExpression(
+                    new ClassExpression(outerClass),
+                    "this"
+                );
+                ((PropertyExpression)pexp).setImplicitThis(true);
+            }
+            pexp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, outerClass);
+            pexp.setSourcePosition(receiver);
+            return makeSetPrivateFieldWithBridgeMethod(pexp, outerClass, fieldName, arguments, safe, spreadSafe, true);
+        }
+        ClassNode classNode = controller.getClassNode();
+        if (field != null && Modifier.isPrivate(field.getModifiers())
+            && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode) || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode,receiverType))
+            && !receiverType.equals(classNode)) {
+            Map<String, MethodNode> mutators = receiverType.redirect().getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_FIELDS_MUTATORS);
+            if (mutators != null) {
+                MethodNode methodNode = mutators.get(fieldName);
+                if (methodNode != null) {
+                    MethodCallExpression mce = new MethodCallExpression(receiver, methodNode.getName(),
+                        new ArgumentListExpression(field.isStatic()?new ConstantExpression(null):receiver, arguments));
+                        mce.setMethodTarget(methodNode);
+                        mce.setSafe(safe);
+                        mce.setSpreadSafe(spreadSafe);
+                    mce.setImplicitThis(implicitThis);
+                    mce.visit(controller.getAcg());
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected void assignToArray(Expression parent, Expression receiver, Expression index, Expression rhsValueLoader, boolean safe) {
+        ClassNode current = getController().getClassNode();
+        ClassNode arrayType = getController().getTypeChooser().resolveType(receiver, current);
+        ClassNode arrayComponentType = arrayType.getComponentType();
+        int operationType = getOperandType(arrayComponentType);
+        BinaryExpressionWriter bew = binExpWriter[operationType];
+
+        if (bew.arraySet(true) && arrayType.isArray() && !safe) {
+            super.assignToArray(parent, receiver, index, rhsValueLoader, safe);
+        } else {
+            /******
+            / This code path is needed because ACG creates array access expressions
+            *******/
+
+            WriterController controller = getController();
+            StaticTypeCheckingVisitor visitor = new StaticCompilationVisitor(controller.getSourceUnit(), controller.getClassNode());
+            // let's replace this assignment to a subscript operator with a
+            // method call
+            // e.g. x[5] = 10
+            // -> (x, [], 5), =, 10
+            // -> methodCall(x, "putAt", [5, 10])
+            ArgumentListExpression ae = new ArgumentListExpression(index, rhsValueLoader);
+            if (rhsValueLoader instanceof VariableSlotLoader && parent instanceof BinaryExpression) {
+                // GROOVY-6061
+                rhsValueLoader.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE,
+                        controller.getTypeChooser().resolveType(parent, controller.getClassNode()));
+            }
+            MethodCallExpression mce = new MethodCallExpression(
+                    receiver,
+                    "putAt",
+                    ae
+            );
+
+            mce.setSafe(safe);
+            mce.setSourcePosition(parent);
+            visitor.visitMethodCallExpression(mce);
+            OperandStack operandStack = controller.getOperandStack();
+            int height = operandStack.getStackLength();
+            mce.visit(controller.getAcg());
+            operandStack.pop();
+            operandStack.remove(operandStack.getStackLength()-height);
+            // return value of assignment
+            rhsValueLoader.visit(controller.getAcg());
+        }
+    }
+
+}