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:33 UTC

[22/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/CallSiteWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/CallSiteWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
new file mode 100644
index 0000000..716f97e
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/CallSiteWriter.java
@@ -0,0 +1,387 @@
+/*
+ *  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;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.InterfaceHelperClassNode;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.CastExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.runtime.callsite.CallSite;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.objectweb.asm.Opcodes.AALOAD;
+import static org.objectweb.asm.Opcodes.AASTORE;
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.ACONST_NULL;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ANEWARRAY;
+import static org.objectweb.asm.Opcodes.ARETURN;
+import static org.objectweb.asm.Opcodes.ASTORE;
+import static org.objectweb.asm.Opcodes.CHECKCAST;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.GETFIELD;
+import static org.objectweb.asm.Opcodes.GETSTATIC;
+import static org.objectweb.asm.Opcodes.IFNONNULL;
+import static org.objectweb.asm.Opcodes.IFNULL;
+import static org.objectweb.asm.Opcodes.INVOKEINTERFACE;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.NEW;
+import static org.objectweb.asm.Opcodes.PUTSTATIC;
+import static org.objectweb.asm.Opcodes.RETURN;
+
+/**
+ * This class represents non public API used by AsmClassGenerator. Don't
+ * use this class in your code
+ */
+public class CallSiteWriter {
+    
+    private static String [] sig = new String [255];
+    private static String getCreateArraySignature(int numberOfArguments) {
+        if (sig[numberOfArguments] == null) {
+            StringBuilder sb = new StringBuilder("(");
+            for (int i = 0; i != numberOfArguments; ++i) {
+                sb.append("Ljava/lang/Object;");
+            }
+            sb.append(")[Ljava/lang/Object;");
+            sig[numberOfArguments] = sb.toString();
+        }
+        return sig[numberOfArguments];
+    }
+    private static final int
+        MOD_PRIVSS = ACC_PRIVATE+ACC_STATIC+ACC_SYNTHETIC,
+        MOD_PUBSS  = ACC_PUBLIC+ACC_STATIC+ACC_SYNTHETIC;
+    private static final ClassNode CALLSITE_ARRAY_NODE = ClassHelper.make(CallSite[].class);
+    private static final String
+        GET_CALLSITE_METHOD     = "$getCallSiteArray",
+        CALLSITE_CLASS          = "org/codehaus/groovy/runtime/callsite/CallSite",
+        CALLSITE_DESC           = "[Lorg/codehaus/groovy/runtime/callsite/CallSite;",
+        GET_CALLSITE_DESC       = "()"+CALLSITE_DESC,
+        CALLSITE_ARRAY_CLASS    = "org/codehaus/groovy/runtime/callsite/CallSiteArray",
+        GET_CALLSITEARRAY_DESC  = "()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray;",
+        CALLSITE_FIELD          = "$callSiteArray",
+        REF_CLASS               = "java/lang/ref/SoftReference",
+        REF_DESC                = "L"+REF_CLASS+";",
+        METHOD_OO_DESC          = "(Ljava/lang/Object;)Ljava/lang/Object;",
+        CREATE_CSA_METHOD       = "$createCallSiteArray";
+    public static final String CONSTRUCTOR = "<$constructor$>";
+    
+    private final List callSites = new ArrayList(32);
+    private int callSiteArrayVarIndex = -1;
+    private final WriterController controller;
+
+    public CallSiteWriter(WriterController wc) {
+        this.controller = wc;
+        ClassNode node = controller.getClassNode();
+        if(node instanceof InterfaceHelperClassNode) {
+            InterfaceHelperClassNode ihcn = (InterfaceHelperClassNode) node;
+            callSites.addAll(ihcn.getCallSites());
+        }
+    }
+    
+    public void makeSiteEntry() {
+        if (controller.isNotClinit()) {
+            controller.getMethodVisitor().visitMethodInsn(INVOKESTATIC, controller.getInternalClassName(), GET_CALLSITE_METHOD, GET_CALLSITE_DESC, false);
+            controller.getOperandStack().push(CALLSITE_ARRAY_NODE);
+            callSiteArrayVarIndex = controller.getCompileStack().defineTemporaryVariable("$local$callSiteArray", CALLSITE_ARRAY_NODE, true);
+        }
+    }
+    
+    public void generateCallSiteArray() {
+        if (!controller.getClassNode().isInterface()) {
+            controller.getClassVisitor().visitField(MOD_PRIVSS, CALLSITE_FIELD, REF_DESC, null, null);
+            generateCreateCallSiteArray();
+            generateGetCallSiteArray();
+        }
+    }
+
+    private void generateGetCallSiteArray() {
+        int visibility = (controller.getClassNode() instanceof InterfaceHelperClassNode) ? MOD_PUBSS : MOD_PRIVSS;
+        MethodVisitor mv = controller.getClassVisitor().visitMethod(visibility, GET_CALLSITE_METHOD, GET_CALLSITE_DESC, null, null);
+        controller.setMethodVisitor(mv);
+        mv.visitCode();
+        mv.visitFieldInsn(GETSTATIC, controller.getInternalClassName(), "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFNULL, l0);
+        mv.visitFieldInsn(GETSTATIC, controller.getInternalClassName(), "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ref/SoftReference", "get", "()Ljava/lang/Object;", false);
+        mv.visitTypeInsn(CHECKCAST, "org/codehaus/groovy/runtime/callsite/CallSiteArray");
+        mv.visitInsn(DUP);
+        mv.visitVarInsn(ASTORE, 0);
+        Label l1 = new Label();
+        mv.visitJumpInsn(IFNONNULL, l1);
+        mv.visitLabel(l0);
+        mv.visitMethodInsn(INVOKESTATIC, controller.getInternalClassName(), "$createCallSiteArray", "()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray;", false);
+        mv.visitVarInsn(ASTORE, 0);
+        mv.visitTypeInsn(NEW, "java/lang/ref/SoftReference");
+        mv.visitInsn(DUP);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/ref/SoftReference", "<init>", "(Ljava/lang/Object;)V", false);
+        mv.visitFieldInsn(PUTSTATIC, controller.getInternalClassName(), "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+        mv.visitLabel(l1);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, "org/codehaus/groovy/runtime/callsite/CallSiteArray", "array", "[Lorg/codehaus/groovy/runtime/callsite/CallSite;");
+        mv.visitInsn(ARETURN);
+        mv.visitMaxs(0, 0);
+        mv.visitEnd();
+    }
+        
+    private void generateCreateCallSiteArray() { 
+        List<String> callSiteInitMethods = new LinkedList<String>(); 
+        int index = 0; 
+        int methodIndex = 0; 
+        final int size = callSites.size(); 
+        final int maxArrayInit = 5000; 
+        // create array initialization methods
+        while (index < size) { 
+            methodIndex++; 
+            String methodName = "$createCallSiteArray_" + methodIndex; 
+            callSiteInitMethods.add(methodName); 
+            MethodVisitor mv = controller.getClassVisitor().visitMethod(MOD_PRIVSS, methodName, "([Ljava/lang/String;)V", null, null);
+            controller.setMethodVisitor(mv);
+            mv.visitCode(); 
+            int methodLimit = size; 
+            // check if the next block is over the max allowed
+            if ((methodLimit - index) > maxArrayInit) { 
+                methodLimit = index + maxArrayInit; 
+            } 
+            for (; index < methodLimit; index++) { 
+                mv.visitVarInsn(ALOAD, 0); 
+                mv.visitLdcInsn(index); 
+                mv.visitLdcInsn(callSites.get(index)); 
+                mv.visitInsn(AASTORE); 
+            } 
+            mv.visitInsn(RETURN); 
+            mv.visitMaxs(2,1); 
+            mv.visitEnd(); 
+        }
+        // create base createCallSiteArray method
+        MethodVisitor mv = controller.getClassVisitor().visitMethod(MOD_PRIVSS, CREATE_CSA_METHOD, GET_CALLSITEARRAY_DESC, null, null);
+        controller.setMethodVisitor(mv);
+        mv.visitCode(); 
+        mv.visitLdcInsn(size); 
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); 
+        mv.visitVarInsn(ASTORE, 0); 
+        for (String methodName : callSiteInitMethods) { 
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESTATIC, controller.getInternalClassName(), methodName, "([Ljava/lang/String;)V", false);
+        } 
+
+        mv.visitTypeInsn(NEW, CALLSITE_ARRAY_CLASS); 
+        mv.visitInsn(DUP); 
+        controller.getAcg().visitClassExpression(new ClassExpression(controller.getClassNode())); 
+
+        mv.visitVarInsn(ALOAD, 0);
+
+        mv.visitMethodInsn(INVOKESPECIAL, CALLSITE_ARRAY_CLASS, "<init>", "(Ljava/lang/Class;[Ljava/lang/String;)V", false);
+        mv.visitInsn(ARETURN); 
+        mv.visitMaxs(0,0); 
+        mv.visitEnd(); 
+    } 
+    
+    private int allocateIndex(String name) {
+        callSites.add(name);
+        return callSites.size()-1;
+    }
+
+    private void invokeSafe(boolean safe, String unsafeMethod, String safeMethod) {
+        String method = unsafeMethod;
+        if (safe) method = safeMethod;
+        controller.getMethodVisitor().visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, method, METHOD_OO_DESC, true);
+        controller.getOperandStack().replace(ClassHelper.OBJECT_TYPE);
+    }
+
+    public void prepareCallSite(String message) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        if (controller.isNotClinit()) {
+            mv.visitVarInsn(ALOAD, callSiteArrayVarIndex);
+        } else {
+            mv.visitMethodInsn(INVOKESTATIC, controller.getClassName(), GET_CALLSITE_METHOD, GET_CALLSITE_DESC, false);
+        }
+        final int index = allocateIndex(message);
+        mv.visitLdcInsn(index);
+        mv.visitInsn(AALOAD);
+    }
+    
+    private void prepareSiteAndReceiver(Expression receiver, String methodName, boolean implicitThis) {
+        prepareSiteAndReceiver(receiver, methodName, implicitThis, false);
+    }
+    
+    protected void prepareSiteAndReceiver(Expression receiver, String methodName, boolean implicitThis, boolean lhs) {
+        //site
+        prepareCallSite(methodName);
+
+        // receiver
+        CompileStack compileStack = controller.getCompileStack();
+        compileStack.pushImplicitThis(implicitThis);
+        compileStack.pushLHS(lhs);
+        receiver.visit(controller.getAcg());
+        controller.getOperandStack().box();
+        compileStack.popLHS();
+        compileStack.popImplicitThis();
+    }
+    
+    protected void visitBoxedArgument(Expression exp) {
+        exp.visit(controller.getAcg());
+        if (!(exp instanceof TupleExpression)) {
+            // we are not in a tuple, so boxing might be missing for
+            // this single argument call
+            controller.getOperandStack().box();
+        }
+    }
+
+    public final void makeSingleArgumentCall(Expression receiver, String message, Expression arguments) {
+        makeSingleArgumentCall(receiver, message, arguments, false);
+    }
+
+    public void makeSingleArgumentCall(Expression receiver, String message, Expression arguments, boolean safe) {
+        OperandStack operandStack = controller.getOperandStack();
+        int m1 = operandStack.getStackLength();
+        //slow Path
+        prepareSiteAndReceiver(receiver, message, false, controller.getCompileStack().isLHS());
+        visitBoxedArgument(arguments);
+        int m2 = operandStack.getStackLength();
+        controller.getMethodVisitor().visitMethodInsn(INVOKEINTERFACE, "org/codehaus/groovy/runtime/callsite/CallSite", safe ? "callSafe" : "call", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
+        operandStack.replace(ClassHelper.OBJECT_TYPE, m2-m1);
+    }
+    
+    public void makeGroovyObjectGetPropertySite(Expression receiver, String methodName, boolean safe, boolean implicitThis) {
+        prepareSiteAndReceiver(receiver, methodName, implicitThis);
+        invokeSafe(safe, "callGroovyObjectGetProperty", "callGroovyObjectGetPropertySafe");
+    }
+    
+    public void makeGetPropertySite(Expression receiver, String methodName, boolean safe, boolean implicitThis) {
+        prepareSiteAndReceiver(receiver, methodName, implicitThis);
+        invokeSafe(safe, "callGetProperty", "callGetPropertySafe");
+    }
+    
+    public void makeCallSite(Expression receiver, String message, Expression arguments, boolean safe, boolean implicitThis, boolean callCurrent, boolean callStatic) {
+        prepareSiteAndReceiver(receiver, message, implicitThis);
+
+        CompileStack compileStack = controller.getCompileStack();
+        compileStack.pushImplicitThis(implicitThis);
+        compileStack.pushLHS(false);
+        boolean constructor = message.equals(CONSTRUCTOR);
+        OperandStack operandStack = controller.getOperandStack();
+        
+        // arguments
+        boolean containsSpreadExpression = AsmClassGenerator.containsSpreadExpression(arguments);
+        int numberOfArguments = containsSpreadExpression ? -1 : AsmClassGenerator.argumentSize(arguments);
+        int operandsToReplace = 1;
+        if (numberOfArguments > MethodCallerMultiAdapter.MAX_ARGS || containsSpreadExpression) {
+            ArgumentListExpression ae;
+            if (arguments instanceof ArgumentListExpression) {
+                ae = (ArgumentListExpression) arguments;
+            } else if (arguments instanceof TupleExpression) {
+                TupleExpression te = (TupleExpression) arguments;
+                ae = new ArgumentListExpression(te.getExpressions());
+            } else {
+                ae = new ArgumentListExpression();
+                ae.addExpression(arguments);
+            }
+            controller.getCompileStack().pushImplicitThis(false);
+            if (containsSpreadExpression) {
+                numberOfArguments = -1;
+                controller.getAcg().despreadList(ae.getExpressions(), true);
+            } else {
+                numberOfArguments = ae.getExpressions().size();
+                for (int i = 0; i < numberOfArguments; i++) {
+                    Expression argument = ae.getExpression(i);
+                    argument.visit(controller.getAcg());
+                    operandStack.box();
+                    if (argument instanceof CastExpression) controller.getAcg().loadWrapper(argument);
+                }
+                operandsToReplace += numberOfArguments;
+            }
+            controller.getCompileStack().popImplicitThis();
+        }
+        controller.getCompileStack().popLHS();
+        controller.getCompileStack().popImplicitThis();
+        
+        MethodVisitor mv = controller.getMethodVisitor();
+        
+        if (numberOfArguments > 4) {
+            final String createArraySignature = getCreateArraySignature(numberOfArguments);
+            mv.visitMethodInsn(INVOKESTATIC, "org/codehaus/groovy/runtime/ArrayUtil", "createArray", createArraySignature, false);
+            //TODO: use pre-generated Object[]
+            operandStack.replace(ClassHelper.OBJECT_TYPE.makeArray(),numberOfArguments);
+            operandsToReplace = operandsToReplace-numberOfArguments+1;
+        }
+
+        final String desc = getDescForParamNum(numberOfArguments);
+        if (callStatic) {
+            mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callStatic", "(Ljava/lang/Class;" + desc, true);
+        } else if (constructor) {
+            mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callConstructor", "(Ljava/lang/Object;" + desc, true);
+        } else if (callCurrent) {
+            mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callCurrent", "(Lgroovy/lang/GroovyObject;" + desc, true);
+        } else if (safe) {
+            mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "callSafe", "(Ljava/lang/Object;" + desc, true);
+        } else {
+            mv.visitMethodInsn(INVOKEINTERFACE, CALLSITE_CLASS, "call", "(Ljava/lang/Object;" + desc, true);
+        }
+        operandStack.replace(ClassHelper.OBJECT_TYPE,operandsToReplace);
+    }
+    
+    private static String getDescForParamNum(int numberOfArguments) {
+        switch (numberOfArguments) {
+            case 0:
+              return ")Ljava/lang/Object;";
+            case 1:
+              return "Ljava/lang/Object;)Ljava/lang/Object;";
+            case 2:
+                return "Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;";
+            case 3:
+                return "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;";
+            case 4:
+                return "Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;";
+            default:
+                return "[Ljava/lang/Object;)Ljava/lang/Object;";
+        }
+    }
+
+    public List<String> getCallSites() {
+        return callSites;
+    }
+
+    public void makeCallSiteArrayInitializer() {
+        final String classInternalName = BytecodeHelper.getClassInternalName(controller.getClassNode());
+        MethodVisitor mv = controller.getMethodVisitor();
+        mv.visitInsn(ACONST_NULL);
+        mv.visitFieldInsn(PUTSTATIC, classInternalName, "$callSiteArray", "Ljava/lang/ref/SoftReference;");
+    }
+
+    public boolean hasCallSiteUse() {
+        return callSiteArrayVarIndex>=0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
new file mode 100644
index 0000000..af305ba
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ClosureWriter.java
@@ -0,0 +1,392 @@
+/*
+ *  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;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.InnerClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+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.FieldExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.Verifier;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.DUP;
+import static org.objectweb.asm.Opcodes.GETFIELD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.NEW;
+
+public class ClosureWriter {
+
+    protected interface UseExistingReference {}
+
+    private final Map<Expression,ClassNode> closureClassMap;
+    private final WriterController controller;
+    private final WriterControllerFactory factory;
+
+    public ClosureWriter(WriterController wc) {
+        this.controller = wc;
+        closureClassMap = new HashMap<Expression,ClassNode>();
+        factory = new WriterControllerFactory() {
+            public WriterController makeController(final WriterController normalController) {
+                return controller;
+            }
+        };
+    }
+
+    public void writeClosure(ClosureExpression expression) {
+        CompileStack compileStack = controller.getCompileStack();
+        MethodVisitor mv = controller.getMethodVisitor();
+        ClassNode classNode = controller.getClassNode();
+        AsmClassGenerator acg = controller.getAcg();
+
+        // generate closure as public class to make sure it can be properly invoked by classes of the
+        // Groovy runtime without circumventing JVM access checks (see CachedMethod for example).
+        int mods = ACC_PUBLIC;
+        if (classNode.isInterface()) {
+            mods |= ACC_STATIC;
+        }
+        ClassNode closureClass = getOrAddClosureClass(expression, mods);
+        String closureClassinternalName = BytecodeHelper.getClassInternalName(closureClass);
+        List constructors = closureClass.getDeclaredConstructors();
+        ConstructorNode node = (ConstructorNode) constructors.get(0);
+
+        Parameter[] localVariableParams = node.getParameters();
+
+        mv.visitTypeInsn(NEW, closureClassinternalName);
+        mv.visitInsn(DUP);
+        if (controller.isStaticMethod() || compileStack.isInSpecialConstructorCall()) {
+            (new ClassExpression(classNode)).visit(acg);
+            (new ClassExpression(controller.getOutermostClass())).visit(acg);
+        } else {
+            mv.visitVarInsn(ALOAD, 0);
+            controller.getOperandStack().push(ClassHelper.OBJECT_TYPE);
+            loadThis();
+        }
+
+        // now let's load the various parameters we're passing
+        // we start at index 2 because the first variable we pass
+        // is the owner instance and at this point it is already
+        // on the stack
+        for (int i = 2; i < localVariableParams.length; i++) {
+            Parameter param = localVariableParams[i];
+            String name = param.getName();
+            loadReference(name, controller);
+            if (param.getNodeMetaData(ClosureWriter.UseExistingReference.class)==null) {
+                param.setNodeMetaData(ClosureWriter.UseExistingReference.class,Boolean.TRUE);
+            }
+        }
+
+        // we may need to pass in some other constructors
+        //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
+        mv.visitMethodInsn(INVOKESPECIAL, closureClassinternalName, "<init>", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, localVariableParams), false);
+        controller.getOperandStack().replace(ClassHelper.CLOSURE_TYPE, localVariableParams.length);
+    }
+    
+    public static void loadReference(String name, WriterController controller) {
+        CompileStack compileStack = controller.getCompileStack();
+        MethodVisitor mv = controller.getMethodVisitor();
+        ClassNode classNode = controller.getClassNode();
+        AsmClassGenerator acg = controller.getAcg();
+        
+        // compileStack.containsVariable(name) means to ask if the variable is already declared
+        // compileStack.getScope().isReferencedClassVariable(name) means to ask if the variable is a field
+        // If it is no field and is not yet declared, then it is either a closure shared variable or
+        // an already declared variable.
+        if (!compileStack.containsVariable(name) && compileStack.getScope().isReferencedClassVariable(name)) {
+            acg.visitFieldExpression(new FieldExpression(classNode.getDeclaredField(name)));
+        } else {
+            BytecodeVariable v = compileStack.getVariable(name, !classNodeUsesReferences(controller.getClassNode()));
+            if (v == null) {
+                // variable is not on stack because we are
+                // inside a nested Closure and this variable
+                // was not used before
+                // then load it from the Closure field
+                FieldNode field = classNode.getDeclaredField(name);
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitFieldInsn(GETFIELD, controller.getInternalClassName(), name, BytecodeHelper.getTypeDescription(field.getType()));
+            } else {
+                mv.visitVarInsn(ALOAD, v.getIndex());
+            }
+            controller.getOperandStack().push(ClassHelper.REFERENCE_TYPE);
+        }
+    }
+
+    public ClassNode getOrAddClosureClass(ClosureExpression expression, int mods) {
+        ClassNode closureClass = closureClassMap.get(expression);
+        if (closureClass == null) {
+            closureClass = createClosureClass(expression, mods);
+            closureClassMap.put(expression, closureClass);
+            controller.getAcg().addInnerClass(closureClass);
+            closureClass.addInterface(ClassHelper.GENERATED_CLOSURE_Type);
+            closureClass.putNodeMetaData(WriterControllerFactory.class, factory);
+        }
+        return closureClass;
+    }
+
+    private static boolean classNodeUsesReferences(ClassNode classNode) {
+        boolean ret = classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
+        if (ret) return ret;
+        if (classNode instanceof InnerClassNode) {
+            InnerClassNode inner = (InnerClassNode) classNode;
+            return inner.isAnonymous();
+        }
+        return false;
+    }
+    
+    protected ClassNode createClosureClass(ClosureExpression expression, int mods) {
+        ClassNode classNode = controller.getClassNode();
+        ClassNode outerClass = controller.getOutermostClass();
+        MethodNode methodNode = controller.getMethodNode();
+        String name = classNode.getName() + "$"
+                + controller.getContext().getNextClosureInnerName(outerClass, classNode, methodNode); // add a more informative name
+        boolean staticMethodOrInStaticClass = controller.isStaticMethod() || classNode.isStaticClass();
+
+        Parameter[] parameters = expression.getParameters();
+        if (parameters == null) {
+            parameters = Parameter.EMPTY_ARRAY;
+        } else if (parameters.length == 0) {
+            // let's create a default 'it' parameter
+            Parameter it = new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL);
+            parameters = new Parameter[]{it};
+            Variable ref = expression.getVariableScope().getDeclaredVariable("it");
+            if (ref!=null) it.setClosureSharedVariable(ref.isClosureSharedVariable());
+        }
+
+        Parameter[] localVariableParams = getClosureSharedVariables(expression);
+        removeInitialValues(localVariableParams);
+
+        InnerClassNode answer = new InnerClassNode(classNode, name, mods, ClassHelper.CLOSURE_TYPE.getPlainNodeReference()); 
+        answer.setEnclosingMethod(controller.getMethodNode());
+        answer.setSynthetic(true);
+        answer.setUsingGenerics(outerClass.isUsingGenerics());
+        answer.setSourcePosition(expression);
+
+        if (staticMethodOrInStaticClass) {
+            answer.setStaticClass(true);
+        }
+        if (controller.isInScriptBody()) {
+            answer.setScriptBody(true);
+        }
+        MethodNode method =
+                answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
+        method.setSourcePosition(expression);
+
+        VariableScope varScope = expression.getVariableScope();
+        if (varScope == null) {
+            throw new RuntimeException(
+                    "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
+        } else {
+            method.setVariableScope(varScope.copy());
+        }
+        if (parameters.length > 1
+                || (parameters.length == 1
+                && parameters[0].getType() != null
+                && parameters[0].getType() != ClassHelper.OBJECT_TYPE
+                && !ClassHelper.OBJECT_TYPE.equals(parameters[0].getType().getComponentType())))
+        {
+
+            // let's add a typesafe call method
+            MethodNode call = answer.addMethod(
+                    "call",
+                    ACC_PUBLIC,
+                    ClassHelper.OBJECT_TYPE,
+                    parameters,
+                    ClassNode.EMPTY_ARRAY,
+                    new ReturnStatement(
+                            new MethodCallExpression(
+                                    VariableExpression.THIS_EXPRESSION,
+                                    "doCall",
+                                    new ArgumentListExpression(parameters))));
+            call.setSourcePosition(expression);
+        }
+
+        // let's make the constructor
+        BlockStatement block = new BlockStatement();
+        // this block does not get a source position, because we don't
+        // want this synthetic constructor to show up in corbertura reports
+        VariableExpression outer = new VariableExpression("_outerInstance");
+        outer.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(outer);
+        VariableExpression thisObject = new VariableExpression("_thisObject");
+        thisObject.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(thisObject);
+        TupleExpression conArgs = new TupleExpression(outer, thisObject);
+        block.addStatement(
+                new ExpressionStatement(
+                        new ConstructorCallExpression(
+                                ClassNode.SUPER,
+                                conArgs)));
+
+        // let's assign all the parameter fields from the outer context
+        for (Parameter param : localVariableParams) {
+            String paramName = param.getName();
+            ClassNode type = param.getType();
+            if (true) {
+                VariableExpression initialValue = new VariableExpression(paramName);
+                initialValue.setAccessedVariable(param);
+                initialValue.setUseReferenceDirectly(true);
+                ClassNode realType = type;
+                type = ClassHelper.makeReference();
+                param.setType(ClassHelper.makeReference());
+                FieldNode paramField = answer.addField(paramName, ACC_PRIVATE | ACC_SYNTHETIC, type, initialValue);
+                paramField.setOriginType(ClassHelper.getWrapper(param.getOriginType()));
+                paramField.setHolder(true);
+                String methodName = Verifier.capitalize(paramName);
+
+                // let's add a getter & setter
+                Expression fieldExp = new FieldExpression(paramField);
+                answer.addMethod(
+                        "get" + methodName,
+                        ACC_PUBLIC,
+                        realType.getPlainNodeReference(),
+                        Parameter.EMPTY_ARRAY,
+                        ClassNode.EMPTY_ARRAY,
+                        new ReturnStatement(fieldExp));
+            }
+        }
+
+        Parameter[] params = new Parameter[2 + localVariableParams.length];
+        params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance");
+        params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject");
+        System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
+
+        ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, block);
+        sn.setSourcePosition(expression);
+        
+        correctAccessedVariable(answer,expression);
+        
+        return answer;
+    }
+
+    private static void correctAccessedVariable(final InnerClassNode closureClass, ClosureExpression ce) {
+        CodeVisitorSupport visitor = new CodeVisitorSupport() {
+            @Override
+            public void visitVariableExpression(VariableExpression expression) {
+                Variable v = expression.getAccessedVariable(); 
+                if (v==null) return;
+                if (!(v instanceof FieldNode)) return;
+                String name = expression.getName();
+                FieldNode fn = closureClass.getDeclaredField(name);
+                if (fn != null) { // only overwrite if we find something more specific
+                    expression.setAccessedVariable(fn);
+                }
+            }  
+        };
+        visitor.visitClosureExpression(ce);
+    }
+
+    /*
+     * this method is called for local variables shared between scopes.
+     * These variables must not have init values because these would
+     * then in later steps be used to create multiple versions of the
+     * same method, in this case the constructor. A closure should not
+     * have more than one constructor!
+     */
+    private static void removeInitialValues(Parameter[] params) {
+        for (int i = 0; i < params.length; i++) {
+            if (params[i].hasInitialExpression()) {
+                Parameter p = new Parameter(params[i].getType(), params[i].getName());
+                p.setOriginType(p.getOriginType());
+                params[i] = p;
+            }
+        }
+    }
+
+    public boolean addGeneratedClosureConstructorCall(ConstructorCallExpression call) {
+        ClassNode classNode = controller.getClassNode();
+        if (!classNode.declaresInterface(ClassHelper.GENERATED_CLOSURE_Type)) return false;
+
+        AsmClassGenerator acg = controller.getAcg();
+        OperandStack operandStack = controller.getOperandStack();
+        
+        MethodVisitor mv = controller.getMethodVisitor();
+        mv.visitVarInsn(ALOAD, 0);
+        ClassNode callNode = classNode.getSuperClass();
+        TupleExpression arguments = (TupleExpression) call.getArguments();
+        if (arguments.getExpressions().size()!=2) throw new GroovyBugError("expected 2 arguments for closure constructor super call, but got"+arguments.getExpressions().size());
+        arguments.getExpression(0).visit(acg);
+        operandStack.box();
+        arguments.getExpression(1).visit(acg);
+        operandStack.box();
+        //TODO: replace with normal String, p not needed
+        Parameter p = new Parameter(ClassHelper.OBJECT_TYPE,"_p");
+        String descriptor = BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, new Parameter[]{p,p});
+        mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(callNode), "<init>", descriptor, false);
+        operandStack.remove(2);
+        return true;
+    }
+
+    protected Parameter[] getClosureSharedVariables(ClosureExpression ce) {
+        VariableScope scope = ce.getVariableScope();
+        Parameter[] ret = new Parameter[scope.getReferencedLocalVariablesCount()];
+        int index = 0;
+        for (Iterator iter = scope.getReferencedLocalVariablesIterator(); iter.hasNext();) {
+            Variable element = (org.codehaus.groovy.ast.Variable) iter.next();
+            Parameter p = new Parameter(element.getType(), element.getName());
+            p.setOriginType(element.getOriginType());
+            p.setClosureSharedVariable(element.isClosureSharedVariable());
+            ret[index] = p;
+            index++;
+        }
+        return ret;
+    }
+    
+    private void loadThis() {
+        MethodVisitor mv = controller.getMethodVisitor();
+        mv.visitVarInsn(ALOAD, 0);
+        if (controller.isInClosure()) {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Closure", "getThisObject", "()Ljava/lang/Object;", false);
+            controller.getOperandStack().push(ClassHelper.OBJECT_TYPE);
+        } else {
+            controller.getOperandStack().push(controller.getClassNode());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java b/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
new file mode 100644
index 0000000..35133bb
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/CompileStack.java
@@ -0,0 +1,872 @@
+/*
+ *  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;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.VariableScope;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+/**
+ * This class is a helper for AsmClassGenerator. It manages
+ * different aspects of the code of a code block like
+ * handling labels, defining variables, and scopes.
+ * After a MethodNode is visited clear should be called, for
+ * initialization the method init should be used.
+ * <p>
+ * Some Notes:
+ * <ul>
+ * <li> every push method will require a later pop call
+ * <li> method parameters may define a category 2 variable, so
+ *      don't ignore the type stored in the variable object
+ * <li> the index of the variable may not be as assumed when
+ *      the variable is a parameter of a method because the
+ *      parameter may be used in a closure, so don't ignore
+ *      the stored variable index
+ * <li> the names of temporary variables can be ignored. The names
+ *      are only used for debugging and do not conflict with each
+ *      other or normal variables. For accessing, the index of the
+ *      variable must be used.
+ * <li> never mix temporary and normal variables by changes to this class.
+ *      While the name is very important for a normal variable, it is only a
+ *      helper construct for temporary variables. That means for example a
+ *      name for a temporary variable can be used multiple times without
+ *      conflict. So mixing them both may lead to the problem that a normal
+ *      or temporary variable is hidden or even removed.  That must not happen!
+ * </ul>
+ *
+ *
+ * @see org.codehaus.groovy.classgen.AsmClassGenerator
+ * @author Jochen Theodorou
+ */
+public class CompileStack implements Opcodes {
+    /**
+     * TODO: remove optimization of this.foo -> this.@foo
+     *
+     */
+
+    // state flag
+    private boolean clear=true;
+    // current scope
+    private VariableScope scope;
+    // current label for continue
+    private Label continueLabel;
+    // current label for break
+    private Label breakLabel;
+    // available variables on stack
+    private Map stackVariables = new HashMap();
+    // index of the last variable on stack
+    private int currentVariableIndex = 1;
+    // index for the next variable on stack
+    private int nextVariableIndex = 1;
+    // currently temporary variables in use
+    private final LinkedList temporaryVariables = new LinkedList();
+    // overall used variables for a method/constructor
+    private final LinkedList usedVariables = new LinkedList();
+    // map containing named labels of parenting blocks
+    private Map superBlockNamedLabels = new HashMap();
+    // map containing named labels of current block
+    private Map currentBlockNamedLabels = new HashMap();
+    // list containing finally blocks
+    // such a block is created by synchronized or finally and
+    // must be called for break/continue/return
+    private LinkedList<BlockRecorder> finallyBlocks = new LinkedList<BlockRecorder>();
+    private final LinkedList<BlockRecorder> visitedBlocks = new LinkedList<BlockRecorder>();
+
+    private Label thisStartLabel, thisEndLabel;
+
+//    private MethodVisitor mv;
+
+    // helper to handle different stack based variables
+    private final LinkedList stateStack = new LinkedList();
+
+    // handle different states for the implicit "this"
+    private final LinkedList<Boolean> implicitThisStack = new LinkedList<Boolean>();
+    // handle different states for being on the left hand side
+    private final LinkedList<Boolean> lhsStack = new LinkedList<Boolean>();
+    {
+        implicitThisStack.add(false);
+        lhsStack.add(false);
+    }
+
+    // defines the first variable index usable after
+    // all parameters of a method
+    private int localVariableOffset;
+    // this is used to store the goals for a "break foo" call
+    // in a loop where foo is a label.
+    private final Map namedLoopBreakLabel = new HashMap();
+    // this is used to store the goals for a "continue foo" call
+    // in a loop where foo is a label.
+    private final Map namedLoopContinueLabel = new HashMap();
+    private String className;
+    private final LinkedList<ExceptionTableEntry> typedExceptions = new LinkedList<ExceptionTableEntry>();
+    private final LinkedList<ExceptionTableEntry> untypedExceptions = new LinkedList<ExceptionTableEntry>();
+    // stores if on left-hand-side during compilation
+    private boolean lhs;
+    // stores if implicit or explicit this is used.
+    private boolean implicitThis;
+    private final WriterController controller;
+    private boolean inSpecialConstructorCall;
+
+    protected static class LabelRange {
+        public Label start;
+        public Label end;
+    }
+
+    public static class BlockRecorder {
+        private boolean isEmpty = true;
+        public Runnable excludedStatement;
+        public final LinkedList<LabelRange> ranges;
+        public BlockRecorder() {
+            ranges = new LinkedList<LabelRange>();
+        }
+        public BlockRecorder(Runnable excludedStatement) {
+            this();
+            this.excludedStatement = excludedStatement;
+        }
+        public void startRange(Label start) {
+            LabelRange range = new LabelRange();
+            range.start = start;
+            ranges.add(range);
+            isEmpty = false;
+        }
+        public void closeRange(Label end) {
+            ranges.getLast().end = end;
+        }
+    }
+
+    private static class ExceptionTableEntry {
+        Label start,end,goal;
+        String sig;
+    }
+
+    private class StateStackElement {
+        final VariableScope scope;
+        final Label continueLabel;
+        final Label breakLabel;
+        final Map stackVariables;
+        final Map currentBlockNamedLabels;
+        final LinkedList<BlockRecorder> finallyBlocks;
+        final boolean inSpecialConstructorCall;
+
+        StateStackElement() {
+            scope = CompileStack.this.scope;
+            continueLabel = CompileStack.this.continueLabel;
+            breakLabel = CompileStack.this.breakLabel;
+            stackVariables = CompileStack.this.stackVariables;
+            currentBlockNamedLabels = CompileStack.this.currentBlockNamedLabels;
+            finallyBlocks = CompileStack.this.finallyBlocks;
+            inSpecialConstructorCall = CompileStack.this.inSpecialConstructorCall;
+        }
+    }
+
+    public CompileStack(WriterController wc) {
+        this.controller = wc;
+    }
+
+    public void pushState() {
+        stateStack.add(new StateStackElement());
+        stackVariables = new HashMap(stackVariables);
+        finallyBlocks = new LinkedList(finallyBlocks);
+    }
+
+    private void popState() {
+        if (stateStack.isEmpty()) {
+             throw new GroovyBugError("Tried to do a pop on the compile stack without push.");
+        }
+        StateStackElement element = (StateStackElement) stateStack.removeLast();
+        scope = element.scope;
+        continueLabel = element.continueLabel;
+        breakLabel = element.breakLabel;
+        stackVariables = element.stackVariables;
+        finallyBlocks = element.finallyBlocks;
+        inSpecialConstructorCall = element.inSpecialConstructorCall;
+    }
+
+    public Label getContinueLabel() {
+        return continueLabel;
+    }
+
+    public Label getBreakLabel() {
+        return breakLabel;
+    }
+
+    public void removeVar(int tempIndex) {
+        final BytecodeVariable head = (BytecodeVariable) temporaryVariables.removeFirst();
+        if (head.getIndex() != tempIndex) {
+            temporaryVariables.addFirst(head);
+            MethodNode methodNode = controller.getMethodNode();
+            if (methodNode==null) {
+                methodNode = controller.getConstructorNode();
+            }
+            throw new GroovyBugError(
+                    "In method "+ (methodNode!=null?methodNode.getText():"<unknown>") + ", " +
+                    "CompileStack#removeVar: tried to remove a temporary " +
+                    "variable with index "+ tempIndex + " in wrong order. " +
+                    "Current temporary variables=" + temporaryVariables);
+        }
+    }
+
+    private void setEndLabels(){
+        Label endLabel = new Label();
+        controller.getMethodVisitor().visitLabel(endLabel);
+        for (Iterator iter = stackVariables.values().iterator(); iter.hasNext();) {
+            BytecodeVariable var = (BytecodeVariable) iter.next();
+            var.setEndLabel(endLabel);
+        }
+        thisEndLabel = endLabel;
+    }
+
+    public void pop() {
+        setEndLabels();
+        popState();
+    }
+
+    public VariableScope getScope() {
+        return scope;
+    }
+
+    /**
+     * creates a temporary variable.
+     *
+     * @param var defines type and name
+     * @param store defines if the toplevel argument of the stack should be stored
+     * @return the index used for this temporary variable
+     */
+    public int defineTemporaryVariable(Variable var, boolean store) {
+        return defineTemporaryVariable(var.getName(), var.getType(),store);
+    }
+
+    public BytecodeVariable getVariable(String variableName ) {
+        return getVariable(variableName, true);
+    }
+
+    /**
+     * Returns a normal variable.
+     * <p>
+     * If <code>mustExist</code> is true and the normal variable doesn't exist,
+     * then this method will throw a GroovyBugError. It is not the intention of
+     * this method to let this happen! And the exception should not be used for
+     * flow control - it is just acting as an assertion. If the exception is thrown
+     * then it indicates a bug in the class using CompileStack.
+     * This method can also not be used to return a temporary variable.
+     * Temporary variables are not normal variables.
+     *
+     * @param variableName name of the variable
+     * @param mustExist    throw exception if variable does not exist
+     * @return the normal variable or null if not found (and <code>mustExist</code> not true)
+     */
+    public BytecodeVariable getVariable(String variableName, boolean mustExist) {
+        if (variableName.equals("this")) return BytecodeVariable.THIS_VARIABLE;
+        if (variableName.equals("super")) return BytecodeVariable.SUPER_VARIABLE;
+        BytecodeVariable v = (BytecodeVariable) stackVariables.get(variableName);
+        if (v == null && mustExist)
+            throw new GroovyBugError("tried to get a variable with the name " + variableName + " as stack variable, but a variable with this name was not created");
+        return v;
+    }
+
+    /**
+     * creates a temporary variable.
+     *
+     * @param name defines type and name
+     * @param store defines if the top-level argument of the stack should be stored
+     * @return the index used for this temporary variable
+     */
+    public int defineTemporaryVariable(String name,boolean store) {
+        return defineTemporaryVariable(name, ClassHelper.DYNAMIC_TYPE,store);
+    }
+
+    /**
+     * creates a temporary variable.
+     *
+     * @param name defines the name
+     * @param node defines the node
+     * @param store defines if the top-level argument of the stack should be stored
+     * @return the index used for this temporary variable
+     */
+    public int defineTemporaryVariable(String name, ClassNode node, boolean store) {
+        BytecodeVariable answer = defineVar(name, node, false, false);
+        temporaryVariables.addFirst(answer); // TRICK: we add at the beginning so when we find for remove or get we always have the last one
+        usedVariables.removeLast();
+
+        if (store) controller.getOperandStack().storeVar(answer);
+
+        return answer.getIndex();
+    }
+
+    private void resetVariableIndex(boolean isStatic) {
+        temporaryVariables.clear();
+        if (!isStatic) {
+            currentVariableIndex=1;
+            nextVariableIndex=1;
+        } else {
+            currentVariableIndex=0;
+            nextVariableIndex=0;
+        }
+    }
+
+    /**
+     * Clears the state of the class. This method should be called
+     * after a MethodNode is visited. Note that a call to init will
+     * fail if clear is not called before
+     */
+    public void clear() {
+        if (stateStack.size()>1) {
+            int size = stateStack.size()-1;
+            throw new GroovyBugError("the compile stack contains "+size+" more push instruction"+(size==1?"":"s")+" than pops.");
+        }
+        if (lhsStack.size()>1) {
+            int size = lhsStack.size()-1;
+            throw new GroovyBugError("lhs stack is supposed to be empty, but has " +
+                                     size + " elements left.");
+        }
+        if (implicitThisStack.size()>1) {
+            int size = implicitThisStack.size()-1;
+            throw new GroovyBugError("implicit 'this' stack is supposed to be empty, but has " +
+                                     size + " elements left.");
+        }
+        clear = true;
+        MethodVisitor mv = controller.getMethodVisitor();
+        // br experiment with local var table so debuggers can retrieve variable names
+        if (true) {//AsmClassGenerator.CREATE_DEBUG_INFO) {
+            if (thisEndLabel==null) setEndLabels();
+
+            if (!scope.isInStaticContext()) {
+                // write "this"
+                mv.visitLocalVariable("this", className, null, thisStartLabel, thisEndLabel, 0);
+            }
+
+            for (Iterator iterator = usedVariables.iterator(); iterator.hasNext();) {
+                BytecodeVariable v = (BytecodeVariable) iterator.next();
+                ClassNode t = v.getType();
+                if (v.isHolder()) t = ClassHelper.REFERENCE_TYPE;
+                String type = BytecodeHelper.getTypeDescription(t);
+                Label start = v.getStartLabel();
+                Label end = v.getEndLabel();
+                mv.visitLocalVariable(v.getName(), type, null, start, end, v.getIndex());
+            }
+        }
+
+        //exception table writing
+        for (ExceptionTableEntry ep : typedExceptions) {
+            mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig);
+        }
+        //exception table writing
+        for (ExceptionTableEntry ep : untypedExceptions) {
+            mv.visitTryCatchBlock(ep.start, ep.end, ep.goal, ep.sig);
+        }
+
+
+        pop();
+        typedExceptions.clear();
+        untypedExceptions.clear();
+        stackVariables.clear();
+        usedVariables.clear();
+        scope = null;
+        finallyBlocks.clear();
+        resetVariableIndex(false);
+        superBlockNamedLabels.clear();
+        currentBlockNamedLabels.clear();
+        namedLoopBreakLabel.clear();
+        namedLoopContinueLabel.clear();
+        continueLabel=null;
+        breakLabel=null;
+        thisStartLabel=null;
+        thisEndLabel=null;
+        mv = null;
+    }
+
+    public void addExceptionBlock (Label start, Label end, Label goal,
+                                   String sig)
+    {
+        // this code is in an extra method to avoid
+        // lazy initialization issues
+        ExceptionTableEntry ep = new ExceptionTableEntry();
+        ep.start = start;
+        ep.end = end;
+        ep.sig = sig;
+        ep.goal = goal;
+        if (sig==null) {
+            untypedExceptions.add(ep);
+        } else {
+            typedExceptions.add(ep);
+        }
+    }
+
+    /**
+     * initializes this class for a MethodNode. This method will
+     * automatically define variables for the method parameters
+     * and will create references if needed.  The created variables
+     * can be accessed by calling getVariable().
+     *
+     */
+    public void init(VariableScope el, Parameter[] parameters) {
+        if (!clear) throw new GroovyBugError("CompileStack#init called without calling clear before");
+        clear=false;
+        pushVariableScope(el);
+        defineMethodVariables(parameters,el.isInStaticContext());
+        this.className = BytecodeHelper.getTypeDescription(controller.getClassNode());
+    }
+
+    /**
+     * Causes the state-stack to add an element and sets
+     * the given scope as new current variable scope. Creates
+     * a element for the state stack so pop has to be called later
+     */
+    public void pushVariableScope(VariableScope el) {
+        pushState();
+        scope = el;
+        superBlockNamedLabels = new HashMap(superBlockNamedLabels);
+        superBlockNamedLabels.putAll(currentBlockNamedLabels);
+        currentBlockNamedLabels = new HashMap();
+    }
+
+    /**
+     * Should be called when descending into a loop that defines
+     * also a scope. Calls pushVariableScope and prepares labels
+     * for a loop structure. Creates a element for the state stack
+     * so pop has to be called later, TODO: @Deprecate
+     */
+    public void pushLoop(VariableScope el, String labelName) {
+        pushVariableScope(el);
+        continueLabel = new Label();
+        breakLabel = new Label();
+        if (labelName != null) {
+            initLoopLabels(labelName);
+        }
+    }
+
+    /**
+     * Should be called when descending into a loop that defines
+     * also a scope. Calls pushVariableScope and prepares labels
+     * for a loop structure. Creates a element for the state stack
+     * so pop has to be called later
+     */
+    public void pushLoop(VariableScope el, List<String> labelNames) {
+        pushVariableScope(el);
+        continueLabel = new Label();
+        breakLabel = new Label();
+        if (labelNames != null) {
+            for (String labelName : labelNames) {
+                initLoopLabels(labelName);
+            }
+        }
+    }
+
+    private void initLoopLabels(String labelName) {
+        namedLoopBreakLabel.put(labelName,breakLabel);
+        namedLoopContinueLabel.put(labelName,continueLabel);
+    }
+
+    /**
+     * Should be called when descending into a loop that does
+     * not define a scope. Creates a element for the state stack
+     * so pop has to be called later, TODO: @Deprecate
+     */
+    public void pushLoop(String labelName) {
+        pushState();
+        continueLabel = new Label();
+        breakLabel = new Label();
+        initLoopLabels(labelName);
+    }
+
+    /**
+     * Should be called when descending into a loop that does
+     * not define a scope. Creates a element for the state stack
+     * so pop has to be called later
+     */
+    public void pushLoop(List<String> labelNames) {
+        pushState();
+        continueLabel = new Label();
+        breakLabel = new Label();
+        if (labelNames != null) {
+            for (String labelName : labelNames) {
+                initLoopLabels(labelName);
+            }
+        }
+    }
+
+    /**
+     * Used for <code>break foo</code> inside a loop to end the
+     * execution of the marked loop. This method will return the
+     * break label of the loop if there is one found for the name.
+     * If not, the current break label is returned.
+     */
+    public Label getNamedBreakLabel(String name) {
+        Label label = getBreakLabel();
+        Label endLabel = null;
+        if (name!=null) endLabel = (Label) namedLoopBreakLabel.get(name);
+        if (endLabel!=null) label = endLabel;
+        return label;
+    }
+
+    /**
+     * Used for <code>continue foo</code> inside a loop to continue
+     * the execution of the marked loop. This method will return
+     * the break label of the loop if there is one found for the
+     * name. If not, getLabel is used.
+     */
+    public Label getNamedContinueLabel(String name) {
+        Label label = getLabel(name);
+        Label endLabel = null;
+        if (name!=null) endLabel = (Label) namedLoopContinueLabel.get(name);
+        if (endLabel!=null) label = endLabel;
+        return label;
+    }
+
+    /**
+     * Creates a new break label and a element for the state stack
+     * so pop has to be called later
+     */
+    public Label pushSwitch(){
+        pushState();
+        breakLabel = new Label();
+        return breakLabel;
+    }
+
+    /**
+     * because a boolean Expression may not be evaluated completely
+     * it is important to keep the registers clean
+     */
+    public void pushBooleanExpression(){
+        pushState();
+    }
+
+    private BytecodeVariable defineVar(String name, ClassNode type, boolean holder, boolean useReferenceDirectly) {
+        int prevCurrent = currentVariableIndex;
+        makeNextVariableID(type,useReferenceDirectly);
+        int index = currentVariableIndex;
+        if (holder && !useReferenceDirectly) index = localVariableOffset++;
+        BytecodeVariable answer = new BytecodeVariable(index, type, name, prevCurrent);
+        usedVariables.add(answer);
+        answer.setHolder(holder);
+        return answer;
+    }
+
+    private void makeLocalVariablesOffset(Parameter[] paras, boolean isInStaticContext) {
+        resetVariableIndex(isInStaticContext);
+
+        for (Parameter para : paras) {
+            makeNextVariableID(para.getType(), false);
+        }
+        localVariableOffset = nextVariableIndex;
+
+        resetVariableIndex(isInStaticContext);
+    }
+
+    private void defineMethodVariables(Parameter[] paras, boolean isInStaticContext) {
+        Label startLabel  = new Label();
+        thisStartLabel = startLabel;
+        controller.getMethodVisitor().visitLabel(startLabel);
+
+        makeLocalVariablesOffset(paras,isInStaticContext);
+
+        for (Parameter para : paras) {
+            String name = para.getName();
+            BytecodeVariable answer;
+            ClassNode type = para.getType();
+            if (para.isClosureSharedVariable()) {
+                boolean useExistingReference = para.getNodeMetaData(ClosureWriter.UseExistingReference.class) != null;
+                answer = defineVar(name, para.getOriginType(), true, useExistingReference);
+                answer.setStartLabel(startLabel);
+                if (!useExistingReference) {
+                    controller.getOperandStack().load(type, currentVariableIndex);
+                    controller.getOperandStack().box();
+
+                    // GROOVY-4237, the original variable should always appear
+                    // in the variable index, otherwise some programs get into
+                    // trouble. So we define a dummy variable for the packaging
+                    // phase and let it end right away before the normal
+                    // reference will be used
+                    Label newStart = new Label();
+                    controller.getMethodVisitor().visitLabel(newStart);
+                    BytecodeVariable var = new BytecodeVariable(currentVariableIndex, para.getOriginType(), name, currentVariableIndex);
+                    var.setStartLabel(startLabel);
+                    var.setEndLabel(newStart);
+                    usedVariables.add(var);
+                    answer.setStartLabel(newStart);
+
+                    createReference(answer);
+                }
+            } else {
+                answer = defineVar(name, type, false, false);
+                answer.setStartLabel(startLabel);
+            }
+            stackVariables.put(name, answer);
+        }
+
+        nextVariableIndex = localVariableOffset;
+    }
+
+    private void createReference(BytecodeVariable reference) {
+        MethodVisitor mv = controller.getMethodVisitor();
+        mv.visitTypeInsn(NEW, "groovy/lang/Reference");
+        mv.visitInsn(DUP_X1);
+        mv.visitInsn(SWAP);
+        mv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V", false);
+        mv.visitVarInsn(ASTORE, reference.getIndex());
+    }
+
+    private static void pushInitValue(ClassNode type, MethodVisitor mv) {
+        if (ClassHelper.isPrimitiveType(type)) {
+            if (type== ClassHelper.long_TYPE) {
+                mv.visitInsn(LCONST_0);
+            } else if (type== ClassHelper.double_TYPE) {
+                mv.visitInsn(DCONST_0);
+            } else if (type== ClassHelper.float_TYPE) {
+                mv.visitInsn(FCONST_0);
+            } else {
+                mv.visitLdcInsn(0);
+            }
+        } else {
+            mv.visitInsn(ACONST_NULL);
+        }
+    }
+
+    /**
+     * Defines a new Variable using an AST variable.
+     * @param initFromStack if true the last element of the
+     *                      stack will be used to initialize
+     *                      the new variable. If false null
+     *                      will be used.
+     */
+    public BytecodeVariable defineVariable(Variable v, boolean initFromStack) {
+        return defineVariable(v, v.getOriginType(), initFromStack);
+    }
+
+    public BytecodeVariable defineVariable(Variable v, ClassNode variableType, boolean initFromStack) {
+        String name = v.getName();
+        BytecodeVariable answer = defineVar(name, variableType, v.isClosureSharedVariable(), v.isClosureSharedVariable());
+        stackVariables.put(name, answer);
+
+        MethodVisitor mv = controller.getMethodVisitor();
+        Label startLabel  = new Label();
+        answer.setStartLabel(startLabel);
+        ClassNode type = answer.getType().redirect();
+        OperandStack operandStack = controller.getOperandStack();
+
+        if (!initFromStack) {
+            if (ClassHelper.isPrimitiveType(v.getOriginType()) && ClassHelper.getWrapper(v.getOriginType()) == variableType) {
+                pushInitValue(v.getOriginType(), mv);
+                operandStack.push(v.getOriginType());
+                operandStack.box();
+                operandStack.remove(1);
+            } else {
+                pushInitValue(type, mv);
+            }
+        }
+        operandStack.push(answer.getType());
+        if (answer.isHolder())  {
+            operandStack.box();
+            operandStack.remove(1);
+            createReference(answer);
+        } else {
+            operandStack.storeVar(answer);
+        }
+
+        mv.visitLabel(startLabel);
+        return answer;
+    }
+
+    /**
+     * @param name the name of the variable of interest
+     * @return true if a variable is already defined
+     */
+    public boolean containsVariable(String name) {
+        return stackVariables.containsKey(name);
+    }
+
+    /**
+     * Calculates the index of the next free register stores it
+     * and sets the current variable index to the old value
+     */
+    private void makeNextVariableID(ClassNode type, boolean useReferenceDirectly) {
+        currentVariableIndex = nextVariableIndex;
+        if ((type== ClassHelper.long_TYPE || type== ClassHelper.double_TYPE) && !useReferenceDirectly) {
+            nextVariableIndex++;
+        }
+        nextVariableIndex++;
+    }
+
+    /**
+     * Returns the label for the given name
+     */
+    public Label getLabel(String name) {
+        if (name==null) return null;
+        Label l = (Label) superBlockNamedLabels.get(name);
+        if (l==null) l = createLocalLabel(name);
+        return l;
+    }
+
+    /**
+     * creates a new named label
+     */
+    public Label createLocalLabel(String name) {
+        Label l = (Label) currentBlockNamedLabels.get(name);
+        if (l==null) {
+            l = new Label();
+            currentBlockNamedLabels.put(name,l);
+        }
+        return l;
+    }
+
+    public void applyFinallyBlocks(Label label, boolean isBreakLabel) {
+        // first find the state defining the label. That is the state
+        // directly after the state not knowing this label. If no state
+        // in the list knows that label, then the defining state is the
+        // current state.
+        StateStackElement result = null;
+        for (ListIterator iter = stateStack.listIterator(stateStack.size()); iter.hasPrevious();) {
+            StateStackElement element = (StateStackElement) iter.previous();
+            if (!element.currentBlockNamedLabels.values().contains(label)) {
+                if (isBreakLabel && element.breakLabel != label) {
+                    result = element;
+                    break;
+                }
+                if (!isBreakLabel && element.continueLabel != label) {
+                    result = element;
+                    break;
+                }
+            }
+        }
+
+        List<BlockRecorder> blocksToRemove;
+        if (result==null) {
+            // all Blocks do know the label, so use all finally blocks
+            blocksToRemove = (List<BlockRecorder>) Collections.EMPTY_LIST;
+        } else {
+            blocksToRemove = result.finallyBlocks;
+        }
+
+        List<BlockRecorder> blocks = new LinkedList<BlockRecorder>(finallyBlocks);
+        blocks.removeAll(blocksToRemove);
+        applyBlockRecorder(blocks);
+    }
+
+
+    private void applyBlockRecorder(List<BlockRecorder> blocks) {
+        if (blocks.isEmpty() || blocks.size() == visitedBlocks.size()) return;
+
+        MethodVisitor mv = controller.getMethodVisitor();
+        Label newStart = new Label();
+
+        for (BlockRecorder fb : blocks) {
+            if (visitedBlocks.contains(fb)) continue;
+
+            Label end = new Label();
+            mv.visitInsn(NOP);
+            mv.visitLabel(end);
+
+            fb.closeRange(end);
+
+            // we exclude the finally block from the exception table
+            // here to avoid double visiting of finally statements
+            fb.excludedStatement.run();
+
+            fb.startRange(newStart);
+        }
+
+        mv.visitInsn(NOP);
+        mv.visitLabel(newStart);
+    }
+
+    public void applyBlockRecorder() {
+        applyBlockRecorder(finallyBlocks);
+    }
+
+    public boolean hasBlockRecorder() {
+        return !finallyBlocks.isEmpty();
+    }
+
+    public void pushBlockRecorder(BlockRecorder recorder) {
+        pushState();
+        finallyBlocks.addFirst(recorder);
+    }
+
+    public void pushBlockRecorderVisit(BlockRecorder finallyBlock) {
+        visitedBlocks.add(finallyBlock);
+    }
+
+    public void popBlockRecorderVisit(BlockRecorder finallyBlock) {
+        visitedBlocks.remove(finallyBlock);
+    }
+
+    public void writeExceptionTable(BlockRecorder block, Label goal, String sig) {
+        if (block.isEmpty) return;
+        MethodVisitor mv = controller.getMethodVisitor();
+        for (LabelRange range : block.ranges) {
+            mv.visitTryCatchBlock(range.start, range.end, goal, sig);
+        }
+    }
+
+//    public MethodVisitor getMethodVisitor() {
+//        return mv;
+//    }
+
+    public boolean isLHS() {
+        return lhs;
+    }
+
+    public void pushLHS(boolean lhs) {
+        lhsStack.add(lhs);
+        this.lhs = lhs;
+    }
+
+    public void popLHS() {
+        lhsStack.removeLast();
+        this.lhs = lhsStack.getLast();
+    }
+
+    public void pushImplicitThis(boolean implicitThis) {
+        implicitThisStack.add(implicitThis);
+        this.implicitThis = implicitThis;
+    }
+
+    public boolean isImplicitThis() {
+        return implicitThis;
+    }
+
+    public void popImplicitThis() {
+        implicitThisStack.removeLast();
+        this.implicitThis = implicitThisStack.getLast();
+    }
+
+    public boolean isInSpecialConstructorCall() {
+        return inSpecialConstructorCall;
+    }
+
+    public void pushInSpecialConstructorCall() {
+        pushState();
+        inSpecialConstructorCall = true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java b/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java
new file mode 100644
index 0000000..22acbaa
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/DelegatingController.java
@@ -0,0 +1,276 @@
+/*
+ *  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;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ConstructorNode;
+import org.codehaus.groovy.ast.InterfaceHelperClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.control.SourceUnit;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * This class will delegate all calls to a WriterController given in the constructor. 
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class DelegatingController extends WriterController {
+    private final WriterController delegationController;
+    
+    public DelegatingController(WriterController normalController) {
+        this.delegationController = normalController;
+    }
+    
+    @Override
+    public void init(final AsmClassGenerator asmClassGenerator, final GeneratorContext gcon, final ClassVisitor cv, final ClassNode cn) {
+        delegationController.init(asmClassGenerator, gcon, cv, cn);
+    }
+
+    @Override
+    public void setMethodNode(final MethodNode mn) {
+        delegationController.setMethodNode(mn);
+    }
+
+    @Override
+    public void setConstructorNode(final ConstructorNode cn) {
+        delegationController.setConstructorNode(cn);
+    }
+    
+    @Override
+    public boolean isFastPath() {
+        return delegationController.isFastPath();
+    }
+    
+    @Override
+    public CallSiteWriter getCallSiteWriter() {
+        return delegationController.getCallSiteWriter();
+    }
+        
+    @Override
+    public StatementWriter getStatementWriter() {
+        return delegationController.getStatementWriter();            
+    }
+    
+    @Override
+    public TypeChooser getTypeChooser() {
+        return delegationController.getTypeChooser();
+    }
+    
+    @Override
+    public AsmClassGenerator getAcg() {
+        return delegationController.getAcg();
+    }
+    
+    @Override
+    public AssertionWriter getAssertionWriter() {
+        return delegationController.getAssertionWriter();
+    }
+    
+    @Override
+    public BinaryExpressionHelper getBinaryExpressionHelper() {
+        return delegationController.getBinaryExpressionHelper();
+    }
+
+    @Override
+    public UnaryExpressionHelper getUnaryExpressionHelper() {
+        return delegationController.getUnaryExpressionHelper();
+    }
+
+    @Override
+    public String getClassName() {
+        return delegationController.getClassName();
+    }
+    
+    @Override
+    public ClassNode getClassNode() {
+        return delegationController.getClassNode();
+    }
+    
+    @Override
+    public ClassVisitor getClassVisitor() {
+        return delegationController.getClassVisitor();
+    }
+    
+    @Override
+    public ClosureWriter getClosureWriter() {
+        return delegationController.getClosureWriter();
+    } 
+    
+    @Override
+    public CompileStack getCompileStack() {
+        return delegationController.getCompileStack();
+    }
+    
+    @Override
+    public ConstructorNode getConstructorNode() {
+        return delegationController.getConstructorNode();
+    }
+    
+    @Override
+    public GeneratorContext getContext() {
+        return delegationController.getContext();
+    }
+    
+    @Override
+    public ClassVisitor getCv() {
+        return delegationController.getCv();
+    }
+    
+    @Override
+    public InterfaceHelperClassNode getInterfaceClassLoadingClass() {
+        return delegationController.getInterfaceClassLoadingClass();
+    }
+    
+    @Override
+    public String getInternalBaseClassName() {
+        return delegationController.getInternalBaseClassName();
+    }
+    
+    @Override
+    public String getInternalClassName() {
+        return delegationController.getInternalClassName();
+    }
+    
+    @Override
+    public InvocationWriter getInvocationWriter() {
+        return delegationController.getInvocationWriter();
+    }
+    
+    @Override
+    public MethodNode getMethodNode() {
+        return delegationController.getMethodNode();
+    }
+    
+    @Override
+    public MethodVisitor getMethodVisitor() {
+        return delegationController.getMethodVisitor();
+    }
+    
+    @Override
+    public OperandStack getOperandStack() {
+        return delegationController.getOperandStack();
+    }
+    
+    @Override
+    public ClassNode getOutermostClass() {
+        return delegationController.getOutermostClass();
+    }
+    
+    @Override
+    public ClassNode getReturnType() {
+        return delegationController.getReturnType();
+    }
+    
+    @Override
+    public SourceUnit getSourceUnit() {
+        return delegationController.getSourceUnit();
+    }
+
+    @Override
+    public boolean isConstructor() {
+        return delegationController.isConstructor();
+    }
+    
+    @Override
+    public boolean isInClosure() {
+        return delegationController.isInClosure();
+    }
+    
+    @Override
+    public boolean isInClosureConstructor() {
+        return delegationController.isInClosureConstructor();
+    }
+    
+    @Override
+    public boolean isNotClinit() {
+        return delegationController.isNotClinit();
+    }
+    
+    @Override
+    public boolean isInScriptBody() {
+        return delegationController.isInScriptBody();
+    }
+    
+    @Override
+    public boolean isNotExplicitThisInClosure(boolean implicitThis) {
+        return delegationController.isNotExplicitThisInClosure(implicitThis);
+    }
+    
+    @Override
+    public boolean isStaticConstructor() {
+        return delegationController.isStaticConstructor();
+    }
+    
+    @Override
+    public boolean isStaticContext() {
+        return delegationController.isStaticContext();
+    }
+    
+    @Override
+    public boolean isStaticMethod() {
+        return delegationController.isStaticMethod();
+    }
+    
+    @Override
+    public void setInterfaceClassLoadingClass(InterfaceHelperClassNode ihc) {
+        delegationController.setInterfaceClassLoadingClass(ihc);
+    }
+    
+    @Override
+    public void setMethodVisitor(MethodVisitor methodVisitor) {
+        delegationController.setMethodVisitor(methodVisitor);
+    }
+    
+    @Override
+    public boolean shouldOptimizeForInt() {
+        return delegationController.shouldOptimizeForInt();
+    }
+    
+    @Override
+    public void switchToFastPath() {
+        delegationController.switchToFastPath();
+    }
+    
+    @Override
+    public void switchToSlowPath() {
+        delegationController.switchToSlowPath();
+    }
+    
+    @Override
+    public int getBytecodeVersion() {
+        return delegationController.getBytecodeVersion();
+    }
+    
+    @Override
+    public void setLineNumber(int n) {
+        delegationController.setLineNumber(n);
+    }
+    
+    @Override
+    public int getLineNumber() {
+        return delegationController.getLineNumber();
+    }
+    
+    @Override
+    public void resetLineNumber() {
+        delegationController.resetLineNumber();
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java b/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java
new file mode 100644
index 0000000..a9e568b
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/ExpressionAsVariableSlot.java
@@ -0,0 +1,81 @@
+/*
+ *  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;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.classgen.BytecodeExpression;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * Helper class that takes an Expression and if visited will load it normally, 
+ * storing the result in a helper variable, which then can be requested after
+ * the visit is completed. A copy of the variable will stay on the stack. 
+ * Subsequent visits will load the stored value instead of visiting the 
+ * expression again
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class ExpressionAsVariableSlot extends BytecodeExpression {
+    private int index = -1;
+    private final Expression exp;
+    private final WriterController controller;
+    private final String name;
+
+    public ExpressionAsVariableSlot(WriterController controller, Expression expression, String name) {
+        this.exp = expression;
+        this.controller = controller;
+        this.name = name;
+    }
+    
+    public ExpressionAsVariableSlot(WriterController controller, Expression expression) {
+        this(controller, expression, "ExpressionAsVariableSlot_TEMP");
+    }
+
+    @Override
+    public void visit(MethodVisitor mv) {
+        OperandStack os = controller.getOperandStack();
+        if (index == -1) { // first visit
+            // visit expression
+            exp.visit(controller.getAcg());
+            // make copy & set type
+            os.dup();
+            this.setType(os.getTopOperand());
+            // store copy in temporary variable
+            CompileStack compileStack = controller.getCompileStack();
+            index = compileStack.defineTemporaryVariable(name, getType(), true);
+        } else {
+            os.load(getType(), index);
+        }
+        // since the calling code will push the type again, we better remove it here
+        os.remove(1);
+    }
+
+    /**
+     * returns the index of the bytecode variable
+     */
+    public int getIndex() {
+        if (index == -1) throw new GroovyBugError("index requested before visit!");
+        return index;
+    }
+
+    @Override
+    public String getText() {
+        return exp.getText();
+    }
+}