http://git-wip-us.apache.org/repos/asf/groovy/blob/a188738d/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/a188738d/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/a188738d/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:[email protected]";>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/a188738d/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:[email protected]";>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();
+    }
+}

Reply via email to