View Javadoc

    1   /*
    2    * $Id: AsmClassGenerator.java,v 1.8 2004/12/14 16:18:14 russel Exp $
    3    *
    4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5    *
    6    * Redistribution and use of this software and associated documentation
    7    * ("Software"), with or without modification, are permitted provided that the
    8    * following conditions are met: 1. Redistributions of source code must retain
    9    * copyright statements and notices. Redistributions must also contain a copy
    10   * of this document. 2. Redistributions in binary form must reproduce the above
    11   * copyright notice, this list of conditions and the following disclaimer in
    12   * the documentation and/or other materials provided with the distribution. 3.
    13   * The name "groovy" must not be used to endorse or promote products derived
    14   * from this Software without prior written permission of The Codehaus. For
    15   * written permission, please contact info@codehaus.org. 4. Products derived
    16   * from this Software may not be called "groovy" nor may "groovy" appear in
    17   * their names without prior written permission of The Codehaus. "groovy" is a
    18   * registered trademark of The Codehaus. 5. Due credit should be given to The
    19   * Codehaus - http://groovy.codehaus.org/
    20   *
    21   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
    22   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    23   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    24   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
    25   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    27   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    28   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    31   * DAMAGE.
    32   *
    33   */
    34  package org.codehaus.groovy.classgen;
    35
    36  import groovy.lang.Closure;
    37  import groovy.lang.GString;
    38  import groovy.lang.GroovyRuntimeException;
    39  import groovy.lang.MissingClassException;
    40  import groovy.lang.Reference;
    41
    42  import java.math.BigDecimal;
    43  import java.math.BigInteger;
    44  import java.security.AccessControlException;
    45  import java.util.ArrayList;
    46  import java.util.HashMap;
    47  import java.util.HashSet;
    48  import java.util.Iterator;
    49  import java.util.LinkedList;
    50  import java.util.List;
    51  import java.util.Map;
    52  import java.util.Set;
    53  import java.util.logging.Logger;
    54
    55  import org.codehaus.groovy.ast.ASTNode;
    56  import org.codehaus.groovy.ast.ClassNode;
    57  import org.codehaus.groovy.ast.CodeVisitorSupport;
    58  import org.codehaus.groovy.ast.CompileUnit;
    59  import org.codehaus.groovy.ast.ConstructorNode;
    60  import org.codehaus.groovy.ast.FieldNode;
    61  import org.codehaus.groovy.ast.GroovyClassVisitor;
    62  import org.codehaus.groovy.ast.GroovyCodeVisitor;
    63  import org.codehaus.groovy.ast.InnerClassNode;
    64  import org.codehaus.groovy.ast.MethodNode;
    65  import org.codehaus.groovy.ast.Parameter;
    66  import org.codehaus.groovy.ast.PropertyNode;
    67  import org.codehaus.groovy.ast.Type;
    68  import org.codehaus.groovy.ast.VariableScope;
    69  import org.codehaus.groovy.ast.expr.ArgumentListExpression;
    70  import org.codehaus.groovy.ast.expr.ArrayExpression;
    71  import org.codehaus.groovy.ast.expr.BinaryExpression;
    72  import org.codehaus.groovy.ast.expr.BooleanExpression;
    73  import org.codehaus.groovy.ast.expr.CastExpression;
    74  import org.codehaus.groovy.ast.expr.ClassExpression;
    75  import org.codehaus.groovy.ast.expr.ClosureExpression;
    76  import org.codehaus.groovy.ast.expr.ConstantExpression;
    77  import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
    78  import org.codehaus.groovy.ast.expr.Expression;
    79  import org.codehaus.groovy.ast.expr.ExpressionTransformer;
    80  import org.codehaus.groovy.ast.expr.FieldExpression;
    81  import org.codehaus.groovy.ast.expr.GStringExpression;
    82  import org.codehaus.groovy.ast.expr.ListExpression;
    83  import org.codehaus.groovy.ast.expr.MapEntryExpression;
    84  import org.codehaus.groovy.ast.expr.MapExpression;
    85  import org.codehaus.groovy.ast.expr.MethodCallExpression;
    86  import org.codehaus.groovy.ast.expr.NegationExpression;
    87  import org.codehaus.groovy.ast.expr.NotExpression;
    88  import org.codehaus.groovy.ast.expr.PostfixExpression;
    89  import org.codehaus.groovy.ast.expr.PrefixExpression;
    90  import org.codehaus.groovy.ast.expr.PropertyExpression;
    91  import org.codehaus.groovy.ast.expr.RangeExpression;
    92  import org.codehaus.groovy.ast.expr.RegexExpression;
    93  import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
    94  import org.codehaus.groovy.ast.expr.TernaryExpression;
    95  import org.codehaus.groovy.ast.expr.TupleExpression;
    96  import org.codehaus.groovy.ast.expr.VariableExpression;
    97  import org.codehaus.groovy.ast.stmt.AssertStatement;
    98  import org.codehaus.groovy.ast.stmt.BlockStatement;
    99  import org.codehaus.groovy.ast.stmt.BreakStatement;
    100 import org.codehaus.groovy.ast.stmt.CaseStatement;
    101 import org.codehaus.groovy.ast.stmt.CatchStatement;
    102 import org.codehaus.groovy.ast.stmt.ContinueStatement;
    103 import org.codehaus.groovy.ast.stmt.DoWhileStatement;
    104 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
    105 import org.codehaus.groovy.ast.stmt.ForStatement;
    106 import org.codehaus.groovy.ast.stmt.IfStatement;
    107 import org.codehaus.groovy.ast.stmt.ReturnStatement;
    108 import org.codehaus.groovy.ast.stmt.Statement;
    109 import org.codehaus.groovy.ast.stmt.SwitchStatement;
    110 import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
    111 import org.codehaus.groovy.ast.stmt.ThrowStatement;
    112 import org.codehaus.groovy.ast.stmt.TryCatchStatement;
    113 import org.codehaus.groovy.ast.stmt.WhileStatement;
    114 import org.codehaus.groovy.runtime.InvokerHelper;
    115 import org.codehaus.groovy.syntax.Token;
    116 import org.codehaus.groovy.syntax.Types;
    117 import org.codehaus.groovy.syntax.parser.RuntimeParserException;
    118 import org.objectweb.asm.ClassVisitor;
    119 import org.objectweb.asm.CodeVisitor;
    120 import org.objectweb.asm.Constants;
    121 import org.objectweb.asm.Label;
    122
    123 /***
    124  * Generates Java class versions of Groovy classes using ASM
    125  *
    126  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    127  * @version $Revision: 1.8 $
    128  * @deprecated  AsmClassGenerator2 contains code for static method bindings.
    129  */
    130 public class AsmClassGenerator extends ClassGenerator {
    131
    132     private Logger log = Logger.getLogger(getClass().getName());
    133
    134     private ClassVisitor cw;
    135     private CodeVisitor cv;
    136     private GeneratorContext context;
    137
    138     private String sourceFile;
    139
    140     // current class details
    141     private ClassNode classNode;
    142     private ClassNode outermostClass;
    143     private String internalClassName;
    144     private String internalBaseClassName;
    145
    146     /*** maps the variable names to the JVM indices */
    147     private Map variableStack = new HashMap();
    148
    149     /*** have we output a return statement yet */
    150     private boolean outputReturn;
    151
    152     /*** are we on the left or right of an expression */
    153     private boolean leftHandExpression;
    154
    155     // cached values
    156     MethodCaller invokeMethodMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeMethod");
    157     MethodCaller invokeMethodSafeMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeMethodSafe");
    158     MethodCaller invokeStaticMethodMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeStaticMethod");
    159     MethodCaller invokeConstructorMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeConstructor");
    160     MethodCaller invokeConstructorOfMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeConstructorOf");
    161     MethodCaller invokeNoArgumentsConstructorOf = MethodCaller.newStatic(InvokerHelper.class, "invokeNoArgumentsConstructorOf");
    162     MethodCaller invokeClosureMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeClosure");
    163     MethodCaller invokeSuperMethodMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeSuperMethod");
    164     MethodCaller invokeNoArgumentsMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeNoArgumentsMethod");
    165     MethodCaller invokeStaticNoArgumentsMethod =
    166         MethodCaller.newStatic(InvokerHelper.class, "invokeStaticNoArgumentsMethod");
    167
    168     MethodCaller asIntMethod = MethodCaller.newStatic(InvokerHelper.class, "asInt");
    169     MethodCaller asTypeMethod = MethodCaller.newStatic(InvokerHelper.class, "asType");
    170     MethodCaller getPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "getProperty");
    171     MethodCaller getPropertySafeMethod = MethodCaller.newStatic(InvokerHelper.class, "getPropertySafe");
    172     MethodCaller setPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "setProperty");
    173     MethodCaller setPropertyMethod2 = MethodCaller.newStatic(InvokerHelper.class, "setProperty2");
    174     MethodCaller setPropertySafeMethod2 = MethodCaller.newStatic(InvokerHelper.class, "setPropertySafe2");
    175     MethodCaller getGroovyObjectPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "getGroovyObjectProperty");
    176     MethodCaller setGroovyObjectPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "setGroovyObjectProperty");
    177     MethodCaller asIteratorMethod = MethodCaller.newStatic(InvokerHelper.class, "asIterator");
    178     MethodCaller asBool = MethodCaller.newStatic(InvokerHelper.class, "asBool");
    179     MethodCaller notBoolean = MethodCaller.newStatic(InvokerHelper.class, "notBoolean");
    180     MethodCaller notObject = MethodCaller.newStatic(InvokerHelper.class, "notObject");
    181     MethodCaller regexPattern = MethodCaller.newStatic(InvokerHelper.class, "regexPattern");
    182     MethodCaller negation = MethodCaller.newStatic(InvokerHelper.class, "negate");
    183
    184     MethodCaller compareIdenticalMethod = MethodCaller.newStatic(InvokerHelper.class, "compareIdentical");
    185     MethodCaller compareEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareEqual");
    186     MethodCaller compareNotEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareNotEqual");
    187     MethodCaller compareToMethod = MethodCaller.newStatic(InvokerHelper.class, "compareTo");
    188     MethodCaller findRegexMethod = MethodCaller.newStatic(InvokerHelper.class, "findRegex");
    189     MethodCaller matchRegexMethod = MethodCaller.newStatic(InvokerHelper.class, "matchRegex");
    190     MethodCaller compareLessThanMethod = MethodCaller.newStatic(InvokerHelper.class, "compareLessThan");
    191     MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareLessThanEqual");
    192     MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(InvokerHelper.class, "compareGreaterThan");
    193     MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareGreaterThanEqual");
    194     MethodCaller isCaseMethod = MethodCaller.newStatic(InvokerHelper.class, "isCase");
    195
    196     MethodCaller createListMethod = MethodCaller.newStatic(InvokerHelper.class, "createList");
    197     MethodCaller createTupleMethod = MethodCaller.newStatic(InvokerHelper.class, "createTuple");
    198     MethodCaller createMapMethod = MethodCaller.newStatic(InvokerHelper.class, "createMap");
    199     MethodCaller createRangeMethod = MethodCaller.newStatic(InvokerHelper.class, "createRange");
    200
    201     MethodCaller assertFailedMethod = MethodCaller.newStatic(InvokerHelper.class, "assertFailed");
    202
    203     MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
    204     MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
    205
    206     // current stack index
    207     private int lastVariableIndex;
    208     private static int tempVariableNameCounter;
    209
    210     // exception blocks list
    211     private List exceptionBlocks = new ArrayList();
    212
    213     private boolean definingParameters;
    214     private Set syntheticStaticFields = new HashSet();
    215     private Set mutableVars = new HashSet();
    216     private boolean passingClosureParams;
    217
    218     private ConstructorNode constructorNode;
    219     private MethodNode methodNode;
    220     //private PropertyNode propertyNode;
    221     private BlockScope scope;
    222     private BytecodeHelper helper = new BytecodeHelper(null);
    223
    224     private VariableScope variableScope;
    225
    226     public AsmClassGenerator(
    227         GeneratorContext context,
    228         ClassVisitor classVisitor,
    229         ClassLoader classLoader,
    230         String sourceFile) {
    231         super(classLoader);
    232         this.context = context;
    233         this.cw = classVisitor;
    234         this.sourceFile = sourceFile;
    235     }
    236
    237     // GroovyClassVisitor interface
    238     //-------------------------------------------------------------------------
    239     public void visitClass(ClassNode classNode) {
    240         try {
    241             syntheticStaticFields.clear();
    242             this.classNode = classNode;
    243             this.outermostClass = null;
    244             this.internalClassName = BytecodeHelper.getClassInternalName(classNode.getName());
    245
    246             //System.out.println("Generating class: " + classNode.getName());
    247
    248             // lets check that the classes are all valid
    249             classNode.setSuperClass(checkValidType(classNode.getSuperClass(), classNode, "Must be a valid base class"));
    250             String[] interfaces = classNode.getInterfaces();
    251             for (int i = 0; i < interfaces.length; i++ ) {
    252                 interfaces[i] = checkValidType(interfaces[i], classNode, "Must be a valid interface name");
    253             }
    254
    255             this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
    256
    257             cw.visit(
    258                 asmJDKVersion,
    259                 classNode.getModifiers(),
    260                 internalClassName,
    261                 internalBaseClassName,
    262                 BytecodeHelper.getClassInternalNames(classNode.getInterfaces()),
    263                 sourceFile);
    264
    265             // set the optional enclosing method attribute of the current inner class
    266 //          br comment out once Groovy uses the latest CVS HEAD of ASM
    267 //            MethodNode enclosingMethod = classNode.getEnclosingMethod();
    268 //            String ownerName = BytecodeHelper.getClassInternalName(enclosingMethod.getDeclaringClass().getName());
    269 //            String descriptor = BytecodeHelper.getMethodDescriptor(enclosingMethod.getReturnType(), enclosingMethod.getParameters());
    270 //            EnclosingMethodAttribute attr = new EnclosingMethodAttribute(ownerName,enclosingMethod.getName(),descriptor);
    271 //            cw.visitAttribute(attr);
    272
    273             classNode.visitContents(this);
    274
    275             createSyntheticStaticFields();
    276
    277             for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
    278                 ClassNode innerClass = (ClassNode) iter.next();
    279                 String innerClassName = innerClass.getName();
    280                 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
    281                 String outerClassName = internalClassName; // default for inner classes
    282                 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
    283                 if (enclosingMethod != null) {
    284                     // local inner classes do not specify the outer class name
    285                     outerClassName = null;
    286                 }
    287                 cw.visitInnerClass(
    288                     innerClassInternalName,
    289                     outerClassName,
    290                     innerClassName,
    291                     innerClass.getModifiers());
    292             }
    293 // br TODO an inner class should have an entry of itself
    294             cw.visitEnd();
    295         }
    296         catch (GroovyRuntimeException e) {
    297             e.setModule(classNode.getModule());
    298             throw e;
    299         }
    300     }
    301
    302     public void visitConstructor(ConstructorNode node) {
    303         // creates a MethodWriter for the (implicit) constructor
    304         //String methodType = Type.getMethodDescriptor(VOID_TYPE, )
    305
    306         this.constructorNode = node;
    307         this.methodNode = null;
    308         this.variableScope = null;
    309
    310         visitParameters(node, node.getParameters());
    311
    312         String methodType = BytecodeHelper.getMethodDescriptor("void", node.getParameters());
    313         cv = cw.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
    314         helper = new BytecodeHelper(cv);
    315
    316         findMutableVariables();
    317         resetVariableStack(node.getParameters());
    318
    319         Statement code = node.getCode();
    320         if (code == null || !firstStatementIsSuperInit(code)) {
    321             // invokes the super class constructor
    322             cv.visitVarInsn(ALOAD, 0);
    323             cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "()V");
    324         }
    325         if (code != null) {
    326             code.visit(this);
    327         }
    328
    329         cv.visitInsn(RETURN);
    330         cv.visitMaxs(0, 0);
    331     }
    332
    333     public void visitMethod(MethodNode node) {
    334         //System.out.println("Visiting method: " + node.getName() + " with
    335         // return type: " + node.getReturnType());
    336         this.constructorNode = null;
    337         this.methodNode = node;
    338         this.variableScope = null;
    339
    340         visitParameters(node, node.getParameters());
    341         node.setReturnType(checkValidType(node.getReturnType(), node, "Must be a valid return type"));
    342
    343         String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
    344         cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
    345         Label labelStart = new Label();
    346         cv.visitLabel(labelStart);
    347         helper = new BytecodeHelper(cv);
    348
    349         findMutableVariables();
    350         resetVariableStack(node.getParameters());
    351
    352         outputReturn = false;
    353
    354         node.getCode().visit(this);
    355
    356         if (!outputReturn) {
    357             cv.visitInsn(RETURN);
    358         }
    359
    360         // lets do all the exception blocks
    361         for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) {
    362             Runnable runnable = (Runnable) iter.next();
    363             runnable.run();
    364         }
    365         exceptionBlocks.clear();
    366
    367         Label labelEnd = new Label();
    368         cv.visitLabel(labelEnd);
    369
    370         // br experiment with local var table so debugers can retrieve variable names
    371 //        Set vars = this.variableStack.keySet();
    372 //        for (Iterator iterator = vars.iterator(); iterator.hasNext();) {
    373 //            String varName = (String) iterator.next();
    374 //            Variable v = (Variable)variableStack.get(varName);
    375 //            String type = v.getTypeName();
    376 //            type = BytecodeHelper.getTypeDescription(type);
    377 //            cv.visitLocalVariable(varName, type, labelStart, labelEnd, v.getIndex()); // the start and end should be fine-pined
    378 //        }
    379
    380         cv.visitMaxs(0, 0);
    381     }
    382
    383     protected void visitParameters(ASTNode node, Parameter[] parameters) {
    384         for (int i = 0, size = parameters.length; i < size; i++ ) {
    385             visitParameter(node, parameters[i]);
    386         }
    387     }
    388
    389     protected void visitParameter(ASTNode node, Parameter parameter) {
    390         if (! parameter.isDynamicType()) {
    391             parameter.setType(checkValidType(parameter.getType(), node, "Must be a valid parameter class"));
    392         }
    393     }
    394
    395     public void visitField(FieldNode fieldNode) {
    396         onLineNumber(fieldNode);
    397
    398         // lets check that the classes are all valid
    399         fieldNode.setType(checkValidType(fieldNode.getType(), fieldNode, "Must be a valid field class for field: " + fieldNode.getName()));
    400
    401         //System.out.println("Visiting field: " + fieldNode.getName() + " on
    402         // class: " + classNode.getName());
    403
    404         Object fieldValue = null;
    405         Expression expression = fieldNode.getInitialValueExpression();
    406         if (expression instanceof ConstantExpression) {
    407             ConstantExpression constantExp = (ConstantExpression) expression;
    408             Object value = constantExp.getValue();
    409             if (isPrimitiveFieldType(fieldNode.getType())) {
    410                 // lets convert any primitive types
    411                 Class type = null;
    412                 try {
    413                     type = loadClass(fieldNode.getType());
    414                     fieldValue = InvokerHelper.asType(value, type);
    415                 }
    416                 catch (Exception e) {
    417                     log.warning("Caught unexpected: " + e);
    418                 }
    419             }
    420         }
    421         cw.visitField(
    422             fieldNode.getModifiers(),
    423             fieldNode.getName(),
    424             BytecodeHelper.getTypeDescription(fieldNode.getType()),
    425             fieldValue,
    426             null);
    427     }
    428
    429     /***
    430      * Creates a getter, setter and field
    431      */
    432     public void visitProperty(PropertyNode statement) {
    433         onLineNumber(statement);
    434         //this.propertyNode = statement;
    435         this.methodNode = null;
    436     }
    437
    438     // GroovyCodeVisitor interface
    439     //-------------------------------------------------------------------------
    440
    441     // Statements
    442     //-------------------------------------------------------------------------
    443
    444     public void visitForLoop(ForStatement loop) {
    445         onLineNumber(loop);
    446
    447
    448         //
    449         // Declare the loop counter.
    450
    451         Type variableType = checkValidType(loop.getVariableType(), loop, "for loop variable");
    452         Variable variable = defineVariable(loop.getVariable(), variableType, true);
    453
    454         if( isInScriptBody() ) {
    455             variable.setProperty( true );
    456         }
    457
    458
    459         //
    460         // Then initialize the iterator and generate the loop control
    461
    462         loop.getCollectionExpression().visit(this);
    463
    464         asIteratorMethod.call(cv);
    465
    466         final int iteratorIdx = defineVariable(createVariableName("iterator"), "java.util.Iterator", false).getIndex();
    467         cv.visitVarInsn(ASTORE, iteratorIdx);
    468
    469         pushBlockScope();
    470
    471         Label continueLabel = scope.getContinueLabel();
    472         cv.visitJumpInsn(GOTO, continueLabel);
    473         Label label2 = new Label();
    474         cv.visitLabel(label2);
    475
    476         BytecodeExpression expression = new BytecodeExpression() {
    477             public void visit(GroovyCodeVisitor visitor) {
    478                 cv.visitVarInsn(ALOAD, iteratorIdx);
    479
    480                 iteratorNextMethod.call(cv);
    481             }
    482         };
    483
    484         evaluateEqual( BinaryExpression.newAssignmentExpression(loop.getVariable(), expression) );
    485
    486
    487         //
    488         // Generate the loop body
    489
    490         loop.getLoopBlock().visit(this);
    491
    492
    493         //
    494         // Generate the loop tail
    495
    496         cv.visitLabel(continueLabel);
    497         cv.visitVarInsn(ALOAD, iteratorIdx);
    498
    499         iteratorHasNextMethod.call(cv);
    500
    501         cv.visitJumpInsn(IFNE, label2);
    502
    503         cv.visitLabel(scope.getBreakLabel());
    504         popScope();
    505     }
    506
    507     public void visitWhileLoop(WhileStatement loop) {
    508         onLineNumber(loop);
    509
    510         /*
    511          * // quick hack if (!methodNode.isStatic()) { cv.visitVarInsn(ALOAD,
    512          * 0); }
    513          */
    514
    515         pushBlockScope();
    516
    517         Label continueLabel = scope.getContinueLabel();
    518
    519         cv.visitJumpInsn(GOTO, continueLabel);
    520         Label l1 = new Label();
    521         cv.visitLabel(l1);
    522
    523         loop.getLoopBlock().visit(this);
    524
    525         cv.visitLabel(continueLabel);
    526         //cv.visitVarInsn(ALOAD, 0);
    527
    528         loop.getBooleanExpression().visit(this);
    529
    530         cv.visitJumpInsn(IFNE, l1);
    531
    532         cv.visitLabel(scope.getBreakLabel());
    533         popScope();
    534     }
    535
    536     public void visitDoWhileLoop(DoWhileStatement loop) {
    537         onLineNumber(loop);
    538
    539         pushBlockScope();
    540
    541         Label breakLabel = scope.getBreakLabel();
    542
    543         Label continueLabel = scope.getContinueLabel();
    544         cv.visitLabel(continueLabel);
    545         Label l1 = new Label();
    546
    547         loop.getLoopBlock().visit(this);
    548
    549         cv.visitLabel(l1);
    550
    551         loop.getBooleanExpression().visit(this);
    552
    553         cv.visitJumpInsn(IFNE, continueLabel);
    554
    555         cv.visitLabel(breakLabel);
    556         popScope();
    557     }
    558
    559     public void visitIfElse(IfStatement ifElse) {
    560         onLineNumber(ifElse);
    561
    562         ifElse.getBooleanExpression().visit(this);
    563
    564         Label l0 = new Label();
    565         cv.visitJumpInsn(IFEQ, l0);
    566         ifElse.getIfBlock().visit(this);
    567
    568         Label l1 = new Label();
    569         cv.visitJumpInsn(GOTO, l1);
    570         cv.visitLabel(l0);
    571
    572         ifElse.getElseBlock().visit(this);
    573         cv.visitLabel(l1);
    574     }
    575
    576     public void visitTernaryExpression(TernaryExpression expression) {
    577         onLineNumber(expression);
    578
    579         expression.getBooleanExpression().visit(this);
    580
    581         Label l0 = new Label();
    582         cv.visitJumpInsn(IFEQ, l0);
    583         expression.getTrueExpression().visit(this);
    584
    585         Label l1 = new Label();
    586         cv.visitJumpInsn(GOTO, l1);
    587         cv.visitLabel(l0);
    588
    589         expression.getFalseExpression().visit(this);
    590         cv.visitLabel(l1);
    591     }
    592
    593     public void visitAssertStatement(AssertStatement statement) {
    594         onLineNumber(statement);
    595
    596         //System.out.println("Assert: " + statement.getLineNumber() + " for: "
    597         // + statement.getText());
    598
    599         BooleanExpression booleanExpression = statement.getBooleanExpression();
    600         booleanExpression.visit(this);
    601
    602         Label l0 = new Label();
    603         cv.visitJumpInsn(IFEQ, l0);
    604
    605         // do nothing
    606
    607         Label l1 = new Label();
    608         cv.visitJumpInsn(GOTO, l1);
    609         cv.visitLabel(l0);
    610
    611         // push expression string onto stack
    612         String expressionText = booleanExpression.getText();
    613         List list = new ArrayList();
    614         addVariableNames(booleanExpression, list);
    615         if (list.isEmpty()) {
    616             cv.visitLdcInsn(expressionText);
    617         }
    618         else {
    619             boolean first = true;
    620
    621             // lets create a new expression
    622             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
    623             cv.visitInsn(DUP);
    624             cv.visitLdcInsn(expressionText + ". Values: ");
    625
    626             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V");
    627
    628             int tempIndex = defineVariable(createVariableName("assert"), "java.lang.Object", false).getIndex();
    629
    630             cv.visitVarInsn(ASTORE, tempIndex);
    631
    632             for (Iterator iter = list.iterator(); iter.hasNext();) {
    633                 String name = (String) iter.next();
    634                 String text = name + " = ";
    635                 if (first) {
    636                     first = false;
    637                 }
    638                 else {
    639                     text = ", " + text;
    640                 }
    641
    642                 cv.visitVarInsn(ALOAD, tempIndex);
    643                 cv.visitLdcInsn(text);
    644                 cv.visitMethodInsn(
    645                     INVOKEVIRTUAL,
    646                     "java/lang/StringBuffer",
    647                     "append",
    648                     "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
    649                 cv.visitInsn(POP);
    650
    651                 cv.visitVarInsn(ALOAD, tempIndex);
    652                 new VariableExpression(name).visit(this);
    653                 cv.visitMethodInsn(
    654                     INVOKEVIRTUAL,
    655                     "java/lang/StringBuffer",
    656                     "append",
    657                     "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
    658                 cv.visitInsn(POP);
    659
    660             }
    661             cv.visitVarInsn(ALOAD, tempIndex);
    662         }
    663
    664         // now the optional exception expression
    665         statement.getMessageExpression().visit(this);
    666
    667         assertFailedMethod.call(cv);
    668         cv.visitLabel(l1);
    669     }
    670
    671     private void addVariableNames(Expression expression, List list) {
    672         if (expression instanceof BooleanExpression) {
    673             BooleanExpression boolExp = (BooleanExpression) expression;
    674             addVariableNames(boolExp.getExpression(), list);
    675         }
    676         else if (expression instanceof BinaryExpression) {
    677             BinaryExpression binExp = (BinaryExpression) expression;
    678             addVariableNames(binExp.getLeftExpression(), list);
    679             addVariableNames(binExp.getRightExpression(), list);
    680         }
    681         else if (expression instanceof VariableExpression) {
    682             VariableExpression varExp = (VariableExpression) expression;
    683             list.add(varExp.getVariable());
    684         }
    685     }
    686
    687     public void visitTryCatchFinally(TryCatchStatement statement) {
    688         onLineNumber(statement);
    689
    690         CatchStatement catchStatement = statement.getCatchStatement(0);
    691
    692         Statement tryStatement = statement.getTryStatement();
    693
    694         if (tryStatement.isEmpty() || catchStatement == null) {
    695             final Label l0 = new Label();
    696             cv.visitLabel(l0);
    697
    698             tryStatement.visit(this);
    699
    700             int index1 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    701             int index2 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    702
    703             final Label l1 = new Label();
    704             cv.visitJumpInsn(JSR, l1);
    705             final Label l2 = new Label();
    706             cv.visitLabel(l2);
    707             final Label l3 = new Label();
    708             cv.visitJumpInsn(GOTO, l3);
    709             final Label l4 = new Label();
    710             cv.visitLabel(l4);
    711             cv.visitVarInsn(ASTORE, index1);
    712             cv.visitJumpInsn(JSR, l1);
    713             final Label l5 = new Label();
    714             cv.visitLabel(l5);
    715             cv.visitVarInsn(ALOAD, index1);
    716             cv.visitInsn(ATHROW);
    717             cv.visitLabel(l1);
    718             cv.visitVarInsn(ASTORE, index2);
    719
    720             statement.getFinallyStatement().visit(this);
    721
    722             cv.visitVarInsn(RET, index2);
    723             cv.visitLabel(l3);
    724
    725             exceptionBlocks.add(new Runnable() {
    726                 public void run() {
    727                     cv.visitTryCatchBlock(l0, l2, l4, null);
    728                     cv.visitTryCatchBlock(l4, l5, l4, null);
    729                 }
    730             });
    731
    732         }
    733         else {
    734             String exceptionVar = catchStatement.getVariable();
    735             String exceptionType =
    736                 checkValidType(catchStatement.getExceptionType(), catchStatement, "in catch statement");
    737
    738             int exceptionIndex = defineVariable(exceptionVar, exceptionType, false).getIndex();
    739             int index2 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    740             int index3 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    741
    742             final Label l0 = new Label();
    743             cv.visitLabel(l0);
    744
    745             tryStatement.visit(this);
    746
    747             final Label l1 = new Label();
    748             cv.visitLabel(l1);
    749             Label l2 = new Label();
    750             cv.visitJumpInsn(JSR, l2);
    751             final Label l3 = new Label();
    752             cv.visitLabel(l3);
    753             Label l4 = new Label();
    754             cv.visitJumpInsn(GOTO, l4);
    755             final Label l5 = new Label();
    756             cv.visitLabel(l5);
    757
    758             cv.visitVarInsn(ASTORE, exceptionIndex);
    759
    760             if (catchStatement != null) {
    761                 catchStatement.visit(this);
    762             }
    763
    764             cv.visitJumpInsn(JSR, l2);
    765             final Label l6 = new Label();
    766             cv.visitLabel(l6);
    767             cv.visitJumpInsn(GOTO, l4);
    768
    769             final Label l7 = new Label();
    770             cv.visitLabel(l7);
    771             cv.visitVarInsn(ASTORE, index2);
    772             cv.visitJumpInsn(JSR, l2);
    773
    774             final Label l8 = new Label();
    775             cv.visitLabel(l8);
    776             cv.visitVarInsn(ALOAD, index2);
    777             cv.visitInsn(ATHROW);
    778             cv.visitLabel(l2);
    779             cv.visitVarInsn(ASTORE, index3);
    780
    781             statement.getFinallyStatement().visit(this);
    782
    783             cv.visitVarInsn(RET, index3);
    784             cv.visitLabel(l4);
    785
    786             // rest of code goes here...
    787
    788             //final String exceptionTypeInternalName = (catchStatement !=
    789             // null) ?
    790             // getTypeDescription(exceptionType) : null;
    791             final String exceptionTypeInternalName =
    792                 (catchStatement != null) ? BytecodeHelper.getClassInternalName(exceptionType) : null;
    793
    794             exceptionBlocks.add(new Runnable() {
    795                 public void run() {
    796                     cv.visitTryCatchBlock(l0, l1, l5, exceptionTypeInternalName);
    797                     cv.visitTryCatchBlock(l0, l3, l7, null);
    798                     cv.visitTryCatchBlock(l5, l6, l7, null);
    799                     cv.visitTryCatchBlock(l7, l8, l7, null);
    800                 }
    801             });
    802         }
    803     }
    804
    805     public void visitSwitch(SwitchStatement statement) {
    806         onLineNumber(statement);
    807
    808         statement.getExpression().visit(this);
    809
    810         // switch does not have a continue label. use its parent's for continue
    811         pushBlockScope(false, true);
    812         scope.setContinueLabel(scope.getParent().getContinueLabel());
    813
    814
    815         int switchVariableIndex = defineVariable(createVariableName("switch"), "java.lang.Object").getIndex();
    816         cv.visitVarInsn(ASTORE, switchVariableIndex);
    817
    818         List caseStatements = statement.getCaseStatements();
    819         int caseCount = caseStatements.size();
    820         Label[] labels = new Label[caseCount + 1];
    821         for (int i = 0; i < caseCount; i++) {
    822             labels[i] = new Label();
    823         }
    824
    825         int i = 0;
    826         for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
    827             CaseStatement caseStatement = (CaseStatement) iter.next();
    828             visitCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]);
    829         }
    830
    831         statement.getDefaultStatement().visit(this);
    832
    833         cv.visitLabel(scope.getBreakLabel());
    834
    835         popScope();
    836     }
    837
    838     public void visitCaseStatement(CaseStatement statement) {
    839     }
    840
    841     public void visitCaseStatement(
    842         CaseStatement statement,
    843         int switchVariableIndex,
    844         Label thisLabel,
    845         Label nextLabel) {
    846
    847         onLineNumber(statement);
    848
    849         cv.visitVarInsn(ALOAD, switchVariableIndex);
    850         statement.getExpression().visit(this);
    851
    852         isCaseMethod.call(cv);
    853
    854         Label l0 = new Label();
    855         cv.visitJumpInsn(IFEQ, l0);
    856
    857         cv.visitLabel(thisLabel);
    858
    859         statement.getCode().visit(this);
    860
    861         // now if we don't finish with a break we need to jump past
    862         // the next comparison
    863         if (nextLabel != null) {
    864             cv.visitJumpInsn(GOTO, nextLabel);
    865         }
    866
    867         cv.visitLabel(l0);
    868     }
    869
    870     public void visitBreakStatement(BreakStatement statement) {
    871         onLineNumber(statement);
    872
    873         cv.visitJumpInsn(GOTO, scope.getBreakLabel());
    874     }
    875
    876     public void visitContinueStatement(ContinueStatement statement) {
    877         onLineNumber(statement);
    878
    879         cv.visitJumpInsn(GOTO, scope.getContinueLabel());
    880     }
    881
    882     public void visitSynchronizedStatement(SynchronizedStatement statement) {
    883         onLineNumber(statement);
    884
    885         statement.getExpression().visit(this);
    886
    887         int index = defineVariable(createVariableName("synchronized"), "java.lang.Integer").getIndex();
    888
    889         cv.visitVarInsn(ASTORE, index);
    890         cv.visitInsn(MONITORENTER);
    891         final Label l0 = new Label();
    892         cv.visitLabel(l0);
    893
    894         statement.getCode().visit(this);
    895
    896         cv.visitVarInsn(ALOAD, index);
    897         cv.visitInsn(MONITOREXIT);
    898         final Label l1 = new Label();
    899         cv.visitJumpInsn(GOTO, l1);
    900         final Label l2 = new Label();
    901         cv.visitLabel(l2);
    902         cv.visitVarInsn(ALOAD, index);
    903         cv.visitInsn(MONITOREXIT);
    904         cv.visitInsn(ATHROW);
    905         cv.visitLabel(l1);
    906
    907         exceptionBlocks.add(new Runnable() {
    908             public void run() {
    909                 cv.visitTryCatchBlock(l0, l2, l2, null);
    910             }
    911         });
    912     }
    913
    914     public void visitThrowStatement(ThrowStatement statement) {
    915         statement.getExpression().visit(this);
    916
    917         // we should infer the type of the exception from the expression
    918         cv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
    919
    920         cv.visitInsn(ATHROW);
    921     }
    922
    923     public void visitReturnStatement(ReturnStatement statement) {
    924         onLineNumber(statement);
    925         String returnType = methodNode.getReturnType();
    926         if (returnType.equals("void")) {
    927         	if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) {
    928                 throw new RuntimeParserException(
    929                         "Cannot use return statement with an expression on a method that returns void",
    930                         statement);
    931
    932         	}
    933             cv.visitInsn(RETURN);
    934             outputReturn = true;
    935             return;
    936         }
    937
    938         Expression expression = statement.getExpression();
    939         evaluateExpression(expression);
    940
    941         //return is based on class type
    942         //TODO: make work with arrays
    943         // we may need to cast
    944         helper.unbox(returnType);
    945         if (returnType.equals("double")) {
    946             cv.visitInsn(DRETURN);
    947         }
    948         else if (returnType.equals("float")) {
    949             cv.visitInsn(FRETURN);
    950         }
    951         else if (returnType.equals("long")) {
    952             cv.visitInsn(LRETURN);
    953         }
    954         else if (returnType.equals("boolean")) {
    955             cv.visitInsn(IRETURN);
    956         }
    957         else if (
    958             returnType.equals("char")
    959                 || returnType.equals("byte")
    960                 || returnType.equals("int")
    961                 || returnType.equals("short")) { //byte,short,boolean,int are
    962             // all IRETURN
    963             cv.visitInsn(IRETURN);
    964         }
    965         else {
    966             doConvertAndCast(returnType, expression);
    967             cv.visitInsn(ARETURN);
    968
    969             /*
    970             if (c == Boolean.class) {
    971                 Label l0 = new Label();
    972                 cv.visitJumpInsn(IFEQ, l0);
    973                 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
    974                 cv.visitInsn(ARETURN);
    975                 cv.visitLabel(l0);
    976                 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
    977                 cv.visitInsn(ARETURN);
    978             }
    979             else {
    980                 if (isValidTypeForCast(returnType) && !returnType.equals(c.getName())) {
    981                     doConvertAndCast(returnType, expression);
    982                 }
    983                 cv.visitInsn(ARETURN);
    984             }
    985             */
    986         }
    987
    988         outputReturn = true;
    989     }
    990
    991     /***
    992      * Casts to the given type unless it can be determined that the cast is unnecessary
    993      */
    994     protected void doConvertAndCast(String type, Expression expression) {
    995         String expType = getExpressionType(expression);
    996
    997         if (isValidTypeForCast(type) && (expType == null || !type.equals(expType))) {
    998             doConvertAndCast(type);
    999         }
    1000     }
    1001
    1002     /***
    1003      * @param expression
    1004      */
    1005     protected void evaluateExpression(Expression expression) {
    1006         visitAndAutobox(expression);
    1007         //expression.visit(this);
    1008
    1009         Expression assignExpr = createReturnLHSExpression(expression);
    1010         if (assignExpr != null) {
    1011             leftHandExpression = false;
    1012             assignExpr.visit(this);
    1013         }
    1014     }
    1015
    1016     public void visitExpressionStatement(ExpressionStatement statement) {
    1017         onLineNumber(statement);
    1018
    1019         Expression expression = statement.getExpression();
    1020         visitAndAutobox(expression);
    1021
    1022         if (isPopRequired(expression)) {
    1023             cv.visitInsn(POP);
    1024         }
    1025     }
    1026
    1027     // Expressions
    1028     //-------------------------------------------------------------------------
    1029
    1030     public void visitBinaryExpression(BinaryExpression expression) {
    1031         switch (expression.getOperation().getType()) {
    1032             case Types.EQUAL :
    1033                 evaluateEqual(expression);
    1034                 break;
    1035
    1036             case Types.COMPARE_IDENTICAL :
    1037                 evaluateBinaryExpression(compareIdenticalMethod, expression);
    1038                 break;
    1039
    1040             case Types.COMPARE_EQUAL :
    1041                 evaluateBinaryExpression(compareEqualMethod, expression);
    1042                 break;
    1043
    1044             case Types.COMPARE_NOT_EQUAL :
    1045                 evaluateBinaryExpression(compareNotEqualMethod, expression);
    1046                 break;
    1047
    1048             case Types.COMPARE_TO :
    1049                 evaluateCompareTo(expression);
    1050                 break;
    1051
    1052             case Types.COMPARE_GREATER_THAN :
    1053                 evaluateBinaryExpression(compareGreaterThanMethod, expression);
    1054                 break;
    1055
    1056             case Types.COMPARE_GREATER_THAN_EQUAL :
    1057                 evaluateBinaryExpression(compareGreaterThanEqualMethod, expression);
    1058                 break;
    1059
    1060             case Types.COMPARE_LESS_THAN :
    1061                 evaluateBinaryExpression(compareLessThanMethod, expression);
    1062                 break;
    1063
    1064             case Types.COMPARE_LESS_THAN_EQUAL :
    1065                 evaluateBinaryExpression(compareLessThanEqualMethod, expression);
    1066                 break;
    1067
    1068             case Types.LOGICAL_AND :
    1069                 evaluateLogicalAndExpression(expression);
    1070                 break;
    1071
    1072             case Types.LOGICAL_OR :
    1073                 evaluateLogicalOrExpression(expression);
    1074                 break;
    1075
    1076             case Types.PLUS :
    1077                 evaluateBinaryExpression("plus", expression);
    1078                 break;
    1079
    1080             case Types.PLUS_EQUAL :
    1081                 evaluateBinaryExpressionWithAsignment("plus", expression);
    1082                 break;
    1083
    1084             case Types.MINUS :
    1085                 evaluateBinaryExpression("minus", expression);
    1086                 break;
    1087
    1088             case Types.MINUS_EQUAL :
    1089                 evaluateBinaryExpressionWithAsignment("minus", expression);
    1090                 break;
    1091
    1092             case Types.MULTIPLY :
    1093                 evaluateBinaryExpression("multiply", expression);
    1094                 break;
    1095
    1096             case Types.MULTIPLY_EQUAL :
    1097                 evaluateBinaryExpressionWithAsignment("multiply", expression);
    1098                 break;
    1099
    1100             case Types.DIVIDE :
    1101                 //SPG don't use divide since BigInteger implements directly
    1102                 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
    1103                 evaluateBinaryExpression("div", expression);
    1104                 break;
    1105
    1106             case Types.DIVIDE_EQUAL :
    1107                 //SPG don't use divide since BigInteger implements directly
    1108                 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
    1109                 evaluateBinaryExpressionWithAsignment("div", expression);
    1110                 break;
    1111
    1112             case Types.INTDIV :
    1113                 evaluateBinaryExpression("intdiv", expression);
    1114                 break;
    1115
    1116             case Types.INTDIV_EQUAL :
    1117                 evaluateBinaryExpressionWithAsignment("intdiv", expression);
    1118                 break;
    1119
    1120             case Types.MOD :
    1121                 evaluateBinaryExpression("mod", expression);
    1122                 break;
    1123
    1124             case Types.MOD_EQUAL :
    1125                 evaluateBinaryExpressionWithAsignment("mod", expression);
    1126                 break;
    1127
    1128             case Types.LEFT_SHIFT :
    1129                 evaluateBinaryExpression("leftShift", expression);
    1130                 break;
    1131
    1132             case Types.RIGHT_SHIFT :
    1133                 evaluateBinaryExpression("rightShift", expression);
    1134                 break;
    1135
    1136             case Types.RIGHT_SHIFT_UNSIGNED :
    1137                 evaluateBinaryExpression("rightShiftUnsigned", expression);
    1138                 break;
    1139
    1140             case Types.KEYWORD_INSTANCEOF :
    1141                 evaluateInstanceof(expression);
    1142                 break;
    1143
    1144             case Types.FIND_REGEX :
    1145                 evaluateBinaryExpression(findRegexMethod, expression);
    1146                 break;
    1147
    1148             case Types.MATCH_REGEX :
    1149                 evaluateBinaryExpression(matchRegexMethod, expression);
    1150                 break;
    1151
    1152             case Types.LEFT_SQUARE_BRACKET :
    1153                 if (leftHandExpression) {
    1154                     throw new RuntimeException("Should not be called");
    1155                     //evaluateBinaryExpression("putAt", expression);
    1156                 }
    1157                 else {
    1158                     evaluateBinaryExpression("getAt", expression);
    1159                 }
    1160                 break;
    1161
    1162             default :
    1163                 throw new ClassGeneratorException("Operation: " + expression.getOperation() + " not supported");
    1164         }
    1165     }
    1166
    1167     public void visitPostfixExpression(PostfixExpression expression) {
    1168         switch (expression.getOperation().getType()) {
    1169             case Types.PLUS_PLUS :
    1170                 evaluatePostfixMethod("next", expression.getExpression());
    1171                 break;
    1172             case Types.MINUS_MINUS :
    1173                 evaluatePostfixMethod("previous", expression.getExpression());
    1174                 break;
    1175         }
    1176     }
    1177
    1178     public void visitPrefixExpression(PrefixExpression expression) {
    1179         switch (expression.getOperation().getType()) {
    1180             case Types.PLUS_PLUS :
    1181                 evaluatePrefixMethod("next", expression.getExpression());
    1182                 break;
    1183             case Types.MINUS_MINUS :
    1184                 evaluatePrefixMethod("previous", expression.getExpression());
    1185                 break;
    1186         }
    1187     }
    1188
    1189     public void visitClosureExpression(ClosureExpression expression) {
    1190         ClassNode innerClass = createClosureClass(expression);
    1191         addInnerClass(innerClass);
    1192         String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName());
    1193
    1194         ClassNode owner = innerClass.getOuterClass();
    1195         String ownerTypeName = owner.getName();
    1196         if (classNode.isStaticClass() || isStaticMethod()) {
    1197             ownerTypeName = "java.lang.Class";
    1198         }
    1199
    1200         passingClosureParams = true;
    1201         List constructors = innerClass.getDeclaredConstructors();
    1202         ConstructorNode node = (ConstructorNode) constructors.get(0);
    1203         Parameter[] localVariableParams = node.getParameters();
    1204
    1205
    1206         //
    1207         // Define in the context any variables that will be
    1208         // created inside the closure.  Note that the first two
    1209         // parameters are always _outerInstance and _delegate,
    1210         // so we don't worry about them.
    1211
    1212         for (int i = 2; i < localVariableParams.length; i++) {
    1213             Parameter param = localVariableParams[i];
    1214             String name = param.getName();
    1215
    1216             if (variableStack.get(name) == null && classNode.getField(name) == null) {
    1217                 defineVariable(name, "java.lang.Object");
    1218             }
    1219         }
    1220
    1221         cv.visitTypeInsn(NEW, innerClassinternalName);
    1222         cv.visitInsn(DUP);
    1223         if (isStaticMethod() || classNode.isStaticClass()) {
    1224             visitClassExpression(new ClassExpression(ownerTypeName));
    1225         }
    1226         else {
    1227             loadThisOrOwner();
    1228         }
    1229
    1230         if (innerClass.getSuperClass().equals("groovy.lang.Closure")) {
    1231             if (isStaticMethod()) {
    1232                 /***
    1233                  * todo could maybe stash this expression in a JVM variable
    1234                  * from previous statement above
    1235                  */
    1236                 visitClassExpression(new ClassExpression(ownerTypeName));
    1237             }
    1238             else {
    1239                 loadThisOrOwner();
    1240             }
    1241         }
    1242
    1243         //String prototype = "(L" + BytecodeHelper.getClassInternalName(ownerTypeName) + ";Ljava/lang/Object;";
    1244
    1245         // now lets load the various parameters we're passing
    1246         for (int i = 2; i < localVariableParams.length; i++) {
    1247             Parameter param = localVariableParams[i];
    1248             String name = param.getName();
    1249
    1250             if (variableStack.get(name) == null) {
    1251                 visitFieldExpression(new FieldExpression(classNode.getField(name)));
    1252             }
    1253             else {
    1254                 visitVariableExpression(new VariableExpression(name));
    1255             }
    1256             //prototype = prototype + "L" + BytecodeHelper.getClassInternalName(param.getType()) + ";";
    1257         }
    1258         passingClosureParams = false;
    1259
    1260         // we may need to pass in some other constructors
    1261         //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
    1262         cv.visitMethodInsn(
    1263             INVOKESPECIAL,
    1264             innerClassinternalName,
    1265             "<init>",
    1266             BytecodeHelper.getMethodDescriptor("void", localVariableParams));
    1267     }
    1268
    1269     /***
    1270      * Loads either this object or if we're inside a closure then load the top level owner
    1271      */
    1272     protected void loadThisOrOwner() {
    1273         if (isInnerClass()) {
    1274             visitFieldExpression(new FieldExpression(classNode.getField("owner")));
    1275         }
    1276         else {
    1277             cv.visitVarInsn(ALOAD, 0);
    1278         }
    1279     }
    1280
    1281     public void visitRegexExpression(RegexExpression expression) {
    1282         expression.getRegex().visit(this);
    1283         regexPattern.call(cv);
    1284     }
    1285
    1286     /***
    1287      * Generate byte code for constants
    1288      * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a>
    1289      */
    1290     public void visitConstantExpression(ConstantExpression expression) {
    1291         Object value = expression.getValue();
    1292         if (value == null) {
    1293             cv.visitInsn(ACONST_NULL);
    1294         }
    1295         else if (value instanceof String) {
    1296             cv.visitLdcInsn(value);
    1297         }
    1298         else if (value instanceof Number) {
    1299             /*** todo it would be more efficient to generate class constants */
    1300             Number n = (Number) value;
    1301             String className = BytecodeHelper.getClassInternalName(value.getClass().getName());
    1302             cv.visitTypeInsn(NEW, className);
    1303             cv.visitInsn(DUP);
    1304             String methodType;
    1305             if (n instanceof Double) {
    1306             	cv.visitLdcInsn(n);
    1307             	methodType = "(D)V";
    1308             }
    1309             else if (n instanceof Float) {
    1310             	cv.visitLdcInsn(n);
    1311             	methodType = "(F)V";
    1312             }
    1313             else if (value instanceof Long) {
    1314             	cv.visitLdcInsn(n);
    1315             	methodType = "(J)V";
    1316             }
    1317             else if (value instanceof BigDecimal) {
    1318             	cv.visitLdcInsn(n.toString());
    1319             	methodType = "(Ljava/lang/String;)V";
    1320             }
    1321             else if (value instanceof BigInteger) {
    1322             	cv.visitLdcInsn(n.toString());
    1323             	methodType = "(Ljava/lang/String;)V";
    1324             }
    1325             else if (value instanceof Integer){
    1326             	cv.visitLdcInsn(n);
    1327             	methodType = "(I)V";
    1328         	}
    1329             else
    1330             {
    1331         		throw new ClassGeneratorException(
    1332         				"Cannot generate bytecode for constant: " + value
    1333         				+ " of type: " + value.getClass().getName()
    1334         				+".  Numeric constant type not supported.");
    1335         	}
    1336
    1337             cv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType);
    1338         }
    1339         else if (value instanceof Boolean) {
    1340             Boolean bool = (Boolean) value;
    1341             String text = (bool.booleanValue()) ? "TRUE" : "FALSE";
    1342             cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;");
    1343         }
    1344         else if (value instanceof Class) {
    1345             Class vc = (Class) value;
    1346             if (!vc.getName().equals("java.lang.Void")) {
    1347                 throw new ClassGeneratorException(
    1348                 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
    1349             }
    1350         }
    1351         else {
    1352             throw new ClassGeneratorException(
    1353                 "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
    1354         }
    1355     }
    1356
    1357     public void visitNegationExpression(NegationExpression expression) {
    1358         Expression subExpression = expression.getExpression();
    1359         subExpression.visit(this);
    1360         negation.call(cv);
    1361     }
    1362
    1363     public void visitCastExpression(CastExpression expression) {
    1364         String type = expression.getType();
    1365         type = checkValidType(type, expression, "in cast");
    1366
    1367         visitAndAutobox(expression.getExpression());
    1368
    1369         doConvertAndCast(type, expression.getExpression());
    1370     }
    1371
    1372     public void visitNotExpression(NotExpression expression) {
    1373         Expression subExpression = expression.getExpression();
    1374         subExpression.visit(this);
    1375
    1376         // This is not the best way to do this. Javac does it by reversing the
    1377         // underlying expressions but that proved
    1378         // fairly complicated for not much gain. Instead we'll just use a
    1379         // utility function for now.
    1380         if (comparisonExpression(expression.getExpression())) {
    1381             notBoolean.call(cv);
    1382         }
    1383         else {
    1384             notObject.call(cv);
    1385         }
    1386     }
    1387
    1388     public void visitBooleanExpression(BooleanExpression expression) {
    1389         expression.getExpression().visit(this);
    1390
    1391         if (!comparisonExpression(expression.getExpression())) {
    1392             asBool.call(cv);
    1393         }
    1394     }
    1395
    1396     public void visitMethodCallExpression(MethodCallExpression call) {
    1397         this.leftHandExpression = false;
    1398
    1399         Expression arguments = call.getArguments();
    1400         /*
    1401          * if (arguments instanceof TupleExpression) { TupleExpression
    1402          * tupleExpression = (TupleExpression) arguments; int size =
    1403          * tupleExpression.getExpressions().size(); if (size == 0) { arguments =
    1404          * ConstantExpression.EMPTY_ARRAY; } }
    1405          */
    1406         boolean superMethodCall = MethodCallExpression.isSuperMethodCall(call);
    1407         String method = call.getMethod();
    1408         if (superMethodCall && method.equals("<init>")) {
    1409             /*** todo handle method types! */
    1410             cv.visitVarInsn(ALOAD, 0);
    1411             if (isInClosureConstructor()) { // br use the second param to init the super class (Closure)
    1412                 cv.visitVarInsn(ALOAD, 2);
    1413                 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
    1414             }
    1415             else {
    1416                 cv.visitVarInsn(ALOAD, 1);
    1417                 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
    1418             }
    1419         }
    1420         else {
    1421             // are we a local variable
    1422             if (isThisExpression(call.getObjectExpression()) && isFieldOrVariable(call.getMethod())) {
    1423                 /*
    1424                  * if (arguments instanceof TupleExpression) { TupleExpression
    1425                  * tupleExpression = (TupleExpression) arguments; int size =
    1426                  * tupleExpression.getExpressions().size(); if (size == 1) {
    1427                  * arguments = (Expression)
    1428                  * tupleExpression.getExpressions().get(0); } }
    1429                  */
    1430
    1431                 // lets invoke the closure method
    1432                 visitVariableExpression(new VariableExpression(method));
    1433                 arguments.visit(this);
    1434                 invokeClosureMethod.call(cv);
    1435             }
    1436             else {
    1437                 if (superMethodCall) {
    1438                     if (method.equals("super") || method.equals("<init>")) {
    1439                         ConstructorNode superConstructorNode = findSuperConstructor(call);
    1440
    1441                         cv.visitVarInsn(ALOAD, 0);
    1442
    1443                         loadArguments(superConstructorNode.getParameters(), arguments);
    1444
    1445                         String descriptor = BytecodeHelper.getMethodDescriptor("void", superConstructorNode.getParameters());
    1446                         cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", descriptor);
    1447                     }
    1448                     else {
    1449                         MethodNode superMethodNode = findSuperMethod(call);
    1450
    1451                         cv.visitVarInsn(ALOAD, 0);
    1452
    1453                         loadArguments(superMethodNode.getParameters(), arguments);
    1454
    1455                         String descriptor = BytecodeHelper.getMethodDescriptor(superMethodNode.getReturnType(), superMethodNode.getParameters());
    1456                         cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superMethodNode.getDeclaringClass().getName()), method, descriptor);
    1457                     }
    1458                 }
    1459                 else {
    1460                     if (emptyArguments(arguments) && !call.isSafe()) {
    1461                         call.getObjectExpression().visit(this);
    1462                         cv.visitLdcInsn(method);
    1463                         invokeNoArgumentsMethod.call(cv);
    1464                     }
    1465                     else {
    1466                         if (argumentsUseStack(arguments)) {
    1467                             int paramIdx =
    1468                                 defineVariable(createVariableName("temp"), "java.lang.Object", false).getIndex();
    1469
    1470                             arguments.visit(this);
    1471
    1472                             cv.visitVarInsn(ASTORE, paramIdx);
    1473
    1474                             call.getObjectExpression().visit(this);
    1475
    1476                             cv.visitLdcInsn(method);
    1477
    1478                             cv.visitVarInsn(ALOAD, paramIdx);
    1479                         }
    1480                         else {
    1481                             call.getObjectExpression().visit(this);
    1482                             cv.visitLdcInsn(method);
    1483                             arguments.visit(this);
    1484                         }
    1485
    1486                         if (call.isSafe()) {
    1487                             invokeMethodSafeMethod.call(cv);
    1488                         }
    1489                         else {
    1490                             invokeMethodMethod.call(cv);
    1491                         }
    1492                     }
    1493                 }
    1494             }
    1495         }
    1496     }
    1497
    1498     /***
    1499      * Loads and coerces the argument values for the given method call
    1500      */
    1501     protected void loadArguments(Parameter[] parameters, Expression expression) {
    1502         TupleExpression argListExp = (TupleExpression) expression;
    1503         List arguments = argListExp.getExpressions();
    1504         for (int i = 0, size = arguments.size(); i < size; i++) {
    1505             Expression argExp = argListExp.getExpression(i);
    1506             Parameter param = parameters[i];
    1507             visitAndAutobox(argExp);
    1508
    1509             String type = param.getType();
    1510             if (BytecodeHelper.isPrimitiveType(type)) {
    1511                 helper.unbox(type);
    1512             }
    1513             doConvertAndCast(type, argExp);
    1514         }
    1515     }
    1516
    1517     /***
    1518      * Attempts to find the method of the given name in a super class
    1519      */
    1520     protected MethodNode findSuperMethod(MethodCallExpression call) {
    1521         String methodName = call.getMethod();
    1522         TupleExpression argExpr = (TupleExpression) call.getArguments();
    1523         int argCount = argExpr.getExpressions().size();
    1524         ClassNode superClassNode = classNode.getSuperClassNode();
    1525         if (superClassNode != null) {
    1526             List methods = superClassNode.getMethods(methodName);
    1527             for (Iterator iter = methods.iterator(); iter.hasNext(); ) {
    1528                 MethodNode method = (MethodNode) iter.next();
    1529                 if (method.getParameters().length == argCount) {
    1530                     return method;
    1531                 }
    1532             }
    1533         }
    1534         throw new GroovyRuntimeException("No such method: " + methodName + " for class: " + classNode.getName(), call);
    1535     }
    1536
    1537     /***
    1538      * Attempts to find the constructor in a super class
    1539      */
    1540     protected ConstructorNode findSuperConstructor(MethodCallExpression call) {
    1541         TupleExpression argExpr = (TupleExpression) call.getArguments();
    1542         int argCount = argExpr.getExpressions().size();
    1543         ClassNode superClassNode = classNode.getSuperClassNode();
    1544         if (superClassNode != null) {
    1545             List constructors = superClassNode.getDeclaredConstructors();
    1546             for (Iterator iter = constructors.iterator(); iter.hasNext(); ) {
    1547                 ConstructorNode constructor = (ConstructorNode) iter.next();
    1548                 if (constructor.getParameters().length == argCount) {
    1549                     return constructor;
    1550                 }
    1551             }
    1552         }
    1553         throw new GroovyRuntimeException("No such constructor for class: " + classNode.getName(), call);
    1554     }
    1555
    1556     protected boolean emptyArguments(Expression arguments) {
    1557         if (arguments instanceof TupleExpression) {
    1558             TupleExpression tupleExpression = (TupleExpression) arguments;
    1559             int size = tupleExpression.getExpressions().size();
    1560             return size == 0;
    1561         }
    1562         return false;
    1563     }
    1564
    1565     public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
    1566         this.leftHandExpression = false;
    1567
    1568         Expression arguments = call.getArguments();
    1569         if (emptyArguments(arguments)) {
    1570             cv.visitLdcInsn(call.getType());
    1571             cv.visitLdcInsn(call.getMethod());
    1572
    1573             invokeStaticNoArgumentsMethod.call(cv);
    1574         }
    1575         else {
    1576             if (arguments instanceof TupleExpression) {
    1577                 TupleExpression tupleExpression = (TupleExpression) arguments;
    1578                 int size = tupleExpression.getExpressions().size();
    1579                 if (size == 1) {
    1580                     arguments = (Expression) tupleExpression.getExpressions().get(0);
    1581                 }
    1582             }
    1583
    1584             cv.visitLdcInsn(call.getType());
    1585             cv.visitLdcInsn(call.getMethod());
    1586             arguments.visit(this);
    1587
    1588             invokeStaticMethodMethod.call(cv);
    1589         }
    1590     }
    1591
    1592     public void visitConstructorCallExpression(ConstructorCallExpression call) {
    1593         this.leftHandExpression = false;
    1594
    1595         Expression arguments = call.getArguments();
    1596         if (arguments instanceof TupleExpression) {
    1597             TupleExpression tupleExpression = (TupleExpression) arguments;
    1598             int size = tupleExpression.getExpressions().size();
    1599             if (size == 0) {
    1600                 arguments = null;
    1601             }
    1602             else if (size == 1) {
    1603                 arguments = (Expression) tupleExpression.getExpressions().get(0);
    1604             }
    1605         }
    1606
    1607         // lets check that the type exists
    1608         String type = checkValidType(call.getType(), call, "in constructor call");
    1609
    1610         //System.out.println("Constructing: " + type);
    1611
    1612         visitClassExpression(new ClassExpression(type));
    1613         if (arguments !=null) {
    1614                arguments.visit(this);
    1615             invokeConstructorOfMethod.call(cv);
    1616         } else {
    1617                invokeNoArgumentsConstructorOf.call(cv);
    1618         }
    1619         /*
    1620          * cv.visitLdcInsn(type);
    1621          *
    1622          * arguments.visit(this);
    1623          *
    1624          * invokeConstructorMethod.call(cv);
    1625          */
    1626     }
    1627
    1628     public void visitPropertyExpression(PropertyExpression expression) {
    1629
    1630         // lets check if we're a fully qualified class name
    1631         String className = checkForQualifiedClass(expression);
    1632         if (className != null) {
    1633             visitClassExpression(new ClassExpression(className));
    1634             return;
    1635         }
    1636         Expression objectExpression = expression.getObjectExpression();
    1637         if (expression.getProperty().equals("class")) {
    1638             if ((objectExpression instanceof ClassExpression)) {
    1639                 visitClassExpression((ClassExpression) objectExpression);
    1640                 return;
    1641             }
    1642             else if (objectExpression instanceof VariableExpression) {
    1643                 VariableExpression varExp = (VariableExpression) objectExpression;
    1644                 className = varExp.getVariable();
    1645                 try {
    1646                     className = resolveClassName(className);
    1647                     visitClassExpression(new ClassExpression(className));
    1648                     return;
    1649                 }
    1650                 catch (Exception e) {
    1651                     // ignore
    1652                 }
    1653             }
    1654         }
    1655
    1656         if (isThisExpression(objectExpression)) {
    1657             // lets use the field expression if its available
    1658             String name = expression.getProperty();
    1659             FieldNode field = classNode.getField(name);
    1660             if (field != null) {
    1661                 visitFieldExpression(new FieldExpression(field));
    1662                 return;
    1663             }
    1664         }
    1665
    1666         boolean left = leftHandExpression;
    1667         // we need to clear the LHS flag to avoid "this." evaluating as ASTORE
    1668         // rather than ALOAD
    1669         leftHandExpression = false;
    1670
    1671         objectExpression.visit(this);
    1672
    1673         cv.visitLdcInsn(expression.getProperty());
    1674
    1675         if (isGroovyObject(objectExpression) && ! expression.isSafe()) {
    1676             if (left) {
    1677                 setGroovyObjectPropertyMethod.call(cv);
    1678             }
    1679             else {
    1680                 getGroovyObjectPropertyMethod.call(cv);
    1681             }
    1682         }
    1683         else {
    1684             if (expression.isSafe()) {
    1685                 if (left) {
    1686                     setPropertySafeMethod2.call(cv);
    1687                 }
    1688                 else {
    1689                     getPropertySafeMethod.call(cv);
    1690                 }
    1691             }
    1692             else {
    1693                 if (left) {
    1694                     setPropertyMethod2.call(cv);
    1695                 }
    1696                 else {
    1697                     getPropertyMethod.call(cv);
    1698                 }
    1699             }
    1700         }
    1701     }
    1702
    1703     protected boolean isGroovyObject(Expression objectExpression) {
    1704         return isThisExpression(objectExpression);
    1705     }
    1706
    1707     /***
    1708      * Checks if the given property expression represents a fully qualified class name
    1709      * @return the class name or null if the property is not a valid class name
    1710      */
    1711     protected String checkForQualifiedClass(PropertyExpression expression) {
    1712         String text = expression.getText();
    1713         try {
    1714             return resolveClassName(text);
    1715         }
    1716         catch (Exception e) {
    1717             if (text.endsWith(".class")) {
    1718                 text = text.substring(0, text.length() - 6);
    1719                 try {
    1720                     return resolveClassName(text);
    1721                 }
    1722                 catch (Exception e2) {
    1723                 }
    1724             }
    1725             return null;
    1726         }
    1727     }
    1728
    1729     public void visitFieldExpression(FieldExpression expression) {
    1730         FieldNode field = expression.getField();
    1731         boolean isStatic = field.isStatic();
    1732
    1733         boolean holder = field.isHolder() && !isInClosureConstructor();
    1734         if (!isStatic && !leftHandExpression) {
    1735             cv.visitVarInsn(ALOAD, 0);
    1736         }
    1737         String type = field.getType();
    1738         int tempIndex = defineVariable(createVariableName("field"), "java.lang.Object", false).getIndex();
    1739
    1740         if (leftHandExpression && !holder) {
    1741             if (isInClosureConstructor()) {
    1742                 helper.doCast(type);
    1743             }
    1744             else {
    1745                 // this may be superflous
    1746                 doConvertAndCast(type);
    1747             }
    1748         }
    1749
    1750         String ownerName =
    1751             (field.getOwner().equals(classNode.getName()))
    1752                 ? internalClassName
    1753                 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
    1754         int opcode  = isStatic ?  GETSTATIC : GETFIELD;
    1755         if (holder) {
    1756             if (leftHandExpression) {
    1757                 cv.visitVarInsn(ASTORE, tempIndex);
    1758                 if (!isStatic)
    1759                     cv.visitVarInsn(ALOAD, 0); // br
    1760                 cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    1761                 //cv.visitInsn(SWAP); // swap the value and the this pointer . swap is used to save a temp
    1762                 cv.visitVarInsn(ALOAD, tempIndex);
    1763                 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
    1764             }
    1765             else {
    1766                 cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    1767                 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
    1768             }
    1769         }
    1770         else {
    1771             if (leftHandExpression) {
    1772                 if (!isStatic)  {
    1773                     opcode = PUTFIELD;
    1774                     helper.store(field.getType(), tempIndex);
    1775                     cv.visitVarInsn(ALOAD, 0); // static does not need this pointer
    1776                     //cv.visitInsn(SWAP); // swap the value and the this pointer . swap is not type safe
    1777                     // cv.visitVarInsn(ALOAD, tempIndex);
    1778                     helper.load(field.getType(), tempIndex);
    1779
    1780                 } else {
    1781                     opcode = PUTSTATIC;
    1782                 }
    1783                 cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    1784             }else {
    1785                 cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    1786                 if (BytecodeHelper.isPrimitiveType(type)) {
    1787                     helper.box(type);
    1788                 }
    1789             }
    1790         }
    1791     }
    1792
    1793     protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first ) {
    1794         int valueIdx = defineVariable(createVariableName("temp"), "java.lang.Object", false).getIndex();
    1795
    1796         if (leftHandExpression && first) {
    1797             cv.visitVarInsn(ASTORE, valueIdx);
    1798         }
    1799
    1800         FieldNode field = expression.getField();
    1801         boolean isStatic = field.isStatic();
    1802
    1803         if (steps > 1 || !isStatic) {
    1804             cv.visitVarInsn(ALOAD, 0);
    1805             cv.visitFieldInsn(
    1806                 GETFIELD,
    1807                 internalClassName,
    1808                 "owner",
    1809                 BytecodeHelper.getTypeDescription(outerClassNode.getName()));
    1810         }
    1811
    1812         if( steps == 1 ) {
    1813             int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
    1814             String ownerName = BytecodeHelper.getClassInternalName(outerClassNode.getName());
    1815
    1816             if (leftHandExpression) {
    1817                 cv.visitVarInsn(ALOAD, valueIdx);
    1818                 boolean holder = field.isHolder() && !isInClosureConstructor();
    1819                 if ( !holder) {
    1820                     doConvertAndCast(field.getType());
    1821                 }
    1822             }
    1823             cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType()));
    1824             if (!leftHandExpression) {
    1825                 if (BytecodeHelper.isPrimitiveType(field.getType())) {
    1826                     helper.box(field.getType());
    1827                 }
    1828             }
    1829         }
    1830
    1831         else {
    1832             visitOuterFieldExpression( expression, outerClassNode.getOuterClass(), steps - 1, false );
    1833         }
    1834     }
    1835
    1836
    1837
    1838     /***
    1839      *  Visits a bare (unqualified) variable expression.
    1840      */
    1841
    1842     public void visitVariableExpression(VariableExpression expression) {
    1843
    1844         String variableName = expression.getVariable();
    1845
    1846       //-----------------------------------------------------------------------
    1847       // SPECIAL CASES
    1848
    1849         //
    1850         // "this" for static methods is the Class instance
    1851
    1852         if (isStaticMethod() && variableName.equals("this")) {
    1853             visitClassExpression(new ClassExpression(classNode.getName()));
    1854             return;                                               // <<< FLOW CONTROL <<<<<<<<<
    1855         }
    1856
    1857         //
    1858         // "super" also requires special handling
    1859
    1860         if (variableName.equals("super")) {
    1861             visitClassExpression(new ClassExpression(classNode.getSuperClass()));
    1862             return;                                               // <<< FLOW CONTROL <<<<<<<<<
    1863         }
    1864
    1865
    1866         //
    1867         // class names return a Class instance, too
    1868
    1869         if (!variableName.equals("this")) {
    1870             String className = resolveClassName(variableName);
    1871             if (className != null) {
    1872                 if (leftHandExpression) {
    1873                     throw new RuntimeParserException(
    1874                         "Cannot use a class expression on the left hand side of an assignment",
    1875                         expression);
    1876                 }
    1877                 visitClassExpression(new ClassExpression(className));
    1878                 return;                                               // <<< FLOW CONTROL <<<<<<<<<
    1879             }
    1880         }
    1881
    1882
    1883       //-----------------------------------------------------------------------
    1884       // GENERAL VARIABLE LOOKUP
    1885
    1886
    1887         //
    1888         // We are handling only unqualified variables here.  Therefore,
    1889         // we do not care about accessors, because local access doesn't
    1890         // go through them.  Therefore, precedence is as follows:
    1891         //   1) local variables, nearest block first
    1892         //   2) class fields
    1893         //   3) repeat search from 2) in next outer class
    1894
    1895         boolean  handled  = false;
    1896         Variable variable = (Variable)variableStack.get( variableName );
    1897
    1898         if( variable != null ) {
    1899
    1900             if( variable.isProperty() ) {
    1901                 processPropertyVariable( variableName, variable );
    1902             }
    1903             else {
    1904                 processStackVariable( variableName, variable );
    1905             }
    1906
    1907             handled = true;
    1908         }
    1909
    1910         //
    1911         // Loop through outer classes for fields
    1912
    1913         else {
    1914
    1915             int       steps   = 0;
    1916             ClassNode current = classNode;
    1917             FieldNode field   = null;
    1918
    1919             do {
    1920                 if( (field = current.getField(variableName)) != null ) {
    1921                     break;
    1922                 }
    1923                 steps++;
    1924
    1925             } while( (current = current.getOuterClass()) != null );
    1926
    1927             if( field != null ) {
    1928                 processFieldAccess( variableName, field, steps );
    1929                 handled = true;
    1930             }
    1931         }
    1932
    1933
    1934         //
    1935         // Finally, if unhandled, create a variable for it.
    1936         // Except there a stack variable should be created,
    1937         // we define the variable as a property accessor and
    1938         // let other parts of the classgen report the error
    1939         // if the property doesn't exist.
    1940
    1941         if( !handled ) {
    1942            String variableType = expression.getType();
    1943            variable = defineVariable( variableName, variableType );
    1944
    1945            if( isInScriptBody() || !leftHandExpression ) {
    1946                variable.setProperty( true );
    1947                processPropertyVariable( variableName, variable );
    1948            }
    1949            else {
    1950                processStackVariable( variableName, variable );
    1951            }
    1952         }
    1953     }
    1954
    1955
    1956     protected void processStackVariable( String name, Variable variable ) {
    1957         String  type   = variable.getTypeName();
    1958         int     index  = variable.getIndex();
    1959         boolean holder = variable.isHolder() && !passingClosureParams;
    1960
    1961         if( leftHandExpression ) {
    1962             if (holder) {
    1963                 int tempIndex = defineVariable(createVariableName("reference"), type, false).getIndex();
    1964                 cv.visitVarInsn(ASTORE, tempIndex);
    1965                 cv.visitVarInsn(ALOAD, index);
    1966                 //cv.visitInsn(SWAP);  // assuming the value is already on the stack
    1967                 cv.visitVarInsn(ALOAD, tempIndex);
    1968                 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
    1969             }
    1970             else {
    1971                 helper.store("objref", index); // br seems right hand values on the stack are always object refs, primitives boxed
    1972                 //cv.visitVarInsn(ASTORE, index);
    1973             }
    1974         }
    1975         else {
    1976             if (holder) {
    1977                 cv.visitVarInsn(ALOAD, index);
    1978                 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
    1979             }
    1980             else {
    1981                 cv.visitVarInsn(ALOAD, index);
    1982             }
    1983         }
    1984
    1985     }
    1986
    1987     protected void processPropertyVariable( String name, Variable variable ) {
    1988         if (variable.isHolder() && passingClosureParams && isInScriptBody() ) {
    1989             // lets create a ScriptReference to pass into the closure
    1990             cv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference");
    1991             cv.visitInsn(DUP);
    1992
    1993             loadThisOrOwner();
    1994             cv.visitLdcInsn(name);
    1995
    1996             cv.visitMethodInsn(
    1997                 INVOKESPECIAL,
    1998                 "org/codehaus/groovy/runtime/ScriptReference",
    1999                 "<init>",
    2000                 "(Lgroovy/lang/Script;Ljava/lang/String;)V");
    2001         }
    2002         else {
    2003             visitPropertyExpression(new PropertyExpression(VariableExpression.THIS_EXPRESSION, name));
    2004         }
    2005     }
    2006
    2007
    2008     protected void processFieldAccess( String name, FieldNode field, int steps ) {
    2009         FieldExpression expression = new FieldExpression(field);
    2010
    2011         if( steps == 0 ) {
    2012             visitFieldExpression( expression );
    2013         }
    2014         else {
    2015             visitOuterFieldExpression( expression, classNode.getOuterClass(), steps, true );
    2016         }
    2017     }
    2018
    2019
    2020
    2021     /***
    2022      * @return true if we are in a script body, where all variables declared are no longer
    2023      * local variables but are properties
    2024      */
    2025     protected boolean isInScriptBody() {
    2026         if (classNode.isScriptBody()) {
    2027             return true;
    2028         }
    2029         else {
    2030             return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
    2031         }
    2032     }
    2033
    2034     /***
    2035      * @return true if this expression will have left a value on the stack
    2036      * that must be popped
    2037      */
    2038     protected boolean isPopRequired(Expression expression) {
    2039         if (expression instanceof MethodCallExpression) {
    2040             return !MethodCallExpression.isSuperMethodCall((MethodCallExpression) expression);
    2041         }
    2042         if (expression instanceof BinaryExpression) {
    2043             BinaryExpression binExp = (BinaryExpression) expression;
    2044             switch (binExp.getOperation().getType()) {
    2045                 case Types.EQUAL :
    2046                 case Types.PLUS_EQUAL :
    2047                 case Types.MINUS_EQUAL :
    2048                 case Types.MULTIPLY_EQUAL :
    2049                 case Types.DIVIDE_EQUAL :
    2050                 case Types.INTDIV_EQUAL :
    2051                 case Types.MOD_EQUAL :
    2052                     return false;
    2053             }
    2054         }
    2055         return true;
    2056     }
    2057
    2058     protected boolean firstStatementIsSuperInit(Statement code) {
    2059         ExpressionStatement expStmt = null;
    2060         if (code instanceof ExpressionStatement) {
    2061             expStmt = (ExpressionStatement) code;
    2062         }
    2063         else if (code instanceof BlockStatement) {
    2064             BlockStatement block = (BlockStatement) code;
    2065             if (!block.getStatements().isEmpty()) {
    2066                 Object expr = block.getStatements().get(0);
    2067                 if (expr instanceof ExpressionStatement) {
    2068                     expStmt = (ExpressionStatement) expr;
    2069                 }
    2070             }
    2071         }
    2072         if (expStmt != null) {
    2073             Expression expr = expStmt.getExpression();
    2074             if (expr instanceof MethodCallExpression) {
    2075             	MethodCallExpression call = (MethodCallExpression) expr;
    2076                 if (MethodCallExpression.isSuperMethodCall(call)) {
    2077                     // not sure which one is constantly used as the super class ctor call. To cover both for now
    2078                 	return call.getMethod().equals("<init>") || call.getMethod().equals("super");
    2079                 }
    2080             }
    2081         }
    2082         return false;
    2083     }
    2084
    2085     protected void createSyntheticStaticFields() {
    2086         for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
    2087             String staticFieldName = (String) iter.next();
    2088             // generate a field node
    2089             cw.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null);
    2090         }
    2091
    2092         if (!syntheticStaticFields.isEmpty()) {
    2093             cv =
    2094                 cw.visitMethod(
    2095                     ACC_STATIC + ACC_SYNTHETIC,
    2096                     "class$",
    2097                     "(Ljava/lang/String;)Ljava/lang/Class;",
    2098                     null,
    2099                     null);
    2100             helper = new BytecodeHelper(cv);
    2101
    2102             Label l0 = new Label();
    2103             cv.visitLabel(l0);
    2104             cv.visitVarInsn(ALOAD, 0);
    2105             cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
    2106             Label l1 = new Label();
    2107             cv.visitLabel(l1);
    2108             cv.visitInsn(ARETURN);
    2109             Label l2 = new Label();
    2110             cv.visitLabel(l2);
    2111             cv.visitVarInsn(ASTORE, 1);
    2112             cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
    2113             cv.visitInsn(DUP);
    2114             cv.visitVarInsn(ALOAD, 1);
    2115             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;");
    2116             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V");
    2117             cv.visitInsn(ATHROW);
    2118             cv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
    2119             cv.visitMaxs(3, 2);
    2120
    2121             cw.visitEnd();
    2122         }
    2123     }
    2124
    2125     public void visitClassExpression(ClassExpression expression) {
    2126         String type = expression.getText();
    2127         //type = checkValidType(type, expression, "Must be a valid type name for a constructor call");
    2128
    2129
    2130         if (BytecodeHelper.isPrimitiveType(type)) {
    2131             String objectType = BytecodeHelper.getObjectTypeForPrimitive(type);
    2132             cv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;");
    2133         }
    2134         else {
    2135             final String staticFieldName =
    2136                 (type.equals(classNode.getName())) ? "class$0" : "class$" + type.replace('.', '$');
    2137
    2138             syntheticStaticFields.add(staticFieldName);
    2139
    2140             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
    2141             Label l0 = new Label();
    2142             cv.visitJumpInsn(IFNONNULL, l0);
    2143             cv.visitLdcInsn(type);
    2144             cv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
    2145             cv.visitInsn(DUP);
    2146             cv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
    2147             Label l1 = new Label();
    2148             cv.visitJumpInsn(GOTO, l1);
    2149             cv.visitLabel(l0);
    2150             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
    2151             cv.visitLabel(l1);
    2152         }
    2153     }
    2154
    2155     public void visitRangeExpression(RangeExpression expression) {
    2156         leftHandExpression = false;
    2157         expression.getFrom().visit(this);
    2158
    2159         leftHandExpression = false;
    2160         expression.getTo().visit(this);
    2161
    2162         helper.pushConstant(expression.isInclusive());
    2163
    2164         createRangeMethod.call(cv);
    2165     }
    2166
    2167     public void visitMapEntryExpression(MapEntryExpression expression) {
    2168     }
    2169
    2170     public void visitMapExpression(MapExpression expression) {
    2171         List entries = expression.getMapEntryExpressions();
    2172         int size = entries.size();
    2173         helper.pushConstant(size * 2);
    2174
    2175         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    2176
    2177         int i = 0;
    2178         for (Iterator iter = entries.iterator(); iter.hasNext();) {
    2179             MapEntryExpression entry = (MapEntryExpression) iter.next();
    2180
    2181             cv.visitInsn(DUP);
    2182             helper.pushConstant(i++);
    2183             visitAndAutobox(entry.getKeyExpression());
    2184             cv.visitInsn(AASTORE);
    2185
    2186             cv.visitInsn(DUP);
    2187             helper.pushConstant(i++);
    2188             visitAndAutobox(entry.getValueExpression());
    2189             cv.visitInsn(AASTORE);
    2190         }
    2191         createMapMethod.call(cv);
    2192     }
    2193
    2194     public void visitTupleExpression(TupleExpression expression) {
    2195         int size = expression.getExpressions().size();
    2196
    2197         helper.pushConstant(size);
    2198
    2199         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    2200
    2201         for (int i = 0; i < size; i++) {
    2202             cv.visitInsn(DUP);
    2203             helper.pushConstant(i);
    2204             visitAndAutobox(expression.getExpression(i));
    2205             cv.visitInsn(AASTORE);
    2206         }
    2207         //createTupleMethod.call(cv);
    2208     }
    2209
    2210     public void visitArrayExpression(ArrayExpression expression) {
    2211         String type = expression.getType();
    2212         String typeName = BytecodeHelper.getClassInternalName(type);
    2213         Expression sizeExpression = expression.getSizeExpression();
    2214         if (sizeExpression != null) {
    2215             // lets convert to an int
    2216             visitAndAutobox(sizeExpression);
    2217             asIntMethod.call(cv);
    2218
    2219             cv.visitTypeInsn(ANEWARRAY, typeName);
    2220         }
    2221         else {
    2222             int size = expression.getExpressions().size();
    2223             helper.pushConstant(size);
    2224
    2225             cv.visitTypeInsn(ANEWARRAY, typeName);
    2226
    2227             for (int i = 0; i < size; i++) {
    2228                 cv.visitInsn(DUP);
    2229                 helper.pushConstant(i);
    2230                 Expression elementExpression = expression.getExpression(i);
    2231                 if (elementExpression == null) {
    2232                     ConstantExpression.NULL.visit(this);
    2233                 }
    2234                 else {
    2235
    2236                     if(!type.equals(elementExpression.getClass().getName())) {
    2237                         visitCastExpression(new CastExpression(type, elementExpression));
    2238                     }
    2239                     else {
    2240                         visitAndAutobox(elementExpression);
    2241                     }
    2242                 }
    2243                 cv.visitInsn(AASTORE);
    2244             }
    2245         }
    2246     }
    2247
    2248     public void visitListExpression(ListExpression expression) {
    2249         int size = expression.getExpressions().size();
    2250         helper.pushConstant(size);
    2251
    2252         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    2253
    2254         for (int i = 0; i < size; i++) {
    2255             cv.visitInsn(DUP);
    2256             helper.pushConstant(i);
    2257             visitAndAutobox(expression.getExpression(i));
    2258             cv.visitInsn(AASTORE);
    2259         }
    2260         createListMethod.call(cv);
    2261     }
    2262
    2263     public void visitGStringExpression(GStringExpression expression) {
    2264         int size = expression.getValues().size();
    2265         helper.pushConstant(size);
    2266
    2267         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    2268
    2269         for (int i = 0; i < size; i++) {
    2270             cv.visitInsn(DUP);
    2271             helper.pushConstant(i);
    2272             visitAndAutobox(expression.getValue(i));
    2273             cv.visitInsn(AASTORE);
    2274         }
    2275
    2276         int paramIdx = defineVariable(createVariableName("iterator"), "java.lang.Object", false).getIndex();
    2277         cv.visitVarInsn(ASTORE, paramIdx);
    2278
    2279         ClassNode innerClass = createGStringClass(expression);
    2280         addInnerClass(innerClass);
    2281         String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName());
    2282
    2283         cv.visitTypeInsn(NEW, innerClassinternalName);
    2284         cv.visitInsn(DUP);
    2285         cv.visitVarInsn(ALOAD, paramIdx);
    2286
    2287         cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", "([Ljava/lang/Object;)V");
    2288     }
    2289
    2290     // Implementation methods
    2291     //-------------------------------------------------------------------------
    2292     protected boolean addInnerClass(ClassNode innerClass) {
    2293         innerClass.setModule(classNode.getModule());
    2294         return innerClasses.add(innerClass);
    2295     }
    2296
    2297     protected ClassNode createClosureClass(ClosureExpression expression) {
    2298         ClassNode owner = getOutermostClass();
    2299         boolean parentIsInnerClass = owner instanceof InnerClassNode;
    2300         String outerClassName = owner.getName();
    2301         String name = outerClassName + "$"
    2302                 + context.getNextClosureInnerName(owner, classNode, methodNode); // br added a more infomative name
    2303         boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass();
    2304         if (staticMethodOrInStaticClass) {
    2305             outerClassName = "java.lang.Class";
    2306         }
    2307         Parameter[] parameters = expression.getParameters();
    2308         if (parameters == null || parameters.length == 0) {
    2309             // lets create a default 'it' parameter
    2310             parameters = new Parameter[] { new Parameter("it")};
    2311         }
    2312
    2313         Parameter[] localVariableParams = getClosureSharedVariables(expression);
    2314
    2315         InnerClassNode answer = new InnerClassNode(owner, name, ACC_SUPER, "groovy.lang.Closure"); // clsures are local inners and not public
    2316         answer.setEnclosingMethod(this.methodNode);
    2317         if (staticMethodOrInStaticClass) {
    2318             answer.setStaticClass(true);
    2319         }
    2320         if (isInScriptBody()) {
    2321             answer.setScriptBody(true);
    2322         }
    2323         MethodNode method =
    2324             answer.addMethod("doCall", ACC_PUBLIC, "java.lang.Object", parameters, expression.getCode());
    2325
    2326         method.setLineNumber(expression.getLineNumber());
    2327         method.setColumnNumber(expression.getColumnNumber());
    2328
    2329         VariableScope varScope = expression.getVariableScope();
    2330         if (varScope == null) {
    2331             throw new RuntimeException(
    2332                 "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
    2333         }
    2334         else {
    2335             method.setVariableScope(varScope);
    2336         }
    2337         if (parameters.length > 1
    2338             || (parameters.length == 1
    2339                 && parameters[0].getType() != null
    2340                 && !parameters[0].getType().equals("java.lang.Object"))) {
    2341
    2342             // lets add a typesafe call method
    2343             answer.addMethod(
    2344                 "call",
    2345                 ACC_PUBLIC,
    2346                 "java.lang.Object",
    2347                 parameters,
    2348                 new ReturnStatement(
    2349                     new MethodCallExpression(
    2350                         VariableExpression.THIS_EXPRESSION,
    2351                         "doCall",
    2352                         new ArgumentListExpression(parameters))));
    2353         }
    2354
    2355         FieldNode ownerField = answer.addField("owner", ACC_PRIVATE, outerClassName, null);
    2356
    2357         // lets make the constructor
    2358         BlockStatement block = new BlockStatement();
    2359         block.addStatement(
    2360             new ExpressionStatement(
    2361                 new MethodCallExpression(
    2362                     new VariableExpression("super"),
    2363                     "<init>",
    2364                     new VariableExpression("_outerInstance"))));
    2365         block.addStatement(
    2366             new ExpressionStatement(
    2367                 new BinaryExpression(
    2368                     new FieldExpression(ownerField),
    2369                     Token.newSymbol(Types.EQUAL, -1, -1),
    2370                     new VariableExpression("_outerInstance"))));
    2371
    2372         // lets assign all the parameter fields from the outer context
    2373         for (int i = 0; i < localVariableParams.length; i++) {
    2374             Parameter param = localVariableParams[i];
    2375             String paramName = param.getName();
    2376             boolean holder = mutableVars.contains(paramName);
    2377             Expression initialValue = null;
    2378             String type = param.getType();
    2379             FieldNode paramField = null;
    2380             if (holder) {
    2381             	initialValue = new VariableExpression(paramName);
    2382                 type = Reference.class.getName();
    2383                 param.makeReference();
    2384                 paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue);
    2385                 paramField.setHolder(true);
    2386                 String realType = param.getRealType();
    2387                 String methodName = Verifier.capitalize(paramName);
    2388
    2389                 // lets add a getter & setter
    2390                 Expression fieldExp = new FieldExpression(paramField);
    2391                 answer.addMethod(
    2392                     "get" + methodName,
    2393                     ACC_PUBLIC,
    2394                     realType,
    2395                     Parameter.EMPTY_ARRAY,
    2396                     new ReturnStatement(fieldExp));
    2397
    2398                 /*
    2399                 answer.addMethod(
    2400                     "set" + methodName,
    2401                     ACC_PUBLIC,
    2402                     "void",
    2403                     new Parameter[] { new Parameter(realType, "__value") },
    2404                     new ExpressionStatement(
    2405                         new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
    2406                         */
    2407             }
    2408             else {
    2409             	PropertyNode propertyNode = answer.addProperty(paramName, ACC_PUBLIC, type, initialValue, null, null);
    2410                 paramField = propertyNode.getField();
    2411                 block.addStatement(
    2412                     new ExpressionStatement(
    2413                         new BinaryExpression(
    2414                             new FieldExpression(paramField),
    2415                             Token.newSymbol(Types.EQUAL, -1, -1),
    2416                             new VariableExpression(paramName))));
    2417             }
    2418         }
    2419
    2420         Parameter[] params = new Parameter[2 + localVariableParams.length];
    2421         params[0] = new Parameter(outerClassName, "_outerInstance");
    2422         params[1] = new Parameter("java.lang.Object", "_delegate");
    2423         System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
    2424
    2425         answer.addConstructor(ACC_PUBLIC, params, block);
    2426         return answer;
    2427     }
    2428
    2429     protected ClassNode getOutermostClass() {
    2430         if (outermostClass == null) {
    2431             outermostClass = classNode;
    2432             while (outermostClass instanceof InnerClassNode) {
    2433                 outermostClass = outermostClass.getOuterClass();
    2434             }
    2435         }
    2436         return outermostClass;
    2437     }
    2438
    2439     protected ClassNode createGStringClass(GStringExpression expression) {
    2440         ClassNode owner = classNode;
    2441         if (owner instanceof InnerClassNode) {
    2442             owner = owner.getOuterClass();
    2443         }
    2444         String outerClassName = owner.getName();
    2445         String name = outerClassName + "$" + context.getNextInnerClassIdx();
    2446         InnerClassNode answer = new InnerClassNode(owner, name, ACC_SUPER, GString.class.getName());
    2447         answer.setEnclosingMethod(this.methodNode);
    2448         FieldNode stringsField =
    2449             answer.addField(
    2450                 "strings",
    2451                 ACC_PRIVATE /*| ACC_STATIC*/,
    2452                 "java.lang.String[]",
    2453                 new ArrayExpression("java.lang.String", expression.getStrings()));
    2454         answer.addMethod(
    2455             "getStrings",
    2456             ACC_PUBLIC,
    2457             "java.lang.String[]",
    2458             Parameter.EMPTY_ARRAY,
    2459             new ReturnStatement(new FieldExpression(stringsField)));
    2460         // lets make the constructor
    2461         BlockStatement block = new BlockStatement();
    2462         block.addStatement(
    2463             new ExpressionStatement(
    2464                 new MethodCallExpression(new VariableExpression("super"), "<init>", new VariableExpression("values"))));
    2465         Parameter[] contructorParams = new Parameter[] { new Parameter("java.lang.Object[]", "values")};
    2466         answer.addConstructor(ACC_PUBLIC, contructorParams, block);
    2467         return answer;
    2468     }
    2469
    2470     protected void doConvertAndCast(String type) {
    2471         if (!type.equals("java.lang.Object")) {
    2472             /*** todo should probably support array coercions */
    2473             if (!type.endsWith("[]") && isValidTypeForCast(type)) {
    2474                 visitClassExpression(new ClassExpression(type));
    2475                 asTypeMethod.call(cv);
    2476             }
    2477
    2478             helper.doCast(type);
    2479         }
    2480     }
    2481
    2482     protected void evaluateLogicalOrExpression(BinaryExpression expression) {
    2483         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
    2484         Label l0 = new Label();
    2485         Label l2 = new Label();
    2486         cv.visitJumpInsn(IFEQ, l0);
    2487
    2488         cv.visitLabel(l2);
    2489
    2490         visitConstantExpression(ConstantExpression.TRUE);
    2491
    2492         Label l1 = new Label();
    2493         cv.visitJumpInsn(GOTO, l1);
    2494         cv.visitLabel(l0);
    2495
    2496         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
    2497
    2498         cv.visitJumpInsn(IFNE, l2);
    2499
    2500         visitConstantExpression(ConstantExpression.FALSE);
    2501         cv.visitLabel(l1);
    2502     }
    2503
    2504     protected void evaluateLogicalAndExpression(BinaryExpression expression) {
    2505         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
    2506         Label l0 = new Label();
    2507         cv.visitJumpInsn(IFEQ, l0);
    2508
    2509         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
    2510
    2511         cv.visitJumpInsn(IFEQ, l0);
    2512
    2513         visitConstantExpression(ConstantExpression.TRUE);
    2514
    2515         Label l1 = new Label();
    2516         cv.visitJumpInsn(GOTO, l1);
    2517         cv.visitLabel(l0);
    2518
    2519         visitConstantExpression(ConstantExpression.FALSE);
    2520
    2521         cv.visitLabel(l1);
    2522     }
    2523
    2524     protected void evaluateBinaryExpression(String method, BinaryExpression expression) {
    2525         Expression leftExpression = expression.getLeftExpression();
    2526         leftHandExpression = false;
    2527         leftExpression.visit(this);
    2528         cv.visitLdcInsn(method);
    2529         leftHandExpression = false;
    2530         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}).visit(this);
    2531         // expression.getRightExpression().visit(this);
    2532         invokeMethodMethod.call(cv);
    2533     }
    2534
    2535     protected void evaluateCompareTo(BinaryExpression expression) {
    2536         Expression leftExpression = expression.getLeftExpression();
    2537         leftHandExpression = false;
    2538         leftExpression.visit(this);
    2539         expression.getRightExpression().visit(this);
    2540         compareToMethod.call(cv);
    2541     }
    2542
    2543     protected void evaluateBinaryExpressionWithAsignment(String method, BinaryExpression expression) {
    2544         Expression leftExpression = expression.getLeftExpression();
    2545         if (leftExpression instanceof BinaryExpression) {
    2546             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
    2547             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
    2548                 // lets replace this assignment to a subscript operator with a
    2549                 // method call
    2550                 // e.g. x[5] += 10
    2551                 // -> (x, [], 5), =, x[5] + 10
    2552                 // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
    2553
    2554                 MethodCallExpression methodCall =
    2555                     new MethodCallExpression(
    2556                         expression.getLeftExpression(),
    2557                         method,
    2558                         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}));
    2559
    2560                 Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression());
    2561
    2562                 visitMethodCallExpression(
    2563                     new MethodCallExpression(
    2564                         leftBinExpr.getLeftExpression(),
    2565                         "putAt",
    2566                         new ArgumentListExpression(new Expression[] { safeIndexExpr, methodCall })));
    2567                 cv.visitInsn(POP);
    2568                 return;
    2569             }
    2570         }
    2571
    2572         evaluateBinaryExpression(method, expression);
    2573
    2574         leftHandExpression = true;
    2575         evaluateExpression(leftExpression);
    2576         leftHandExpression = false;
    2577     }
    2578
    2579     protected void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression expression) {
    2580         Expression leftExpression = expression.getLeftExpression();
    2581         /*
    2582         if (isNonStaticField(leftExpression)) {
    2583             cv.visitVarInsn(ALOAD, 0);
    2584         }
    2585         */
    2586
    2587         leftHandExpression = false;
    2588
    2589         evaluateExpression(leftExpression);
    2590         leftHandExpression = false;
    2591         evaluateExpression(expression.getRightExpression());
    2592         // now lets invoke the method
    2593         compareMethod.call(cv);
    2594     }
    2595
    2596     protected void evaluateEqual(BinaryExpression expression) {
    2597         Expression leftExpression = expression.getLeftExpression();
    2598         if (leftExpression instanceof BinaryExpression) {
    2599             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
    2600             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
    2601                 // lets replace this assignment to a subscript operator with a
    2602                 // method call
    2603                 // e.g. x[5] = 10
    2604                 // -> (x, [], 5), =, 10
    2605                 // -> methodCall(x, "putAt", [5, 10])
    2606                 visitMethodCallExpression(
    2607                     new MethodCallExpression(
    2608                         leftBinExpr.getLeftExpression(),
    2609                         "putAt",
    2610                         new ArgumentListExpression(
    2611                             new Expression[] { leftBinExpr.getRightExpression(), expression.getRightExpression()})));
    2612                 cv.visitInsn(POP);
    2613                 return;
    2614             }
    2615         }
    2616         // br we'll load this pointer later right where we really need it
    2617
    2618         // lets evaluate the RHS then hopefully the LHS will be a field
    2619         leftHandExpression = false;
    2620         Expression rightExpression = expression.getRightExpression();
    2621
    2622         String type = getLHSType(leftExpression);
    2623         if (type != null) {
    2624             //System.out.println("### expression: " + leftExpression);
    2625             //System.out.println("### type: " + type);
    2626
    2627             // lets not cast for primitive types as we handle these in field setting etc
    2628             if (BytecodeHelper.isPrimitiveType(type)) {
    2629                 rightExpression.visit(this);
    2630             }
    2631             else {
    2632                 visitCastExpression(new CastExpression(type, rightExpression));
    2633             }
    2634         }
    2635         else {
    2636             visitAndAutobox(rightExpression);
    2637         }
    2638
    2639         leftHandExpression = true;
    2640         leftExpression.visit(this);
    2641         leftHandExpression = false;
    2642     }
    2643
    2644     /***
    2645      * Deduces the type name required for some casting
    2646      *
    2647      * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
    2648      */
    2649     protected String getLHSType(Expression leftExpression) {
    2650         if (leftExpression instanceof VariableExpression) {
    2651             VariableExpression varExp = (VariableExpression) leftExpression;
    2652             String type = varExp.getType();
    2653             if (isValidTypeForCast(type)) {
    2654                 return type;
    2655             }
    2656             String variableName = varExp.getVariable();
    2657             Variable variable = (Variable) variableStack.get(variableName);
    2658             if (variable != null) {
    2659                 if (variable.isHolder() || variable.isProperty()) {
    2660                     return null;
    2661                 }
    2662                 type = variable.getTypeName();
    2663                 if (isValidTypeForCast(type)) {
    2664                     return type;
    2665                 }
    2666             }
    2667             else {
    2668                 FieldNode field = classNode.getField(variableName);
    2669                 if (field == null) {
    2670                     field = classNode.getOuterField(variableName);
    2671                 }
    2672                 if (field != null) {
    2673                     type = field.getType();
    2674                     if (!field.isHolder() && isValidTypeForCast(type)) {
    2675                         return type;
    2676                     }
    2677                 }
    2678             }
    2679         }
    2680         return null;
    2681     }
    2682
    2683     protected boolean isValidTypeForCast(String type) {
    2684         return type != null && !type.equals("java.lang.Object") && !type.equals("groovy.lang.Reference") && !BytecodeHelper.isPrimitiveType(type);
    2685     }
    2686
    2687     protected void visitAndAutobox(Expression expression) {
    2688         expression.visit(this);
    2689
    2690         if (comparisonExpression(expression)) {
    2691             Label l0 = new Label();
    2692             cv.visitJumpInsn(IFEQ, l0);
    2693             cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
    2694             Label l1 = new Label();
    2695             cv.visitJumpInsn(GOTO, l1);
    2696             cv.visitLabel(l0);
    2697             cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
    2698             cv.visitLabel(l1);
    2699         }
    2700     }
    2701
    2702     protected void evaluatePrefixMethod(String method, Expression expression) {
    2703         if (isNonStaticField(expression) && ! isHolderVariable(expression) && !isStaticMethod()) {
    2704             cv.visitVarInsn(ALOAD, 0);
    2705         }
    2706         expression.visit(this);
    2707         cv.visitLdcInsn(method);
    2708         invokeNoArgumentsMethod.call(cv);
    2709
    2710         leftHandExpression = true;
    2711         expression.visit(this);
    2712         leftHandExpression = false;
    2713         expression.visit(this);
    2714     }
    2715
    2716     protected void evaluatePostfixMethod(String method, Expression expression) {
    2717 //        if (isNonStaticField(expression) && ! isHolderVariable(expression) && !isStaticMethod()) {
    2718 //            cv.visitVarInsn(ALOAD, 0);  // br again, loading this pointer is moved to staying close to variable
    2719 //        }
    2720         leftHandExpression = false;
    2721         expression.visit(this);
    2722
    2723         int tempIdx = defineVariable(createVariableName("postfix"), "java.lang.Object", false).getIndex();
    2724         cv.visitVarInsn(ASTORE, tempIdx);
    2725         cv.visitVarInsn(ALOAD, tempIdx);
    2726
    2727         cv.visitLdcInsn(method);
    2728         invokeNoArgumentsMethod.call(cv);
    2729
    2730         leftHandExpression = true;
    2731         expression.visit(this);
    2732         leftHandExpression = false;
    2733
    2734         cv.visitVarInsn(ALOAD, tempIdx);
    2735     }
    2736
    2737     protected boolean isHolderVariable(Expression expression) {
    2738         if (expression instanceof FieldExpression) {
    2739             FieldExpression fieldExp = (FieldExpression) expression;
    2740             return fieldExp.getField().isHolder();
    2741         }
    2742         if (expression instanceof VariableExpression) {
    2743             VariableExpression varExp = (VariableExpression) expression;
    2744             Variable variable = (Variable) variableStack.get(varExp.getVariable());
    2745             if (variable != null) {
    2746                 return variable.isHolder();
    2747             }
    2748             FieldNode field = classNode.getField(varExp.getVariable());
    2749             if (field != null) {
    2750                 return field.isHolder();
    2751             }
    2752         }
    2753         return false;
    2754     }
    2755
    2756     protected void evaluateInstanceof(BinaryExpression expression) {
    2757         expression.getLeftExpression().visit(this);
    2758         Expression rightExp = expression.getRightExpression();
    2759         String className = null;
    2760         if (rightExp instanceof ClassExpression) {
    2761             ClassExpression classExp = (ClassExpression) rightExp;
    2762             className = classExp.getType();
    2763         }
    2764         else {
    2765             throw new RuntimeException(
    2766                 "Right hand side of the instanceof keyworld must be a class name, not: " + rightExp);
    2767         }
    2768         className = checkValidType(className, expression, "Must be a valid type name for an instanceof statement");
    2769         String classInternalName = BytecodeHelper.getClassInternalName(className);
    2770         cv.visitTypeInsn(INSTANCEOF, classInternalName);
    2771     }
    2772
    2773     /***
    2774      * @return true if the given argument expression requires the stack, in
    2775      *         which case the arguments are evaluated first, stored in the
    2776      *         variable stack and then reloaded to make a method call
    2777      */
    2778     protected boolean argumentsUseStack(Expression arguments) {
    2779         return arguments instanceof TupleExpression || arguments instanceof ClosureExpression;
    2780     }
    2781
    2782     /***
    2783      * @return true if the given expression represents a non-static field
    2784      */
    2785     protected boolean isNonStaticField(Expression expression) {
    2786         FieldNode field = null;
    2787         if (expression instanceof VariableExpression) {
    2788             VariableExpression varExp = (VariableExpression) expression;
    2789             field = classNode.getField(varExp.getVariable());
    2790         }
    2791         else if (expression instanceof FieldExpression) {
    2792             FieldExpression fieldExp = (FieldExpression) expression;
    2793             field = classNode.getField(fieldExp.getFieldName());
    2794         }
    2795         else if (expression instanceof PropertyExpression) {
    2796             PropertyExpression fieldExp = (PropertyExpression) expression;
    2797             field = classNode.getField(fieldExp.getProperty());
    2798         }
    2799         if (field != null) {
    2800             return !field.isStatic();
    2801         }
    2802         return false;
    2803     }
    2804
    2805     protected boolean isThisExpression(Expression expression) {
    2806         if (expression instanceof VariableExpression) {
    2807             VariableExpression varExp = (VariableExpression) expression;
    2808             return varExp.getVariable().equals("this");
    2809         }
    2810         return false;
    2811     }
    2812
    2813     /***
    2814      * For assignment expressions, return a safe expression for the LHS we can use
    2815      * to return the value
    2816      */
    2817     protected Expression createReturnLHSExpression(Expression expression) {
    2818         if (expression instanceof BinaryExpression) {
    2819             BinaryExpression binExpr = (BinaryExpression) expression;
    2820             if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
    2821                 return createReusableExpression(binExpr.getLeftExpression());
    2822             }
    2823         }
    2824         return null;
    2825     }
    2826
    2827     protected Expression createReusableExpression(Expression expression) {
    2828         ExpressionTransformer transformer = new ExpressionTransformer() {
    2829             public Expression transform(Expression expression) {
    2830                 if (expression instanceof PostfixExpression) {
    2831                     PostfixExpression postfixExp = (PostfixExpression) expression;
    2832                     return postfixExp.getExpression();
    2833                 }
    2834                 else if (expression instanceof PrefixExpression) {
    2835                     PrefixExpression prefixExp = (PrefixExpression) expression;
    2836                     return prefixExp.getExpression();
    2837                 }
    2838                 return expression;
    2839             }
    2840         };
    2841
    2842         // could just be a postfix / prefix expression or nested inside some other expression
    2843         return transformer.transform(expression.transformExpression(transformer));
    2844     }
    2845
    2846     protected boolean comparisonExpression(Expression expression) {
    2847         if (expression instanceof BinaryExpression) {
    2848             BinaryExpression binExpr = (BinaryExpression) expression;
    2849             switch (binExpr.getOperation().getType()) {
    2850                 case Types.COMPARE_EQUAL :
    2851                 case Types.MATCH_REGEX :
    2852                 case Types.COMPARE_GREATER_THAN :
    2853                 case Types.COMPARE_GREATER_THAN_EQUAL :
    2854                 case Types.COMPARE_LESS_THAN :
    2855                 case Types.COMPARE_LESS_THAN_EQUAL :
    2856                 case Types.COMPARE_IDENTICAL :
    2857                 case Types.COMPARE_NOT_EQUAL :
    2858                 case Types.KEYWORD_INSTANCEOF :
    2859                     return true;
    2860             }
    2861         }
    2862         else if (expression instanceof BooleanExpression) {
    2863             return true;
    2864         }
    2865         return false;
    2866     }
    2867
    2868     protected void onLineNumber(ASTNode statement) {
    2869         int number = statement.getLineNumber();
    2870         if (number >= 0 && cv != null) {
    2871             Label l = new Label();
    2872             cv.visitLabel(l);
    2873             cv.visitLineNumber(number, l);
    2874         }
    2875     }
    2876
    2877     protected VariableScope getVariableScope() {
    2878         if (variableScope == null) {
    2879             if (methodNode != null) {
    2880                 // if we're a closure method we'll have our variable scope already created
    2881                 variableScope = methodNode.getVariableScope();
    2882                 if (variableScope == null) {
    2883                     variableScope = new VariableScope();
    2884                     methodNode.setVariableScope(variableScope);
    2885                     VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
    2886                     visitor.setParameters(methodNode.getParameters());
    2887                     Statement code = methodNode.getCode();
    2888                     if (code != null) {
    2889                         code.visit(visitor);
    2890                     }
    2891                 }
    2892                 addFieldsToVisitor(variableScope);
    2893             }
    2894             else if (constructorNode != null) {
    2895                 variableScope = new VariableScope();
    2896                 constructorNode.setVariableScope(variableScope);
    2897                 VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
    2898                 visitor.setParameters(constructorNode.getParameters());
    2899                 Statement code = constructorNode.getCode();
    2900                 if (code != null) {
    2901                     code.visit(visitor);
    2902                 }
    2903                 addFieldsToVisitor(variableScope);
    2904             }
    2905             else {
    2906                 throw new RuntimeException("Can't create a variable scope outside of a method or constructor");
    2907             }
    2908         }
    2909         return variableScope;
    2910     }
    2911
    2912     /***
    2913      * @return a list of parameters for each local variable which needs to be
    2914      *         passed into a closure
    2915      */
    2916     protected Parameter[] getClosureSharedVariables(ClosureExpression expression) {
    2917         List vars = new ArrayList();
    2918
    2919         //
    2920         // First up, get the scopes for outside and inside the closure.
    2921         // The inner scope must cover all nested closures, as well, as
    2922         // everything that will be needed must be imported.
    2923
    2924         VariableScope outerScope = getVariableScope().createRecursiveParentScope();
    2925         VariableScope innerScope = expression.getVariableScope();
    2926         if (innerScope == null) {
    2927             System.out.println(
    2928                 "No variable scope for: " + expression + " method: " + methodNode + " constructor: " + constructorNode);
    2929             innerScope = new VariableScope(getVariableScope());
    2930         }
    2931         else {
    2932             innerScope = innerScope.createRecursiveChildScope();
    2933         }
    2934
    2935
    2936         //
    2937         // DeclaredVariables include any name that was assigned to within
    2938         // the scope.  ReferencedVariables include any name that was read
    2939         // from within the scope.  We get the sets from each and must piece
    2940         // together the stack variable import list for the closure.  Note
    2941         // that we don't worry about field variables here, as we don't have
    2942         // to do anything special with them.  Stack variables, on the other
    2943         // hand, have to be wrapped up in References for use.
    2944
    2945         Set outerDecls = outerScope.getDeclaredVariables();
    2946         Set outerRefs  = outerScope.getReferencedVariables();
    2947         Set innerDecls = innerScope.getDeclaredVariables();
    2948         Set innerRefs  = innerScope.getReferencedVariables();
    2949
    2950
    2951         //
    2952         // So, we care about any name referenced in the closure UNLESS:
    2953         //   1) it's not declared in the outer context;
    2954         //   2) it's a parameter;
    2955         //   3) it's a field in the context class that isn't overridden
    2956         //      by a stack variable in the outer context.
    2957         //
    2958         // BUG: We don't actually have the necessary information to do
    2959         //      this right!  The outer declarations don't distinguish
    2960         //      between assignments and variable declarations.  Therefore
    2961         //      we can't tell when field variables have been overridden
    2962         //      by stack variables in the outer context.  This must
    2963         //      be fixed!
    2964
    2965         Set varSet = new HashSet();
    2966         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
    2967             String var = (String) iter.next();
    2968             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
    2969             if (outerDecls.contains(var) && (isNotFieldOfOutermostClass(var))) {
    2970                 String type = getVariableType(var);
    2971                 vars.add(new Parameter(type, var));
    2972                 varSet.add(var);
    2973             }
    2974         }
    2975         for (Iterator iter = outerRefs.iterator(); iter.hasNext();) {
    2976             String var = (String) iter.next();
    2977             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
    2978             if (innerDecls.contains(var) && (isNotFieldOfOutermostClass(var)) && !varSet.contains(var)) {
    2979                 String type = getVariableType(var);
    2980                 vars.add(new Parameter(type, var));
    2981             }
    2982         }
    2983
    2984
    2985         Parameter[] answer = new Parameter[vars.size()];
    2986         vars.toArray(answer);
    2987         return answer;
    2988     }
    2989
    2990     protected boolean isNotFieldOfOutermostClass(String var) {
    2991         //return classNode.getField(var) == null || isInnerClass();
    2992         return getOutermostClass().getField(var) == null;
    2993     }
    2994
    2995     protected void findMutableVariables() {
    2996         /*
    2997         VariableScopeCodeVisitor outerVisitor = new VariableScopeCodeVisitor(true);
    2998         node.getCode().visit(outerVisitor);
    2999
    3000         addFieldsToVisitor(outerVisitor);
    3001
    3002         VariableScopeCodeVisitor innerVisitor = outerVisitor.getClosureVisitor();
    3003         */
    3004         VariableScope outerScope = getVariableScope();
    3005
    3006         // lets create a scope concatenating all the closure expressions
    3007         VariableScope innerScope = outerScope.createCompositeChildScope();
    3008
    3009         Set outerDecls = outerScope.getDeclaredVariables();
    3010         Set outerRefs = outerScope.getReferencedVariables();
    3011         Set innerDecls = innerScope.getDeclaredVariables();
    3012         Set innerRefs = innerScope.getReferencedVariables();
    3013
    3014         mutableVars.clear();
    3015
    3016         for (Iterator iter = innerDecls.iterator(); iter.hasNext();) {
    3017             String var = (String) iter.next();
    3018             if ((outerDecls.contains(var) || outerRefs.contains(var)) && classNode.getField(var) == null) {
    3019                 mutableVars.add(var);
    3020             }
    3021         }
    3022
    3023         // we may call the closure twice and modify the variable in the outer scope
    3024         // so for now lets assume that all variables are mutable
    3025         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
    3026             String var = (String) iter.next();
    3027             if (outerDecls.contains(var) && classNode.getField(var) == null) {
    3028                 mutableVars.add(var);
    3029             }
    3030         }
    3031
    3032         //                System.out.println();
    3033         //                System.out.println("method: " + methodNode + " classNode: " + classNode);
    3034         //                System.out.println("child scopes: " + outerScope.getChildren());
    3035         //                System.out.println("outerDecls: " + outerDecls);
    3036         //                System.out.println("outerRefs: " + outerRefs);
    3037         //                System.out.println("innerDecls: " + innerDecls);
    3038         //                System.out.println("innerRefs: " + innerRefs);
    3039     }
    3040
    3041     protected void addFieldsToVisitor(VariableScope scope) {
    3042         for (Iterator iter = classNode.getFields().iterator(); iter.hasNext();) {
    3043             FieldNode field = (FieldNode) iter.next();
    3044             String name = field.getName();
    3045
    3046             scope.getDeclaredVariables().add(name);
    3047             scope.getReferencedVariables().add(name);
    3048         }
    3049     }
    3050
    3051     private boolean isInnerClass() {
    3052         return classNode instanceof InnerClassNode;
    3053     }
    3054
    3055     protected String getVariableType(String name) {
    3056         Variable variable = (Variable) variableStack.get(name);
    3057         if (variable != null) {
    3058             return variable.getTypeName();
    3059         }
    3060         return null;
    3061     }
    3062
    3063     protected void resetVariableStack(Parameter[] parameters) {
    3064         lastVariableIndex = -1;
    3065         variableStack.clear();
    3066
    3067         scope = null;
    3068         pushBlockScope();
    3069
    3070         // lets push this onto the stack
    3071         definingParameters = true;
    3072         if (!isStaticMethod()) {
    3073             defineVariable("this", classNode.getName()).getIndex();
    3074         } // now lets create indices for the parameteres
    3075         for (int i = 0; i < parameters.length; i++) {
    3076             Parameter parameter = parameters[i];
    3077             String type = parameter.getType();
    3078             int idx = defineVariable(parameter.getName(), type).getIndex();
    3079             if (BytecodeHelper.isPrimitiveType(type)) {
    3080                 helper.load(type, idx);
    3081                 helper.box(type);
    3082                 cv.visitVarInsn(ASTORE, idx);
    3083             }
    3084         }
    3085         definingParameters = false;
    3086     }
    3087
    3088     protected void popScope() {
    3089         int lastID = scope.getLastVariableIndex();
    3090
    3091         List removeKeys = new ArrayList();
    3092         for (Iterator iter = variableStack.entrySet().iterator(); iter.hasNext();) {
    3093             Map.Entry entry = (Map.Entry) iter.next();
    3094             String name = (String) entry.getKey();
    3095             Variable value = (Variable) entry.getValue();
    3096             if (value.getIndex() >= lastID) {
    3097                 removeKeys.add(name);
    3098             }
    3099         }
    3100         for (Iterator iter = removeKeys.iterator(); iter.hasNext();) {
    3101             variableStack.remove(iter.next());
    3102         }
    3103         /*
    3104                */
    3105         scope = scope.getParent();
    3106     }
    3107
    3108
    3109     protected void pushBlockScope() {
    3110         pushBlockScope(true, true);
    3111     }
    3112
    3113     protected void pushBlockScope(boolean canContinue, boolean canBreak) {
    3114         scope = new BlockScope(scope);
    3115         scope.setContinueLabel(canContinue ? new Label() : null);
    3116         scope.setBreakLabel(canBreak? new Label() : null);
    3117         scope.setLastVariableIndex(getNextVariableID());
    3118     }
    3119
    3120     /***
    3121      * Defines the given variable in scope and assigns it to the stack
    3122      */
    3123     protected Variable defineVariable(String name, String type) {
    3124         return defineVariable(name, type, true);
    3125     }
    3126
    3127     protected Variable defineVariable(String name, String type, boolean define) {
    3128         return defineVariable(name, new Type(type), define);
    3129     }
    3130
    3131     private Variable defineVariable(String name, Type type, boolean define) {
    3132         Variable answer = (Variable) variableStack.get(name);
    3133         if (answer == null) {
    3134             lastVariableIndex = getNextVariableID();
    3135             answer = new Variable(lastVariableIndex, type, name);
    3136             if (mutableVars.contains(name)) {
    3137                 answer.setHolder(true);
    3138             }
    3139             variableStack.put(name, answer);
    3140             if (define) {
    3141                 if (definingParameters) {
    3142                     if (answer.isHolder()) {
    3143                         cv.visitTypeInsn(NEW, "groovy/lang/Reference"); // br todo to associate a label with the variable
    3144                         cv.visitInsn(DUP);
    3145                         cv.visitVarInsn(ALOAD, lastVariableIndex);
    3146                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V"); // br wrap the param in a ref
    3147                         cv.visitVarInsn(ASTORE, lastVariableIndex);
    3148                     }
    3149                 }
    3150                 else {
    3151                     // using new variable inside a comparison expression
    3152                     // so lets initialize it too
    3153                     if (answer.isHolder() && !isInScriptBody()) { // br doesn't seem to need different treatment, in script or not
    3154                         //cv.visitVarInsn(ASTORE, lastVariableIndex + 1); // I might need this to set the reference value
    3155
    3156                         cv.visitTypeInsn(NEW, "groovy/lang/Reference");
    3157                         cv.visitInsn(DUP);
    3158                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "()V");
    3159
    3160                         cv.visitVarInsn(ASTORE, lastVariableIndex);
    3161                         //cv.visitVarInsn(ALOAD, idx + 1);
    3162                     }
    3163                     else {
    3164                         if (!leftHandExpression) {
    3165                             cv.visitInsn(ACONST_NULL);
    3166                             cv.visitVarInsn(ASTORE, lastVariableIndex);
    3167                         }
    3168                     }
    3169                 }
    3170             }
    3171         }
    3172         return answer;
    3173     }
    3174
    3175     private int getNextVariableID() {
    3176         return Math.max(lastVariableIndex + 1, variableStack.size());
    3177     }
    3178
    3179     /*** @return true if the given name is a local variable or a field */
    3180     protected boolean isFieldOrVariable(String name) {
    3181         return variableStack.containsKey(name) || classNode.getField(name) != null;
    3182     }
    3183
    3184     protected Type checkValidType(Type type, ASTNode node, String message) {
    3185         if (type.isDynamic()) {
    3186             return type;
    3187         }
    3188         String name = checkValidType(type.getName(), node, message);
    3189         if (type.getName().equals(name)) {
    3190             return type;
    3191         }
    3192         return new Type(name);
    3193     }
    3194
    3195     protected String checkValidType(String type, ASTNode node, String message) {
    3196         if (type.endsWith("[]")) {
    3197             String postfix = "[]";
    3198             String prefix = type.substring(0, type.length() - 2);
    3199             return checkValidType(prefix, node, message) + postfix;
    3200         }
    3201         int idx = type.indexOf('$');
    3202         if (idx > 0) {
    3203             String postfix = type.substring(idx);
    3204             String prefix = type.substring(0, idx);
    3205             return checkValidType(prefix, node, message) + postfix;
    3206         }
    3207         if (BytecodeHelper.isPrimitiveType(type) || "void".equals(type)) {
    3208             return type;
    3209         }
    3210         String original = type;
    3211         type = resolveClassName(type);
    3212         if (type != null) {
    3213             return type;
    3214         }
    3215         throw new MissingClassException(original, node, message + " for class: " + classNode.getName());
    3216     }
    3217
    3218     protected String resolveClassName(String type) {
    3219         return classNode.resolveClassName(type);
    3220     }
    3221
    3222     protected String createVariableName(String type) {
    3223         return "__" + type + (++tempVariableNameCounter);
    3224     }
    3225
    3226     /***
    3227      * @return if the type of the expression can be determined at compile time
    3228      *         then this method returns the type - otherwise null
    3229      */
    3230     protected String getExpressionType(Expression expression) {
    3231         if (comparisonExpression(expression)) {
    3232             return "boolean";
    3233         }
    3234         if (expression instanceof VariableExpression) {
    3235             VariableExpression varExpr = (VariableExpression) expression;
    3236             Variable variable = (Variable) variableStack.get(varExpr.getVariable());
    3237             if (variable != null && !variable.isHolder()) {
    3238                 Type type = variable.getType();
    3239                 if (! type.isDynamic()) {
    3240                     return type.getName();
    3241                 }
    3242             }
    3243         }
    3244         return null;
    3245     }
    3246
    3247     /***
    3248      * @return true if the value is an Integer, a Float, a Long, a Double or a
    3249      *         String .
    3250      */
    3251     protected boolean isPrimitiveFieldType(String type) {
    3252         return type.equals("java.lang.String")
    3253             || type.equals("java.lang.Integer")
    3254             || type.equals("java.lang.Double")
    3255             || type.equals("java.lang.Long")
    3256             || type.equals("java.lang.Float");
    3257     }
    3258
    3259     protected boolean isInClosureConstructor() {
    3260         return constructorNode != null
    3261             && classNode.getOuterClass() != null
    3262             && classNode.getSuperClass().equals(Closure.class.getName());
    3263     }
    3264
    3265     protected boolean isStaticMethod() {
    3266         if (methodNode == null) { // we're in a constructor
    3267             return false;
    3268         }
    3269         return methodNode.isStatic();
    3270     }
    3271
    3272     /***
    3273      * @return loads the given type name
    3274      */
    3275     protected Class loadClass(String name) {
    3276         try {
    3277             CompileUnit compileUnit = getCompileUnit();
    3278             if (compileUnit != null) {
    3279                 return compileUnit.loadClass(name);
    3280             }
    3281             else {
    3282                 throw new ClassGeneratorException("Could not load class: " + name);
    3283             }
    3284         }
    3285         catch (ClassNotFoundException e) {
    3286             throw new ClassGeneratorException("Could not load class: " + name + " reason: " + e, e);
    3287         }
    3288     }
    3289
    3290     protected CompileUnit getCompileUnit() {
    3291         CompileUnit answer = classNode.getCompileUnit();
    3292         if (answer == null) {
    3293             answer = context.getCompileUnit();
    3294         }
    3295         return answer;
    3296     }
    3297 }