View Javadoc

    1   /*
    2    * $Id: AsmClassGenerator2.java,v 1.7 2005/01/11 16:57:13 glaforge 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.*;
    37
    38  import java.lang.reflect.Constructor;
    39  import java.lang.reflect.Field;
    40  import java.lang.reflect.Method;
    41  import java.lang.reflect.Modifier;
    42  import java.util.*;
    43  import java.util.regex.Matcher;
    44  import java.util.logging.Logger;
    45  import java.security.AccessController;
    46  import java.security.PrivilegedAction;
    47
    48  import org.codehaus.groovy.ast.ASTNode;
    49  import org.codehaus.groovy.ast.ClassNode;
    50  import org.codehaus.groovy.ast.CompileUnit;
    51  import org.codehaus.groovy.ast.ConstructorNode;
    52  import org.codehaus.groovy.ast.FieldNode;
    53  import org.codehaus.groovy.ast.GroovyCodeVisitor;
    54  import org.codehaus.groovy.ast.InnerClassNode;
    55  import org.codehaus.groovy.ast.MethodNode;
    56  import org.codehaus.groovy.ast.Parameter;
    57  import org.codehaus.groovy.ast.PropertyNode;
    58  import org.codehaus.groovy.ast.Type;
    59  import org.codehaus.groovy.ast.VariableScope;
    60  import org.codehaus.groovy.ast.expr.ArgumentListExpression;
    61  import org.codehaus.groovy.ast.expr.ArrayExpression;
    62  import org.codehaus.groovy.ast.expr.BinaryExpression;
    63  import org.codehaus.groovy.ast.expr.BooleanExpression;
    64  import org.codehaus.groovy.ast.expr.CastExpression;
    65  import org.codehaus.groovy.ast.expr.ClassExpression;
    66  import org.codehaus.groovy.ast.expr.ClosureExpression;
    67  import org.codehaus.groovy.ast.expr.ConstantExpression;
    68  import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
    69  import org.codehaus.groovy.ast.expr.Expression;
    70  import org.codehaus.groovy.ast.expr.ExpressionTransformer;
    71  import org.codehaus.groovy.ast.expr.FieldExpression;
    72  import org.codehaus.groovy.ast.expr.GStringExpression;
    73  import org.codehaus.groovy.ast.expr.ListExpression;
    74  import org.codehaus.groovy.ast.expr.MapEntryExpression;
    75  import org.codehaus.groovy.ast.expr.MapExpression;
    76  import org.codehaus.groovy.ast.expr.MethodCallExpression;
    77  import org.codehaus.groovy.ast.expr.NegationExpression;
    78  import org.codehaus.groovy.ast.expr.NotExpression;
    79  import org.codehaus.groovy.ast.expr.PostfixExpression;
    80  import org.codehaus.groovy.ast.expr.PrefixExpression;
    81  import org.codehaus.groovy.ast.expr.PropertyExpression;
    82  import org.codehaus.groovy.ast.expr.RangeExpression;
    83  import org.codehaus.groovy.ast.expr.RegexExpression;
    84  import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
    85  import org.codehaus.groovy.ast.expr.TernaryExpression;
    86  import org.codehaus.groovy.ast.expr.TupleExpression;
    87  import org.codehaus.groovy.ast.expr.VariableExpression;
    88  import org.codehaus.groovy.ast.stmt.AssertStatement;
    89  import org.codehaus.groovy.ast.stmt.BlockStatement;
    90  import org.codehaus.groovy.ast.stmt.BreakStatement;
    91  import org.codehaus.groovy.ast.stmt.CaseStatement;
    92  import org.codehaus.groovy.ast.stmt.CatchStatement;
    93  import org.codehaus.groovy.ast.stmt.ContinueStatement;
    94  import org.codehaus.groovy.ast.stmt.DoWhileStatement;
    95  import org.codehaus.groovy.ast.stmt.ExpressionStatement;
    96  import org.codehaus.groovy.ast.stmt.ForStatement;
    97  import org.codehaus.groovy.ast.stmt.IfStatement;
    98  import org.codehaus.groovy.ast.stmt.ReturnStatement;
    99  import org.codehaus.groovy.ast.stmt.Statement;
    100 import org.codehaus.groovy.ast.stmt.SwitchStatement;
    101 import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
    102 import org.codehaus.groovy.ast.stmt.ThrowStatement;
    103 import org.codehaus.groovy.ast.stmt.TryCatchStatement;
    104 import org.codehaus.groovy.ast.stmt.WhileStatement;
    105 import org.codehaus.groovy.runtime.DefaultGroovyMethods;
    106 import org.codehaus.groovy.runtime.InvokerHelper;
    107 import org.codehaus.groovy.runtime.RegexSupport;
    108 import org.codehaus.groovy.syntax.Token;
    109 import org.codehaus.groovy.syntax.Types;
    110 import org.codehaus.groovy.syntax.SyntaxException;
    111 import org.codehaus.groovy.syntax.parser.RuntimeParserException;
    112 import org.objectweb.asm.ClassVisitor;
    113 import org.objectweb.asm.CodeVisitor;
    114 import org.objectweb.asm.Label;
    115 import org.objectweb.asm.ClassWriter;
    116
    117 /***
    118  * Generates Java class versions of Groovy classes using ASM
    119  * Based on AsmClassGenerator 1.6.
    120  * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    121  * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
    122  *
    123  * @version $Revision: 1.7 $
    124  */
    125 public class AsmClassGenerator2 extends ClassGenerator {
    126
    127     private Logger log = Logger.getLogger(getClass().getName());
    128
    129     private ClassVisitor cw;
    130     private CodeVisitor cv;
    131     private GeneratorContext context;
    132
    133     private String sourceFile;
    134
    135     // current class details
    136     private ClassNode classNode;
    137     private ClassNode outermostClass;
    138     private String internalClassName;
    139     private String internalBaseClassName;
    140
    141     /*** maps the variable names to the JVM indices */
    142     private Map variableStack = new HashMap();
    143
    144     /*** have we output a return statement yet */
    145     private boolean outputReturn;
    146
    147     /*** are we on the left or right of an expression */
    148     private boolean leftHandExpression;
    149
    150     // cached values
    151     MethodCaller invokeMethodMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeMethod");
    152     MethodCaller invokeMethodSafeMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeMethodSafe");
    153     MethodCaller invokeStaticMethodMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeStaticMethod");
    154     MethodCaller invokeConstructorMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeConstructor");
    155     MethodCaller invokeConstructorOfMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeConstructorOf");
    156     MethodCaller invokeNoArgumentsConstructorOf = MethodCaller.newStatic(InvokerHelper.class, "invokeNoArgumentsConstructorOf");
    157     MethodCaller invokeClosureMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeClosure");
    158     MethodCaller invokeSuperMethodMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeSuperMethod");
    159     MethodCaller invokeNoArgumentsMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeNoArgumentsMethod");
    160     MethodCaller invokeStaticNoArgumentsMethod = MethodCaller.newStatic(InvokerHelper.class, "invokeStaticNoArgumentsMethod");
    161
    162     MethodCaller asIntMethod = MethodCaller.newStatic(InvokerHelper.class, "asInt");
    163     MethodCaller asTypeMethod = MethodCaller.newStatic(InvokerHelper.class, "asType");
    164     MethodCaller getPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "getProperty");
    165     MethodCaller getPropertySafeMethod = MethodCaller.newStatic(InvokerHelper.class, "getPropertySafe");
    166     MethodCaller setPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "setProperty");
    167     MethodCaller setPropertyMethod2 = MethodCaller.newStatic(InvokerHelper.class, "setProperty2");
    168     MethodCaller setPropertySafeMethod2 = MethodCaller.newStatic(InvokerHelper.class, "setPropertySafe2");
    169     MethodCaller getGroovyObjectPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "getGroovyObjectProperty");
    170     MethodCaller setGroovyObjectPropertyMethod = MethodCaller.newStatic(InvokerHelper.class, "setGroovyObjectProperty");
    171     MethodCaller asIteratorMethod = MethodCaller.newStatic(InvokerHelper.class, "asIterator");
    172     MethodCaller asBool = MethodCaller.newStatic(InvokerHelper.class, "asBool");
    173     MethodCaller notBoolean = MethodCaller.newStatic(InvokerHelper.class, "notBoolean");
    174     MethodCaller notObject = MethodCaller.newStatic(InvokerHelper.class, "notObject");
    175     MethodCaller regexPattern = MethodCaller.newStatic(InvokerHelper.class, "regexPattern");
    176     MethodCaller negation = MethodCaller.newStatic(InvokerHelper.class, "negate");
    177     MethodCaller convertPrimitiveArray = MethodCaller.newStatic(InvokerHelper.class, "convertPrimitiveArray");
    178     MethodCaller convertToPrimitiveArray = MethodCaller.newStatic(InvokerHelper.class, "convertToPrimitiveArray");
    179
    180     MethodCaller compareIdenticalMethod = MethodCaller.newStatic(InvokerHelper.class, "compareIdentical");
    181     MethodCaller compareEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareEqual");
    182     MethodCaller compareNotEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareNotEqual");
    183     MethodCaller compareToMethod = MethodCaller.newStatic(InvokerHelper.class, "compareTo");
    184     MethodCaller findRegexMethod = MethodCaller.newStatic(InvokerHelper.class, "findRegex");
    185     MethodCaller matchRegexMethod = MethodCaller.newStatic(InvokerHelper.class, "matchRegex");
    186     MethodCaller compareLessThanMethod = MethodCaller.newStatic(InvokerHelper.class, "compareLessThan");
    187     MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareLessThanEqual");
    188     MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(InvokerHelper.class, "compareGreaterThan");
    189     MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(InvokerHelper.class, "compareGreaterThanEqual");
    190     MethodCaller isCaseMethod = MethodCaller.newStatic(InvokerHelper.class, "isCase");
    191
    192     MethodCaller createListMethod = MethodCaller.newStatic(InvokerHelper.class, "createList");
    193     MethodCaller createTupleMethod = MethodCaller.newStatic(InvokerHelper.class, "createTuple");
    194     MethodCaller createMapMethod = MethodCaller.newStatic(InvokerHelper.class, "createMap");
    195     MethodCaller createRangeMethod = MethodCaller.newStatic(InvokerHelper.class, "createRange");
    196
    197     MethodCaller assertFailedMethod = MethodCaller.newStatic(InvokerHelper.class, "assertFailed");
    198
    199     MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
    200     MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
    201
    202
    203     // current stack index
    204     private int lastVariableIndex;
    205     private static int tempVariableNameCounter;
    206
    207     // exception blocks list
    208     private List exceptionBlocks = new ArrayList();
    209
    210     private boolean definingParameters;
    211     private Set syntheticStaticFields = new HashSet();
    212     private Set mutableVars = new HashSet();
    213     private boolean passingClosureParams;
    214
    215     private ConstructorNode constructorNode;
    216     private MethodNode methodNode;
    217     //private PropertyNode propertyNode;
    218     private BlockScope scope;
    219     private BytecodeHelper helper = new BytecodeHelper(null);
    220
    221     private VariableScope variableScope;
    222     public static final boolean CREATE_DEBUG_INFO = false;
    223     private static final boolean MARK_START = true;
    224
    225     public static final String EB_SWITCH_NAME = "static.dispatching";
    226     public boolean ENABLE_EARLY_BINDING;
    227     {    //
    228         String ebSwitch = (String) AccessController.doPrivileged(new PrivilegedAction() {
    229             public Object run() {
    230                 return System.getProperty(EB_SWITCH_NAME, "false"); // set default to true if early binding is on by default.
    231             }
    232         });
    233         //System.out.println("ebSwitch = " + ebSwitch);
    234         if (ebSwitch.equals("true")) {
    235             ENABLE_EARLY_BINDING  = true;
    236         }
    237         else if (ebSwitch.equals("false")) {
    238             ENABLE_EARLY_BINDING  = false;
    239         }
    240         else {
    241             ENABLE_EARLY_BINDING  = false;
    242             log.warning("The value of system property " + EB_SWITCH_NAME + " is not recognized. Late dispatching is assumed. ");
    243         }
    244     }
    245     public static final boolean ASM_DEBUG = false; // add marker in the bytecode to show source-byecode relationship
    246     private int lineNumber = -1;
    247     private int columnNumber = -1;
    248     private ASTNode currentASTNode = null;
    249
    250     private DummyClassGenerator dummyGen = null;
    251     private ClassWriter dummyClassWriter = null;
    252
    253     public AsmClassGenerator2(
    254         GeneratorContext context,
    255         ClassVisitor classVisitor,
    256         ClassLoader classLoader,
    257         String sourceFile) {
    258         super(classLoader);
    259         this.context = context;
    260         this.cw = classVisitor;
    261         this.sourceFile = sourceFile;
    262
    263         this.dummyClassWriter = new ClassWriter(true);
    264         dummyGen  = new DummyClassGenerator(context, dummyClassWriter, classLoader, sourceFile);
    265
    266     }
    267
    268     // GroovyClassVisitor interface
    269     //-------------------------------------------------------------------------
    270     public void visitClass(ClassNode classNode) {
    271         // todo to be tested
    272         // createDummyClass(classNode);
    273
    274         try {
    275             syntheticStaticFields.clear();
    276             this.classNode = classNode;
    277             this.outermostClass = null;
    278             this.internalClassName = BytecodeHelper.getClassInternalName(classNode.getName());
    279
    280             //System.out.println("Generating class: " + classNode.getName());
    281
    282             // lets check that the classes are all valid
    283             classNode.setSuperClass(checkValidType(classNode.getSuperClass(), classNode, "Must be a valid base class"));
    284             String[] interfaces = classNode.getInterfaces();
    285             for (int i = 0; i < interfaces.length; i++ ) {
    286                 interfaces[i] = checkValidType(interfaces[i], classNode, "Must be a valid interface name");
    287             }
    288
    289             this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
    290
    291             cw.visit(
    292                 asmJDKVersion,
    293                 classNode.getModifiers(),
    294                 internalClassName,
    295                 internalBaseClassName,
    296                 BytecodeHelper.getClassInternalNames(classNode.getInterfaces()),
    297                 sourceFile);
    298
    299             // set the optional enclosing method attribute of the current inner class
    300 //          br comment out once Groovy uses the latest CVS HEAD of ASM
    301 //            MethodNode enclosingMethod = classNode.getEnclosingMethod();
    302 //            String ownerName = BytecodeHelper.getClassInternalName(enclosingMethod.getDeclaringClass().getName());
    303 //            String descriptor = BytecodeHelper.getMethodDescriptor(enclosingMethod.getReturnType(), enclosingMethod.getParameters());
    304 //            EnclosingMethodAttribute attr = new EnclosingMethodAttribute(ownerName,enclosingMethod.getName(),descriptor);
    305 //            cw.visitAttribute(attr);
    306
    307             classNode.visitContents(this);
    308
    309             createSyntheticStaticFields();
    310
    311             for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
    312                 ClassNode innerClass = (ClassNode) iter.next();
    313                 String innerClassName = innerClass.getName();
    314                 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
    315                 String outerClassName = internalClassName; // default for inner classes
    316                 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
    317                 if (enclosingMethod != null) {
    318                     // local inner classes do not specify the outer class name
    319                     outerClassName = null;
    320                 }
    321                 cw.visitInnerClass(
    322                     innerClassInternalName,
    323                     outerClassName,
    324                     innerClassName,
    325                     innerClass.getModifiers());
    326             }
    327 // br TODO an inner class should have an entry of itself
    328             cw.visitEnd();
    329         }
    330         catch (GroovyRuntimeException e) {
    331             e.setModule(classNode.getModule());
    332             throw e;
    333         }
    334     }
    335
    336     // create a surrogate class that represents the classNode
    337     // the surrogate has the "face" of the real class. It's used for
    338     // type resolving "this"
    339     private void createDummyClass(ClassNode classNode) {
    340         dummyGen.visitClass(classNode);
    341         byte[] code = dummyClassWriter.toByteArray();
    342
    343         ClassLoader parentLoader = getClass().getClassLoader();
    344         GroovyClassLoader groovyLoader = new GroovyClassLoader(parentLoader);
    345         Class theClass = groovyLoader.defineClass(classNode.getName(), code);
    346
    347         if (theClass != null) {
    348             classCache.put(classNode.getName(), theClass);
    349         }
    350     }
    351
    352     public void visitConstructor(ConstructorNode node) {
    353         // creates a MethodWriter for the (implicit) constructor
    354         //String methodType = Type.getMethodDescriptor(VOID_TYPE, )
    355
    356         this.constructorNode = node;
    357         this.methodNode = null;
    358         this.variableScope = null;
    359
    360         visitParameters(node, node.getParameters());
    361
    362         String methodType = BytecodeHelper.getMethodDescriptor("void", node.getParameters());
    363         cv = cw.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
    364         helper = new BytecodeHelper(cv);
    365
    366         findMutableVariables();
    367         resetVariableStack(node.getParameters());
    368
    369         Statement code = node.getCode();
    370         if (code == null || !firstStatementIsSuperInit(code)) {
    371             // invokes the super class constructor
    372             cv.visitVarInsn(ALOAD, 0);
    373             cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "()V");
    374         }
    375         if (code != null) {
    376             code.visit(this);
    377         }
    378
    379         cv.visitInsn(RETURN);
    380         cv.visitMaxs(0, 0);
    381     }
    382
    383     public void visitMethod(MethodNode node) {
    384         //System.out.println("Visiting method: " + node.getName() + " with
    385         // return type: " + node.getReturnType());
    386         this.constructorNode = null;
    387         this.methodNode = node;
    388         this.variableScope = null;
    389
    390         visitParameters(node, node.getParameters());
    391         node.setReturnType(checkValidType(node.getReturnType(), node, "Must be a valid return type"));
    392
    393         String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
    394         cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
    395         Label labelStart = new Label();
    396         cv.visitLabel(labelStart);
    397         helper = new BytecodeHelper(cv);
    398
    399         findMutableVariables();
    400         resetVariableStack(node.getParameters());
    401
    402
    403         outputReturn = false;
    404
    405         node.getCode().visit(this);
    406
    407         if (!outputReturn) {
    408             cv.visitInsn(RETURN);
    409         }
    410
    411         // lets do all the exception blocks
    412         for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) {
    413             Runnable runnable = (Runnable) iter.next();
    414             runnable.run();
    415         }
    416         exceptionBlocks.clear();
    417
    418         Label labelEnd = new Label();
    419         cv.visitLabel(labelEnd);
    420
    421         // br experiment with local var table so debuggers can retrieve variable names
    422         if (CREATE_DEBUG_INFO) {
    423             Set vars = this.variableStack.keySet();
    424             for (Iterator iterator = vars.iterator(); iterator.hasNext();) {
    425                 String varName = (String) iterator.next();
    426                 Variable v = (Variable)variableStack.get(varName);
    427                 String type = v.getTypeName();
    428                 type = BytecodeHelper.getTypeDescription(type);
    429                 Label start = v.getStartLabel() != null ? v.getStartLabel() : labelStart;
    430                 Label end = v.getEndLabel() != null ? v.getEndLabel() : labelEnd;
    431                 cv.visitLocalVariable(varName, type, start, end, v.getIndex());
    432             }
    433         }
    434         cv.visitMaxs(0, 0);
    435     }
    436
    437     protected void visitParameters(ASTNode node, Parameter[] parameters) {
    438         for (int i = 0, size = parameters.length; i < size; i++ ) {
    439             visitParameter(node, parameters[i]);
    440         }
    441     }
    442
    443     protected void visitParameter(ASTNode node, Parameter parameter) {
    444         if (! parameter.isDynamicType()) {
    445             parameter.setType(checkValidType(parameter.getType(), node, "Must be a valid parameter class"));
    446         }
    447     }
    448
    449     public void visitField(FieldNode fieldNode) {
    450         onLineNumber(fieldNode, "visitField: " + fieldNode.getName());
    451
    452         // lets check that the classes are all valid
    453         fieldNode.setType(checkValidType(fieldNode.getType(), fieldNode, "Must be a valid field class for field: " + fieldNode.getName()));
    454
    455         //System.out.println("Visiting field: " + fieldNode.getName() + " on
    456         // class: " + classNode.getName());
    457
    458         Object fieldValue = null;
    459         Expression expression = fieldNode.getInitialValueExpression();
    460         if (expression instanceof ConstantExpression) {
    461             ConstantExpression constantExp = (ConstantExpression) expression;
    462             Object value = constantExp.getValue();
    463             if (isPrimitiveFieldType(fieldNode.getType())) {
    464                 // lets convert any primitive types
    465                 Class type = null;
    466                 try {
    467                     type = loadClass(fieldNode.getType());
    468                     fieldValue = /*InvokerHelper.*/asType(value, type);
    469                 }
    470                 catch (Exception e) {
    471                     log.warning("Caught unexpected: " + e);
    472                 }
    473             }
    474         }
    475         cw.visitField(
    476             fieldNode.getModifiers(),
    477             fieldNode.getName(),
    478             BytecodeHelper.getTypeDescription(fieldNode.getType()),
    479             null, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
    480             null);
    481     }
    482
    483     /***
    484      * Creates a getter, setter and field
    485      */
    486     public void visitProperty(PropertyNode statement) {
    487         onLineNumber(statement, "visitProperty:" + statement.getField().getName());
    488         //this.propertyNode = statement;
    489         this.methodNode = null;
    490     }
    491
    492     // GroovyCodeVisitor interface
    493     //-------------------------------------------------------------------------
    494
    495     // Statements
    496     //-------------------------------------------------------------------------
    497
    498     public void visitForLoop(ForStatement loop) {
    499         onLineNumber(loop, "visitForLoop");
    500         Class elemType = null;
    501         if (ENABLE_EARLY_BINDING) {
    502             Expression collectionExp = loop.getCollectionExpression();
    503             collectionExp.resolve(this);
    504             Class cls = collectionExp.getTypeClass();
    505             if (cls != null) {
    506                 if (cls.isArray()) {
    507                     elemType = cls.getComponentType();
    508                     if (elemType != null) {
    509                         Type varType = new Type(elemType.getName());
    510                         loop.setVariableType(varType);
    511                     }
    512                 }
    513                 else if (collectionExp instanceof ListExpression) {
    514                     elemType = ((ListExpression)collectionExp).getComponentTypeClass();
    515                     if (elemType != null) {
    516                         Type varType = new Type(elemType.getName());
    517                         loop.setVariableType(varType);
    518                     }
    519                 }
    520                 else if (collectionExp instanceof RangeExpression) {
    521                     // use the from type class. assuming both from and to are of the same type
    522                     elemType = ((RangeExpression)collectionExp).getFrom().getTypeClass();
    523                     if (elemType != null) {
    524                         Type varType = new Type(elemType.getName());
    525                         loop.setVariableType(varType);
    526                     }
    527                 }
    528             }
    529         }
    530
    531
    532         //
    533         // Declare the loop counter.
    534         Type variableType = checkValidType(loop.getVariableType(), loop, "for loop variable");
    535         Variable variable = defineVariable(loop.getVariable(), variableType, true);
    536
    537         if( isInScriptBody() ) {
    538             variable.setProperty( true );
    539         }
    540
    541
    542         //
    543         // Then initialize the iterator and generate the loop control
    544
    545         loop.getCollectionExpression().visit(this);
    546
    547         asIteratorMethod.call(cv);
    548
    549         final Variable iterTemp = storeInTemp("iterator", "java.util.Iterator");
    550         final int iteratorIdx = iterTemp.getIndex();
    551
    552         // to push scope here allows the iterator available after the loop, such as the i in: for (i in 1..5)
    553         // move it to the top will make the iterator a local var in the for loop.
    554         pushBlockScope();
    555
    556         Label continueLabel = scope.getContinueLabel();
    557         cv.visitJumpInsn(GOTO, continueLabel);
    558         Label label2 = new Label();
    559         cv.visitLabel(label2);
    560
    561         final Class elemClass = elemType;
    562         BytecodeExpression expression = new BytecodeExpression() {
    563             public void visit(GroovyCodeVisitor visitor) {
    564                 cv.visitVarInsn(ALOAD, iteratorIdx);
    565                 iteratorNextMethod.call(cv);
    566             }
    567
    568             protected void resolveType(AsmClassGenerator2 resolver) {
    569                 setTypeClass(elemClass);
    570             }
    571         };
    572
    573         evaluateEqual( BinaryExpression.newAssignmentExpression(loop.getVariable(), expression) );
    574         cv.visitInsn(POP); // br now the evaluateEqual() will leave a value on the stack. pop it.
    575
    576         //
    577         // Generate the loop body
    578
    579         loop.getLoopBlock().visit(this);
    580
    581
    582         //
    583         // Generate the loop tail
    584
    585         cv.visitLabel(continueLabel);
    586         cv.visitVarInsn(ALOAD, iteratorIdx);
    587
    588         iteratorHasNextMethod.call(cv);
    589
    590         cv.visitJumpInsn(IFNE, label2);
    591
    592         cv.visitLabel(scope.getBreakLabel());
    593         popScope();
    594     }
    595
    596     public void visitWhileLoop(WhileStatement loop) {
    597         onLineNumber(loop, "visitWhileLoop");
    598
    599         pushBlockScope();
    600
    601         Label continueLabel = scope.getContinueLabel();
    602
    603         cv.visitJumpInsn(GOTO, continueLabel);
    604         Label l1 = new Label();
    605         cv.visitLabel(l1);
    606
    607         loop.getLoopBlock().visit(this);
    608
    609         cv.visitLabel(continueLabel);
    610
    611         loop.getBooleanExpression().visit(this);
    612
    613         cv.visitJumpInsn(IFNE, l1);
    614
    615         cv.visitLabel(scope.getBreakLabel());
    616         popScope();
    617     }
    618
    619     public void visitDoWhileLoop(DoWhileStatement loop) {
    620         onLineNumber(loop, "visitDoWhileLoop");
    621
    622         pushBlockScope();
    623
    624         Label breakLabel = scope.getBreakLabel();
    625
    626         Label continueLabel = scope.getContinueLabel();
    627         cv.visitLabel(continueLabel);
    628         Label l1 = new Label();
    629
    630         loop.getLoopBlock().visit(this);
    631
    632         cv.visitLabel(l1);
    633
    634         loop.getBooleanExpression().visit(this);
    635
    636         cv.visitJumpInsn(IFNE, continueLabel);
    637
    638         cv.visitLabel(breakLabel);
    639         popScope();
    640     }
    641
    642     public void visitIfElse(IfStatement ifElse) {
    643         onLineNumber(ifElse, "visitIfElse");
    644
    645         ifElse.getBooleanExpression().visit(this);
    646
    647         Label l0 = new Label();
    648         cv.visitJumpInsn(IFEQ, l0);
    649         pushBlockScope(false, false);
    650         ifElse.getIfBlock().visit(this);
    651         popScope();
    652
    653         Label l1 = new Label();
    654         cv.visitJumpInsn(GOTO, l1);
    655         cv.visitLabel(l0);
    656
    657         pushBlockScope(false, false);
    658         ifElse.getElseBlock().visit(this);
    659         cv.visitLabel(l1);
    660         popScope();
    661     }
    662
    663     public void visitTernaryExpression(TernaryExpression expression) {
    664         onLineNumber(expression, "visitTernaryExpression");
    665
    666         expression.getBooleanExpression().visit(this);
    667
    668         Label l0 = new Label();
    669         cv.visitJumpInsn(IFEQ, l0);
    670         expression.getTrueExpression().visit(this);
    671
    672         Label l1 = new Label();
    673         cv.visitJumpInsn(GOTO, l1);
    674         cv.visitLabel(l0);
    675
    676         expression.getFalseExpression().visit(this);
    677         cv.visitLabel(l1);
    678     }
    679
    680     public void visitAssertStatement(AssertStatement statement) {
    681         onLineNumber(statement, "visitAssertStatement");
    682
    683         //System.out.println("Assert: " + statement.getLineNumber() + " for: "
    684         // + statement.getText());
    685
    686         BooleanExpression booleanExpression = statement.getBooleanExpression();
    687         booleanExpression.visit(this);
    688
    689         Label l0 = new Label();
    690         cv.visitJumpInsn(IFEQ, l0);
    691
    692         // do nothing
    693
    694         Label l1 = new Label();
    695         cv.visitJumpInsn(GOTO, l1);
    696         cv.visitLabel(l0);
    697
    698         // push expression string onto stack
    699         String expressionText = booleanExpression.getText();
    700         List list = new ArrayList();
    701         addVariableNames(booleanExpression, list);
    702         if (list.isEmpty()) {
    703             cv.visitLdcInsn(expressionText);
    704         }
    705         else {
    706             boolean first = true;
    707
    708             // lets create a new expression
    709             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
    710             cv.visitInsn(DUP);
    711             cv.visitLdcInsn(expressionText + ". Values: ");
    712
    713             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V");
    714
    715             Variable assertTemp = visitASTOREInTemp("assert");
    716             int tempIndex  = assertTemp.getIndex();
    717
    718             for (Iterator iter = list.iterator(); iter.hasNext();) {
    719                 String name = (String) iter.next();
    720                 String text = name + " = ";
    721                 if (first) {
    722                     first = false;
    723                 }
    724                 else {
    725                     text = ", " + text;
    726                 }
    727
    728                 cv.visitVarInsn(ALOAD, tempIndex);
    729                 cv.visitLdcInsn(text);
    730                 cv.visitMethodInsn(
    731                     INVOKEVIRTUAL,
    732                     "java/lang/StringBuffer",
    733                     "append",
    734                     "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
    735                 cv.visitInsn(POP);
    736
    737                 cv.visitVarInsn(ALOAD, tempIndex);
    738                 new VariableExpression(name).visit(this);
    739                 cv.visitMethodInsn(
    740                     INVOKEVIRTUAL,
    741                     "java/lang/StringBuffer",
    742                     "append",
    743                     "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
    744                 cv.visitInsn(POP);
    745
    746             }
    747             cv.visitVarInsn(ALOAD, tempIndex);
    748             removeVar(assertTemp);
    749         }
    750         // now the optional exception expression
    751         statement.getMessageExpression().visit(this);
    752
    753         assertFailedMethod.call(cv);
    754         cv.visitLabel(l1);
    755     }
    756
    757     private void addVariableNames(Expression expression, List list) {
    758         if (expression instanceof BooleanExpression) {
    759             BooleanExpression boolExp = (BooleanExpression) expression;
    760             addVariableNames(boolExp.getExpression(), list);
    761         }
    762         else if (expression instanceof BinaryExpression) {
    763             BinaryExpression binExp = (BinaryExpression) expression;
    764             addVariableNames(binExp.getLeftExpression(), list);
    765             addVariableNames(binExp.getRightExpression(), list);
    766         }
    767         else if (expression instanceof VariableExpression) {
    768             VariableExpression varExp = (VariableExpression) expression;
    769             list.add(varExp.getVariable());
    770         }
    771     }
    772
    773     public void visitTryCatchFinally(TryCatchStatement statement) {
    774         onLineNumber(statement, "visitTryCatchFinally");
    775 // todo need to add blockscope handling
    776         CatchStatement catchStatement = statement.getCatchStatement(0);
    777
    778         Statement tryStatement = statement.getTryStatement();
    779
    780         if (tryStatement.isEmpty() || catchStatement == null) {
    781             final Label l0 = new Label();
    782             cv.visitLabel(l0);
    783
    784             tryStatement.visit(this);
    785
    786
    787             int index1 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    788             int index2 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    789
    790             final Label l1 = new Label();
    791             cv.visitJumpInsn(JSR, l1);
    792             final Label l2 = new Label();
    793             cv.visitLabel(l2);
    794             final Label l3 = new Label();
    795             cv.visitJumpInsn(GOTO, l3);
    796             final Label l4 = new Label();
    797             cv.visitLabel(l4);
    798             cv.visitVarInsn(ASTORE, index1);
    799             cv.visitJumpInsn(JSR, l1);
    800             final Label l5 = new Label();
    801             cv.visitLabel(l5);
    802             cv.visitVarInsn(ALOAD, index1);
    803             cv.visitInsn(ATHROW);
    804             cv.visitLabel(l1);
    805             cv.visitVarInsn(ASTORE, index2);
    806
    807             statement.getFinallyStatement().visit(this);
    808
    809             cv.visitVarInsn(RET, index2);
    810             cv.visitLabel(l3);
    811
    812             exceptionBlocks.add(new Runnable() {
    813                 public void run() {
    814                     cv.visitTryCatchBlock(l0, l2, l4, null);
    815                     cv.visitTryCatchBlock(l4, l5, l4, null);
    816                 }
    817             });
    818
    819         }
    820         else {
    821             String exceptionVar = catchStatement.getVariable();
    822             String exceptionType =
    823                 checkValidType(catchStatement.getExceptionType(), catchStatement, "in catch statement");
    824
    825             int exceptionIndex = defineVariable(exceptionVar, exceptionType, false).getIndex();
    826             int index2 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    827             int index3 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
    828
    829             final Label l0 = new Label();
    830             cv.visitLabel(l0);
    831
    832             tryStatement.visit(this);
    833
    834             final Label l1 = new Label();
    835             cv.visitLabel(l1);
    836             Label l2 = new Label();
    837             cv.visitJumpInsn(JSR, l2);
    838             final Label l3 = new Label();
    839             cv.visitLabel(l3);
    840             Label l4 = new Label();
    841             cv.visitJumpInsn(GOTO, l4);
    842             final Label l5 = new Label();
    843             cv.visitLabel(l5);
    844
    845             cv.visitVarInsn(ASTORE, exceptionIndex);
    846
    847             if (catchStatement != null) {
    848                 catchStatement.visit(this);
    849             }
    850
    851             cv.visitJumpInsn(JSR, l2);
    852             final Label l6 = new Label();
    853             cv.visitLabel(l6);
    854             cv.visitJumpInsn(GOTO, l4);
    855
    856             final Label l7 = new Label();
    857             cv.visitLabel(l7);
    858             cv.visitVarInsn(ASTORE, index2);
    859             cv.visitJumpInsn(JSR, l2);
    860
    861             final Label l8 = new Label();
    862             cv.visitLabel(l8);
    863             cv.visitVarInsn(ALOAD, index2);
    864             cv.visitInsn(ATHROW);
    865             cv.visitLabel(l2);
    866             cv.visitVarInsn(ASTORE, index3);
    867
    868             statement.getFinallyStatement().visit(this);
    869
    870             cv.visitVarInsn(RET, index3);
    871             cv.visitLabel(l4);
    872
    873             // rest of code goes here...
    874
    875             //final String exceptionTypeInternalName = (catchStatement !=
    876             // null) ?
    877             // getTypeDescription(exceptionType) : null;
    878             final String exceptionTypeInternalName =
    879                 (catchStatement != null) ? BytecodeHelper.getClassInternalName(exceptionType) : null;
    880
    881             exceptionBlocks.add(new Runnable() {
    882                 public void run() {
    883                     cv.visitTryCatchBlock(l0, l1, l5, exceptionTypeInternalName);
    884                     cv.visitTryCatchBlock(l0, l3, l7, null);
    885                     cv.visitTryCatchBlock(l5, l6, l7, null);
    886                     cv.visitTryCatchBlock(l7, l8, l7, null);
    887                 }
    888             });
    889         }
    890     }
    891
    892     private Variable storeInTemp(String name, String type) {
    893         Variable var  = defineVariable(createVariableName(name), type, false);
    894         int varIdx = var.getIndex();
    895         cv.visitVarInsn(ASTORE, varIdx);
    896         if (CREATE_DEBUG_INFO) cv.visitLabel(var.getStartLabel());
    897         return var;
    898     }
    899
    900     public void visitSwitch(SwitchStatement statement) {
    901         onLineNumber(statement, "visitSwitch");
    902
    903         statement.getExpression().visit(this);
    904
    905         // switch does not have a continue label. use its parent's for continue
    906         pushBlockScope(false, true);
    907         //scope.setContinueLabel(scope.getParent().getContinueLabel());
    908
    909
    910         int switchVariableIndex = defineVariable(createVariableName("switch"), "java.lang.Object").getIndex();
    911         cv.visitVarInsn(ASTORE, switchVariableIndex);
    912
    913         List caseStatements = statement.getCaseStatements();
    914         int caseCount = caseStatements.size();
    915         Label[] labels = new Label[caseCount + 1];
    916         for (int i = 0; i < caseCount; i++) {
    917             labels[i] = new Label();
    918         }
    919
    920         int i = 0;
    921         for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
    922             CaseStatement caseStatement = (CaseStatement) iter.next();
    923             visitCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]);
    924         }
    925
    926         statement.getDefaultStatement().visit(this);
    927
    928         cv.visitLabel(scope.getBreakLabel());
    929
    930         popScope();
    931     }
    932
    933     public void visitCaseStatement(CaseStatement statement) {
    934     }
    935
    936     public void visitCaseStatement(
    937         CaseStatement statement,
    938         int switchVariableIndex,
    939         Label thisLabel,
    940         Label nextLabel) {
    941
    942         onLineNumber(statement, "visitCaseStatement");
    943
    944         cv.visitVarInsn(ALOAD, switchVariableIndex);
    945         statement.getExpression().visit(this);
    946
    947         isCaseMethod.call(cv);
    948
    949         Label l0 = new Label();
    950         cv.visitJumpInsn(IFEQ, l0);
    951
    952         cv.visitLabel(thisLabel);
    953
    954         statement.getCode().visit(this);
    955
    956         // now if we don't finish with a break we need to jump past
    957         // the next comparison
    958         if (nextLabel != null) {
    959             cv.visitJumpInsn(GOTO, nextLabel);
    960         }
    961
    962         cv.visitLabel(l0);
    963     }
    964
    965     public void visitBreakStatement(BreakStatement statement) {
    966         onLineNumber(statement, "visitBreakStatement");
    967
    968         Label breakLabel = scope.getBreakLabel();
    969         if (breakLabel != null ) {
    970             cv.visitJumpInsn(GOTO, breakLabel);
    971         } else {
    972             // should warn that break is not allowed in the context.
    973         }
    974     }
    975
    976     public void visitContinueStatement(ContinueStatement statement) {
    977         onLineNumber(statement, "visitContinueStatement");
    978
    979         Label continueLabel = scope.getContinueLabel();
    980         if (continueLabel != null ) {
    981             cv.visitJumpInsn(GOTO, continueLabel);
    982         } else {
    983             // should warn that continue is not allowed in the context.
    984         }
    985     }
    986
    987     public void visitSynchronizedStatement(SynchronizedStatement statement) {
    988         onLineNumber(statement, "visitSynchronizedStatement");
    989
    990         statement.getExpression().visit(this);
    991
    992         int index = defineVariable(createVariableName("synchronized"), "java.lang.Integer").getIndex();
    993
    994         cv.visitVarInsn(ASTORE, index);
    995         cv.visitInsn(MONITORENTER);
    996         final Label l0 = new Label();
    997         cv.visitLabel(l0);
    998
    999         statement.getCode().visit(this);
    1000
    1001         cv.visitVarInsn(ALOAD, index);
    1002         cv.visitInsn(MONITOREXIT);
    1003         final Label l1 = new Label();
    1004         cv.visitJumpInsn(GOTO, l1);
    1005         final Label l2 = new Label();
    1006         cv.visitLabel(l2);
    1007         cv.visitVarInsn(ALOAD, index);
    1008         cv.visitInsn(MONITOREXIT);
    1009         cv.visitInsn(ATHROW);
    1010         cv.visitLabel(l1);
    1011
    1012         exceptionBlocks.add(new Runnable() {
    1013             public void run() {
    1014                 cv.visitTryCatchBlock(l0, l2, l2, null);
    1015             }
    1016         });
    1017     }
    1018
    1019     public void visitThrowStatement(ThrowStatement statement) {
    1020         statement.getExpression().visit(this);
    1021
    1022         // we should infer the type of the exception from the expression
    1023         cv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
    1024
    1025         cv.visitInsn(ATHROW);
    1026     }
    1027
    1028     public void visitReturnStatement(ReturnStatement statement) {
    1029         onLineNumber(statement, "visitReturnStatement");
    1030         String returnType = methodNode.getReturnType();
    1031         if (returnType.equals("void")) {
    1032         	if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) {
    1033                 throwException("Cannot use return statement with an expression on a method that returns void");
    1034         	}
    1035             cv.visitInsn(RETURN);
    1036             outputReturn = true;
    1037             return;
    1038         }
    1039
    1040         Expression expression = statement.getExpression();
    1041         evaluateExpression(expression);
    1042         if (returnType.equals("java.lang.Object") && expression.getType() != null && expression.getType().equals("void")) {
    1043             cv.visitInsn(ACONST_NULL); // cheat the caller
    1044             cv.visitInsn(ARETURN);
    1045         } else {
    1046             //return is based on class type
    1047             //TODO: make work with arrays
    1048             // we may need to cast
    1049             helper.unbox(returnType);
    1050             if (returnType.equals("double")) {
    1051                 cv.visitInsn(DRETURN);
    1052             }
    1053             else if (returnType.equals("float")) {
    1054                 cv.visitInsn(FRETURN);
    1055             }
    1056             else if (returnType.equals("long")) {
    1057                 cv.visitInsn(LRETURN);
    1058             }
    1059             else if (returnType.equals("boolean")) {
    1060                 cv.visitInsn(IRETURN);
    1061             }
    1062             else if (
    1063                     returnType.equals("char")
    1064                     || returnType.equals("byte")
    1065                     || returnType.equals("int")
    1066                     || returnType.equals("short")) { //byte,short,boolean,int are
    1067                 // all IRETURN
    1068                 cv.visitInsn(IRETURN);
    1069             }
    1070             else {
    1071                 doConvertAndCast(returnType, expression);
    1072                 cv.visitInsn(ARETURN);
    1073
    1074                 /*
    1075                 if (c == Boolean.class) {
    1076                 Label l0 = new Label();
    1077                 cv.visitJumpInsn(IFEQ, l0);
    1078                 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
    1079                 cv.visitInsn(ARETURN);
    1080                 cv.visitLabel(l0);
    1081                 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
    1082                 cv.visitInsn(ARETURN);
    1083                 }
    1084                 else {
    1085                 if (isValidTypeForCast(returnType) && !returnType.equals(c.getName())) {
    1086                 doConvertAndCast(returnType, expression);
    1087                 }
    1088                 cv.visitInsn(ARETURN);
    1089                 }
    1090                 */
    1091             }
    1092         }
    1093         outputReturn = true;
    1094     }
    1095
    1096     /***
    1097      * Casts to the given type unless it can be determined that the cast is unnecessary
    1098      */
    1099     protected void doConvertAndCast(String type, Expression expression) {
    1100         String expType = getExpressionType(expression);
    1101         // temp resolution: convert all primitive casting to corresponsing Object type
    1102         if (BytecodeHelper.isPrimitiveType(type)) {
    1103             type = BytecodeHelper.getObjectTypeForPrimitive(type);
    1104         }
    1105         if (isValidTypeForCast(type) && (expType == null || !type.equals(expType))) {
    1106             doConvertAndCast(type);
    1107         }
    1108     }
    1109
    1110     /***
    1111      * @param expression
    1112      */
    1113     protected void evaluateExpression(Expression expression) {
    1114         visitAndAutoboxBoolean(expression);
    1115         //expression.visit(this);
    1116
    1117         Expression assignExpr = createReturnLHSExpression(expression);
    1118         if (assignExpr != null) {
    1119             leftHandExpression = false;
    1120             assignExpr.visit(this);
    1121         }
    1122     }
    1123
    1124     public void visitExpressionStatement(ExpressionStatement statement) {
    1125         onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName());
    1126
    1127         Expression expression = statement.getExpression();
    1128 // disabled in favor of JIT resolving
    1129 //        if (ENABLE_EARLY_BINDING)
    1130 //            expression.resolve(this);
    1131
    1132         visitAndAutoboxBoolean(expression);
    1133
    1134         if (isPopRequired(expression)) {
    1135             cv.visitInsn(POP);
    1136         }
    1137     }
    1138
    1139     // Expressions
    1140     //-------------------------------------------------------------------------
    1141
    1142     public void visitBinaryExpression(BinaryExpression expression) {
    1143         onLineNumber(expression, "visitBinaryExpression: \"" + expression.getOperation().getText() + "\" ");
    1144         switch (expression.getOperation().getType()) {
    1145             case Types.EQUAL : // = assignment
    1146                 evaluateEqual(expression);
    1147                 break;
    1148
    1149             case Types.COMPARE_IDENTICAL : // ===
    1150                 evaluateBinaryExpression(compareIdenticalMethod, expression);
    1151                 break;
    1152
    1153             case Types.COMPARE_EQUAL : // ==
    1154                 evaluateBinaryExpression(compareEqualMethod, expression);
    1155                 break;
    1156
    1157             case Types.COMPARE_NOT_EQUAL :
    1158                 evaluateBinaryExpression(compareNotEqualMethod, expression);
    1159                 break;
    1160
    1161             case Types.COMPARE_TO :
    1162                 evaluateCompareTo(expression);
    1163                 break;
    1164
    1165             case Types.COMPARE_GREATER_THAN :
    1166                 evaluateBinaryExpression(compareGreaterThanMethod, expression);
    1167                 break;
    1168
    1169             case Types.COMPARE_GREATER_THAN_EQUAL :
    1170                 evaluateBinaryExpression(compareGreaterThanEqualMethod, expression);
    1171                 break;
    1172
    1173             case Types.COMPARE_LESS_THAN :
    1174                 evaluateBinaryExpression(compareLessThanMethod, expression);
    1175                 break;
    1176
    1177             case Types.COMPARE_LESS_THAN_EQUAL :
    1178                 evaluateBinaryExpression(compareLessThanEqualMethod, expression);
    1179                 break;
    1180
    1181             case Types.LOGICAL_AND :
    1182                 evaluateLogicalAndExpression(expression);
    1183                 break;
    1184
    1185             case Types.LOGICAL_OR :
    1186                 evaluateLogicalOrExpression(expression);
    1187                 break;
    1188
    1189             case Types.PLUS :
    1190                 {
    1191                     if (ENABLE_EARLY_BINDING) {
    1192                         expression.resolve(this);
    1193                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
    1194                             evaluateBinaryExpression("plus", expression);
    1195                             break;
    1196                         }
    1197                         Expression leftExpression = expression.getLeftExpression();
    1198                         Expression rightExpression = expression.getRightExpression();
    1199                         Class lclass = leftExpression.getTypeClass();
    1200                         Class rclass = rightExpression.getTypeClass();
    1201                         if (lclass == null || rclass == null) {
    1202                             evaluateBinaryExpression("plus", expression);
    1203                             break;
    1204                         }
    1205                         if (lclass == String.class && rclass == String.class) {
    1206 //                            MethodCallExpression call = new MethodCallExpression(
    1207 //                                    leftExpression,
    1208 //                                    "concat",
    1209 //                                    new ArgumentListExpression(new Expression[] {rightExpression}));
    1210 //                            call.setTypeClass(String.class); // must do to avoid excessive resolving
    1211 //                            visitMethodCallExpression(call);
    1212                             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
    1213                             cv.visitInsn(DUP);
    1214                             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
    1215                             load(leftExpression);
    1216                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
    1217                             load(rightExpression);
    1218                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
    1219                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
    1220                         }
    1221                         else if (lclass == String.class && Number.class.isAssignableFrom(rclass) ) {
    1222                             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
    1223                             cv.visitInsn(DUP);
    1224                             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
    1225                             load(leftExpression);
    1226                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
    1227                             load(rightExpression);
    1228                             // will Object.toString() work here?
    1229                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
    1230                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
    1231                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
    1232                         }
    1233                         else if (rclass == String.class && Number.class.isAssignableFrom(lclass) ) {
    1234                             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
    1235                             cv.visitInsn(DUP);
    1236                             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
    1237                             load(leftExpression);
    1238                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
    1239                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
    1240                             load(rightExpression);
    1241                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); // note the arg is object type for safety
    1242                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
    1243                         }
    1244                         else if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
    1245                             // assuming all return boxed version for primitives
    1246                             load(leftExpression);
    1247                             helper.quickUnboxIfNecessary(int.class);
    1248                             load(rightExpression);
    1249                             helper.quickUnboxIfNecessary(int.class);
    1250                             cv.visitInsn(IADD);
    1251                             helper.quickBoxIfNecessary(int.class);
    1252                         }
    1253                         else if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
    1254                             // let's use groovy utilities in the DefaultGroovyMethods
    1255                             load(leftExpression);
    1256                             load(rightExpression);
    1257                             cv.visitMethodInsn(
    1258                                     INVOKESTATIC,
    1259                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1260                                     "plus",
    1261                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
    1262                         }
    1263                         else { // todo add more more number optimiztion
    1264                             evaluateBinaryExpression("plus", expression);
    1265                         }
    1266
    1267                     } else {
    1268                         evaluateBinaryExpression("plus", expression);
    1269                     }
    1270                 }
    1271                 break;
    1272
    1273             case Types.PLUS_EQUAL :
    1274                 evaluateBinaryExpressionWithAsignment("plus", expression);
    1275                 break;
    1276             case Types.MINUS :
    1277                 {
    1278                     if (ENABLE_EARLY_BINDING) {
    1279                         expression.resolve(this);
    1280                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
    1281                             evaluateBinaryExpression("minus", expression);
    1282                             break;
    1283                         }
    1284                         Expression leftExpression = expression.getLeftExpression();
    1285                         Expression rightExpression = expression.getRightExpression();
    1286                         Class lclass = leftExpression.getTypeClass();
    1287                         Class rclass = rightExpression.getTypeClass();
    1288                         if (lclass == null || rclass == null) {
    1289                             evaluateBinaryExpression("minus", expression);
    1290                             break;
    1291                         }
    1292                         if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
    1293                             // assuming all return boxed version for primitives
    1294                             load(leftExpression);
    1295                             helper.quickUnboxIfNecessary(int.class);
    1296                             load(rightExpression);
    1297                             helper.quickUnboxIfNecessary(int.class);
    1298                             cv.visitInsn(ISUB);
    1299                             helper.quickBoxIfNecessary(int.class);
    1300                         }
    1301                         else
    1302                         if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
    1303                             // let's use groovy utilities in the DefaultGroovyMethods
    1304                             load(leftExpression);
    1305                             load(rightExpression);
    1306                             cv.visitMethodInsn(
    1307                                     INVOKESTATIC,
    1308                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1309                                     "minus",
    1310                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
    1311                         }
    1312                         else { // todo add more more number optimiztion
    1313                             evaluateBinaryExpression("minus", expression);
    1314                         }
    1315                     } else {
    1316                         evaluateBinaryExpression("minus", expression);
    1317                     }
    1318                 }
    1319                 break;
    1320             case Types.MINUS_EQUAL :
    1321                 evaluateBinaryExpressionWithAsignment("minus", expression);
    1322                 break;
    1323
    1324             case Types.MULTIPLY :
    1325                 {
    1326                     if (ENABLE_EARLY_BINDING) {
    1327                         expression.resolve(this);
    1328                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
    1329                             evaluateBinaryExpression("multiply", expression);
    1330                             break;
    1331                         }
    1332                         Expression leftExpression = expression.getLeftExpression();
    1333                         Expression rightExpression = expression.getRightExpression();
    1334                         Class lclass = leftExpression.getTypeClass();
    1335                         Class rclass = rightExpression.getTypeClass();
    1336                         if (lclass == null || rclass == null) {
    1337                             evaluateBinaryExpression("multiply", expression);
    1338                             break;
    1339                         }
    1340                         if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
    1341                             // assuming all return boxed version for primitives
    1342                             load(leftExpression);
    1343                             helper.quickUnboxIfNecessary(int.class);
    1344                             load(rightExpression);
    1345                             helper.quickUnboxIfNecessary(int.class);
    1346                             cv.visitInsn(IMUL);
    1347                             helper.quickBoxIfNecessary(int.class);
    1348                         }
    1349                         else if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
    1350                             // let's use groovy utilities in the DefaultGroovyMethods
    1351                             load(leftExpression);
    1352                             load(rightExpression);
    1353                             cv.visitMethodInsn(
    1354                                     INVOKESTATIC,
    1355                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1356                                     "multiply",
    1357                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
    1358                         }
    1359                         else { // todo add more more number optimiztion
    1360                             evaluateBinaryExpression("multiply", expression);
    1361                         }
    1362                     } else {
    1363                         evaluateBinaryExpression("multiply", expression);
    1364                     }
    1365                 }
    1366
    1367                 break;
    1368
    1369             case Types.MULTIPLY_EQUAL :
    1370                 evaluateBinaryExpressionWithAsignment("multiply", expression);
    1371                 break;
    1372
    1373             case Types.DIVIDE :
    1374                 //SPG don't use divide since BigInteger implements directly
    1375                 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
    1376                 {
    1377                     if (ENABLE_EARLY_BINDING) {
    1378                         expression.resolve(this);
    1379                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
    1380                             evaluateBinaryExpression("div", expression);
    1381                             break;
    1382                         }
    1383                         Expression leftExpression = expression.getLeftExpression();
    1384                         Expression rightExpression = expression.getRightExpression();
    1385                         Class lclass = leftExpression.getTypeClass();
    1386                         Class rclass = rightExpression.getTypeClass();
    1387                         if (lclass == null || rclass == null) {
    1388                             evaluateBinaryExpression("div", expression);
    1389                             break;
    1390                         }
    1391 //
    1392 //                        if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
    1393 //                            // assuming all return boxed version for primitives
    1394 //                            load(leftExpression);
    1395 //                            helper.quickUnboxIfNecessary(int.class);
    1396 //                            cv.visitInsn(I2D);
    1397 //                            load(rightExpression);
    1398 //                            helper.quickUnboxIfNecessary(int.class);
    1399 //                            cv.visitInsn(I2D);
    1400 //                            cv.visitInsn(DDIV);
    1401 //                            helper.quickBoxIfNecessary(double.class);
    1402 //                        }
    1403 //                        else
    1404                             if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
    1405                             // let's use groovy utilities in the DefaultGroovyMethods
    1406                             load(leftExpression);
    1407                             load(rightExpression);
    1408                             cv.visitMethodInsn(
    1409                                     INVOKESTATIC,
    1410                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1411                                     "div",
    1412                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
    1413                         }
    1414                         else { // todo add more more number optimiztion
    1415                             evaluateBinaryExpression("div", expression);
    1416                         }
    1417                     } else {
    1418                         evaluateBinaryExpression("div", expression);
    1419                     }
    1420                 }
    1421
    1422                 break;
    1423
    1424             case Types.DIVIDE_EQUAL :
    1425                 //SPG don't use divide since BigInteger implements directly
    1426                 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
    1427                 evaluateBinaryExpressionWithAsignment("div", expression);
    1428                 break;
    1429
    1430             case Types.INTDIV :
    1431                 {
    1432                     if (ENABLE_EARLY_BINDING) {
    1433                         expression.resolve(this);
    1434                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
    1435                             evaluateBinaryExpression("intdiv", expression);
    1436                             break;
    1437                         }
    1438                         Expression leftExpression = expression.getLeftExpression();
    1439                         Expression rightExpression = expression.getRightExpression();
    1440                         Class lclass = leftExpression.getTypeClass();
    1441                         Class rclass = rightExpression.getTypeClass();
    1442                         if (lclass == null || rclass == null) {
    1443                             evaluateBinaryExpression("intdiv", expression);
    1444                             break;
    1445                         }
    1446                         if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
    1447                             // let's use groovy utilities in the DefaultGroovyMethods
    1448                             load(leftExpression);
    1449                             load(rightExpression);
    1450                             cv.visitMethodInsn(
    1451                                     INVOKESTATIC,
    1452                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1453                                     "intdiv",
    1454                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
    1455                         }
    1456                         else { // todo add more more number optimiztion
    1457                             evaluateBinaryExpression("intdiv", expression);
    1458                         }
    1459                     } else {
    1460                         evaluateBinaryExpression("intdiv", expression);
    1461                     }
    1462                 }
    1463                 break;
    1464
    1465             case Types.INTDIV_EQUAL :
    1466                 evaluateBinaryExpressionWithAsignment("intdiv", expression);
    1467                 break;
    1468
    1469             case Types.MOD :
    1470                 evaluateBinaryExpression("mod", expression);
    1471                 break;
    1472
    1473             case Types.MOD_EQUAL :
    1474                 evaluateBinaryExpressionWithAsignment("mod", expression);
    1475                 break;
    1476
    1477             case Types.LEFT_SHIFT :
    1478                 evaluateBinaryExpression("leftShift", expression);
    1479                 break;
    1480
    1481             case Types.RIGHT_SHIFT :
    1482                 evaluateBinaryExpression("rightShift", expression);
    1483                 break;
    1484
    1485             case Types.RIGHT_SHIFT_UNSIGNED :
    1486                 evaluateBinaryExpression("rightShiftUnsigned", expression);
    1487                 break;
    1488
    1489             case Types.KEYWORD_INSTANCEOF :
    1490                 evaluateInstanceof(expression);
    1491                 break;
    1492
    1493             case Types.FIND_REGEX :
    1494                 evaluateBinaryExpression(findRegexMethod, expression);
    1495                 break;
    1496
    1497             case Types.MATCH_REGEX :
    1498                 evaluateBinaryExpression(matchRegexMethod, expression);
    1499                 break;
    1500
    1501             case Types.LEFT_SQUARE_BRACKET :
    1502                 if (leftHandExpression) {
    1503                     throwException("Should not be called here. Possible reason: postfix operation on array.");
    1504                     // This is handled right now in the evaluateEqual()
    1505                     // should support this here later
    1506                     //evaluateBinaryExpression("putAt", expression);
    1507                 }
    1508                 else if (ENABLE_EARLY_BINDING) {
    1509                     expression.resolve(this);
    1510                     if (expression.isResolveFailed() || !expression.isTypeResolved()) {
    1511                         evaluateBinaryExpression("getAt", expression);
    1512                         break;
    1513                     }
    1514                     Expression leftExpression = expression.getLeftExpression();
    1515                     Expression rightExpression = expression.getRightExpression();
    1516                     Class lclass = leftExpression.getTypeClass();
    1517                     Class rclass = rightExpression.getTypeClass();
    1518                     if (lclass == null || rclass == null) {
    1519                         evaluateBinaryExpression("getAt", expression);
    1520                         break;
    1521                     }
    1522                     if (lclass == String.class && rclass == Integer.class) {
    1523                         load(leftExpression); cast(String.class);
    1524                         load(rightExpression); helper.quickUnboxIfNecessary(int.class);
    1525                         cv.visitMethodInsn(
    1526                                 INVOKESTATIC,
    1527                                 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1528                                 "getAt",
    1529                                 "([Ljava/lang/String;I)Ljava/lang/String;");
    1530                         break;
    1531                     }
    1532                     else if (lclass.isArray() && rclass == Integer.class) {
    1533                         load(leftExpression); // cast it?
    1534                         load(rightExpression); helper.quickUnboxIfNecessary(int.class);
    1535                         Class elemType = lclass.getComponentType();
    1536                         if (!elemType.isPrimitive()) {
    1537                             cv.visitMethodInsn(
    1538                                     INVOKESTATIC,
    1539                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1540                                     "getAt",
    1541                                     "([Ljava/lang/Object;I)Ljava/lang/Object;");
    1542                             cast(elemType);
    1543                         }
    1544                         else {
    1545                             evaluateBinaryExpression("getAt", expression); // todo more optim
    1546                         }
    1547                         break;
    1548                     }
    1549                     else if (List.class == lclass && rclass == Integer.class){
    1550                         // there is special logic in treating list subscript
    1551                         load(leftExpression); cast(List.class);
    1552                         load(rightExpression); helper.quickUnboxIfNecessary(int.class);
    1553                             //INVOKESTATIC org/codehaus/groovy/runtime/DefaultGroovyMethods getAt (Ljava/util/List;I)Ljava/lang/Object;
    1554                         cv.visitMethodInsn(
    1555                                 INVOKESTATIC,
    1556                                 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1557                                 "getAt",
    1558                                 "(Ljava/util/List;I)Ljava/lang/Object;");
    1559                         break;
    1560                     }
    1561                     else if (Map.class.isAssignableFrom(lclass)){ // todo test this
    1562                         visitMethodCallExpression(
    1563                                 new MethodCallExpression(
    1564                                         leftExpression,
    1565                                         "get",
    1566                                         new ArgumentListExpression(
    1567                                                 new Expression[] { rightExpression})));
    1568                         break;
    1569                     }
    1570                     else {
    1571                         evaluateBinaryExpression("getAt", expression); // todo more optim
    1572                         break;
    1573                     }
    1574                 }
    1575                 else {
    1576                     evaluateBinaryExpression("getAt", expression);
    1577                 }
    1578                 break;
    1579
    1580             default :
    1581                 throwException("Operation: " + expression.getOperation() + " not supported");
    1582         }
    1583     }
    1584
    1585     private void load(Expression exp) {
    1586
    1587         boolean wasLeft = leftHandExpression;
    1588         leftHandExpression = false;
    1589 //        if (CREATE_DEBUG_INFO)
    1590 //            helper.mark("-- loading expression: " + exp.getClass().getName() +
    1591 //                    " at [" + exp.getLineNumber() + ":" + exp.getColumnNumber() + "]");
    1592         //exp.visit(this);
    1593         visitAndAutoboxBoolean(exp);
    1594 //        if (CREATE_DEBUG_INFO)
    1595 //            helper.mark(" -- end of loading --");
    1596
    1597
    1598         if (ENABLE_EARLY_BINDING){
    1599 // casting might be expensive. should do JIT casting
    1600
    1601 //            Class cls = exp.getTypeClass();
    1602 //            if (cls != null && !cls.isPrimitive() && cls != Object.class) {
    1603 //                cast(cls);
    1604 //            }
    1605         }
    1606         //evaluateExpression(exp);
    1607         leftHandExpression  = wasLeft;
    1608     }
    1609
    1610     public void visitPostfixExpression(PostfixExpression expression) {
    1611         if (ENABLE_EARLY_BINDING) {
    1612             int type = expression.getOperation().getType();
    1613             expression.resolve(this);
    1614             if (expression.isResolveFailed() || !expression.isTypeResolved()) {
    1615                 evaluatePostfixMethod("next", expression.getExpression());
    1616                 return;
    1617             }
    1618             Class lclass = expression.getTypeClass();
    1619             Expression exp = expression.getExpression();
    1620             String func = type == Types.PLUS_PLUS ? "next" : "previous";
    1621             int op = type == Types.PLUS_PLUS ? IADD : ISUB;
    1622
    1623             if (lclass == Integer.class) {
    1624                 load(exp);
    1625                 cv.visitInsn(DUP); // leave the old value on the stack;
    1626                 helper.quickUnboxIfNecessary(int.class);
    1627                 cv.visitInsn(ICONST_1);
    1628                 cv.visitInsn(op);
    1629                 helper.quickBoxIfNecessary(int.class);
    1630                 store(exp);
    1631             }
    1632             else if (Number.class.isAssignableFrom(lclass)) {
    1633                 // let's use groovy utilities in the DefaultGroovyMethods
    1634                 load(exp);
    1635                 cv.visitInsn(DUP); // leave the old value on the stack;
    1636                 cv.visitMethodInsn(
    1637                         INVOKESTATIC,
    1638                         BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
    1639                         func,
    1640                         "(Ljava/lang/Number;)Ljava/lang/Number;");
    1641                 store(exp);
    1642             }
    1643             else { // todo add more more number optimiztion
    1644                 evaluatePostfixMethod(func, exp);
    1645             }
    1646
    1647         } else {
    1648             switch (expression.getOperation().getType()) {
    1649                 case Types.PLUS_PLUS :
    1650                     evaluatePostfixMethod("next", expression.getExpression());
    1651                     break;
    1652                 case Types.MINUS_MINUS :
    1653                     evaluatePostfixMethod("previous", expression.getExpression());
    1654                     break;
    1655             }
    1656         }
    1657     }
    1658
    1659     // store the data on the stack to the expression (variablem, property, field, etc.
    1660     private void store(Expression expression) {
    1661         if (expression instanceof BinaryExpression) {
    1662             throwException("BinaryExpression appeared on LHS. ");
    1663         }
    1664         if (ASM_DEBUG) {
    1665             if (expression instanceof VariableExpression) {
    1666                 helper.mark(((VariableExpression)expression).getVariable());
    1667             }
    1668         }
    1669         boolean wasLeft = leftHandExpression;
    1670         leftHandExpression = true;
    1671         expression.visit(this);
    1672         //evaluateExpression(expression);
    1673         leftHandExpression = wasLeft;
    1674         return;
    1675     }
    1676
    1677     private void throwException(String s) {
    1678         //throw new ClassGeneratorException(s + ". Source: " + classNode.getName() + ":[" + this.lineNumber + ":" + this.columnNumber + "]");
    1679         throw new RuntimeParserException(s, currentASTNode);
    1680     }
    1681
    1682     public void visitPrefixExpression(PrefixExpression expression) {
    1683         switch (expression.getOperation().getType()) {
    1684             case Types.PLUS_PLUS :
    1685                 evaluatePrefixMethod("next", expression.getExpression());
    1686                 break;
    1687             case Types.MINUS_MINUS :
    1688                 evaluatePrefixMethod("previous", expression.getExpression());
    1689                 break;
    1690         }
    1691     }
    1692
    1693     public void visitClosureExpression(ClosureExpression expression) {
    1694         ClassNode innerClass = createClosureClass(expression);
    1695         addInnerClass(innerClass);
    1696         String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName());
    1697
    1698         ClassNode owner = innerClass.getOuterClass();
    1699         String ownerTypeName = owner.getName();
    1700         if (classNode.isStaticClass() || isStaticMethod()) {
    1701             ownerTypeName = "java.lang.Class";
    1702         }
    1703
    1704         passingClosureParams = true;
    1705         List constructors = innerClass.getDeclaredConstructors();
    1706         ConstructorNode node = (ConstructorNode) constructors.get(0);
    1707         Parameter[] localVariableParams = node.getParameters();
    1708
    1709
    1710         //
    1711         // Define in the context any variables that will be
    1712         // created inside the closure.  Note that the first two
    1713         // parameters are always _outerInstance and _delegate,
    1714         // so we don't worry about them.
    1715
    1716         for (int i = 2; i < localVariableParams.length; i++) {
    1717             Parameter param = localVariableParams[i];
    1718             String name = param.getName();
    1719
    1720             if (variableStack.get(name) == null && classNode.getField(name) == null) {
    1721                 defineVariable(name, "java.lang.Object"); // todo  should use param type is available
    1722             }
    1723         }
    1724
    1725         cv.visitTypeInsn(NEW, innerClassinternalName);
    1726         cv.visitInsn(DUP);
    1727         if (isStaticMethod() || classNode.isStaticClass()) {
    1728             visitClassExpression(new ClassExpression(ownerTypeName));
    1729         }
    1730         else {
    1731             loadThisOrOwner();
    1732         }
    1733
    1734         if (innerClass.getSuperClass().equals("groovy.lang.Closure")) {
    1735             if (isStaticMethod()) {
    1736                 /***
    1737                  * todo could maybe stash this expression in a JVM variable
    1738                  * from previous statement above
    1739                  */
    1740                 visitClassExpression(new ClassExpression(ownerTypeName));
    1741             }
    1742             else {
    1743                 loadThisOrOwner();
    1744             }
    1745         }
    1746
    1747         //String prototype = "(L" + BytecodeHelper.getClassInternalName(ownerTypeName) + ";Ljava/lang/Object;";
    1748
    1749         // now lets load the various parameters we're passing
    1750         for (int i = 2; i < localVariableParams.length; i++) {
    1751             Parameter param = localVariableParams[i];
    1752             String name = param.getName();
    1753
    1754             if (variableStack.get(name) == null) {
    1755                 visitFieldExpression(new FieldExpression(classNode.getField(name)));
    1756             }
    1757             else {
    1758                 visitVariableExpression(new VariableExpression(name));
    1759             }
    1760             //prototype = prototype + "L" + BytecodeHelper.getClassInternalName(param.getType()) + ";";
    1761         }
    1762         passingClosureParams = false;
    1763
    1764         // we may need to pass in some other constructors
    1765         //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
    1766         cv.visitMethodInsn(
    1767             INVOKESPECIAL,
    1768             innerClassinternalName,
    1769             "<init>",
    1770             BytecodeHelper.getMethodDescriptor("void", localVariableParams));
    1771     }
    1772
    1773     /***
    1774      * Loads either this object or if we're inside a closure then load the top level owner
    1775      */
    1776     protected void loadThisOrOwner() {
    1777         if (isInnerClass()) {
    1778             visitFieldExpression(new FieldExpression(classNode.getField("owner")));
    1779         }
    1780         else {
    1781             cv.visitVarInsn(ALOAD, 0);
    1782         }
    1783     }
    1784
    1785     public void visitRegexExpression(RegexExpression expression) {
    1786         expression.getRegex().visit(this);
    1787         regexPattern.call(cv);
    1788     }
    1789
    1790     /***
    1791      * Generate byte code for constants
    1792      * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a>
    1793      */
    1794     public void visitConstantExpression(ConstantExpression expression) {
    1795         Object value = expression.getValue();
    1796         helper.loadConstant(value);
    1797     }
    1798
    1799     public void visitNegationExpression(NegationExpression expression) {
    1800         Expression subExpression = expression.getExpression();
    1801         subExpression.visit(this);
    1802         negation.call(cv);
    1803     }
    1804
    1805     public void visitCastExpression(CastExpression expression) {
    1806         String type = expression.getType();
    1807         type = checkValidType(type, expression, "in cast");
    1808
    1809         visitAndAutoboxBoolean(expression.getExpression());
    1810
    1811         doConvertAndCast(type, expression.getExpression());
    1812     }
    1813
    1814     public void visitNotExpression(NotExpression expression) {
    1815         Expression subExpression = expression.getExpression();
    1816         subExpression.visit(this);
    1817
    1818         // This is not the best way to do this. Javac does it by reversing the
    1819         // underlying expressions but that proved
    1820         // fairly complicated for not much gain. Instead we'll just use a
    1821         // utility function for now.
    1822         if (isComparisonExpression(expression.getExpression())) {
    1823             notBoolean.call(cv);
    1824         }
    1825         else {
    1826             notObject.call(cv);
    1827         }
    1828     }
    1829
    1830     /***
    1831      * return a primitive boolean value of the BooleanExpresion.
    1832      * @param expression
    1833      */
    1834     public void visitBooleanExpression(BooleanExpression expression) {
    1835         expression.getExpression().visit(this);
    1836
    1837         if (!isComparisonExpression(expression.getExpression())) {
    1838 // comment out for optimization when boolean values are not autoboxed for eg. function calls.
    1839 //           Class typeClass = expression.getExpression().getTypeClass();
    1840 //           if (typeClass != null && typeClass != boolean.class) {
    1841                 asBool.call(cv); // to return a primitive boolean
    1842 //            }
    1843         }
    1844     }
    1845
    1846     public void visitMethodCallExpression(MethodCallExpression call) {
    1847         onLineNumber(call, "visitMethodCallExpression: \"" + call.getMethod() + "\":");
    1848         if (ENABLE_EARLY_BINDING)
    1849             call.resolve(this);
    1850
    1851         this.leftHandExpression = false;
    1852
    1853         Expression arguments = call.getArguments();
    1854         /*
    1855          * if (arguments instanceof TupleExpression) { TupleExpression
    1856          * tupleExpression = (TupleExpression) arguments; int size =
    1857          * tupleExpression.getExpressions().size(); if (size == 0) { arguments =
    1858          * ConstantExpression.EMPTY_ARRAY; } }
    1859          */
    1860         boolean superMethodCall = MethodCallExpression.isSuperMethodCall(call);
    1861         String method = call.getMethod();
    1862         if (superMethodCall && method.equals("<init>")) {
    1863             /*** todo handle method types! */
    1864             cv.visitVarInsn(ALOAD, 0);
    1865             if (isInClosureConstructor()) { // br use the second param to init the super class (Closure)
    1866                 cv.visitVarInsn(ALOAD, 2);
    1867                 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
    1868             }
    1869             else {
    1870                 cv.visitVarInsn(ALOAD, 1);
    1871                 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
    1872             }
    1873         }
    1874         else {
    1875             // are we a local variable
    1876             if (isThisExpression(call.getObjectExpression()) && isFieldOrVariable(call.getMethod())) {
    1877                 /*
    1878                  * if (arguments instanceof TupleExpression) { TupleExpression
    1879                  * tupleExpression = (TupleExpression) arguments; int size =
    1880                  * tupleExpression.getExpressions().size(); if (size == 1) {
    1881                  * arguments = (Expression)
    1882                  * tupleExpression.getExpressions().get(0); } }
    1883                  */
    1884
    1885                 // lets invoke the closure method
    1886                 visitVariableExpression(new VariableExpression(method));
    1887                 arguments.visit(this);
    1888                 invokeClosureMethod.call(cv);
    1889             }
    1890             else {
    1891                 if (superMethodCall) {
    1892                     if (method.equals("super") || method.equals("<init>")) {
    1893                         ConstructorNode superConstructorNode = findSuperConstructor(call);
    1894
    1895                         cv.visitVarInsn(ALOAD, 0);
    1896
    1897                         loadArguments(superConstructorNode.getParameters(), arguments);
    1898
    1899                         String descriptor = BytecodeHelper.getMethodDescriptor("void", superConstructorNode.getParameters());
    1900                         cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", descriptor);
    1901                     }
    1902                     else {
    1903                         MethodNode superMethodNode = findSuperMethod(call);
    1904
    1905                         cv.visitVarInsn(ALOAD, 0);
    1906
    1907                         loadArguments(superMethodNode.getParameters(), arguments);
    1908
    1909                         String descriptor = BytecodeHelper.getMethodDescriptor(superMethodNode.getReturnType(), superMethodNode.getParameters());
    1910                         cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superMethodNode.getDeclaringClass().getName()), method, descriptor);
    1911                     }
    1912                 }
    1913                 else {
    1914                     // let's try early binding
    1915                     if (ENABLE_EARLY_BINDING) {
    1916                         try {
    1917                             MetaMethod metamethod = call.getMetaMethod(); // todo change it to resolveMethodCallExpression
    1918                             if (metamethod != null) {
    1919                                 Class decClass = metamethod.getDeclaringClass();
    1920                                 String ownerClassName = null;
    1921                                 if (decClass == null) {
    1922                                     // meaning the class is the current class
    1923                                     ownerClassName = BytecodeHelper.getClassInternalName(classNode.getName());
    1924                                 }
    1925                                 else {
    1926                                     ownerClassName = BytecodeHelper.getClassInternalName(decClass.getName());
    1927                                 }
    1928
    1929                                 String methodName = call.getMethod();
    1930                                 String descr = BytecodeHelper.getMethodDescriptor(metamethod);
    1931                                 Class[] params = metamethod.getParameterTypes();
    1932                                 //
    1933                                 Label l2 = new Label();
    1934
    1935                                 if (metamethod.isStatic()) {
    1936                                 } else {
    1937                                     boolean wasLeft = leftHandExpression;
    1938                                     leftHandExpression = false;
    1939                                     call.getObjectExpression().visit(this);
    1940
    1941                                     if (call.isSafe()) {
    1942                                         helper.dup();
    1943                                         cv.visitJumpInsn(IFNULL, l2);
    1944                                     }
    1945
    1946                                     cv.visitTypeInsn(CHECKCAST, ownerClassName);
    1947                                     leftHandExpression = wasLeft;
    1948                                 }
    1949                                 //
    1950                                 if (arguments instanceof TupleExpression) {
    1951                                     TupleExpression tupleExpression = (TupleExpression) arguments;
    1952                                     List argexps = tupleExpression.getExpressions();
    1953                                     for (int i = 0; i < argexps.size(); i++) {
    1954                                         Expression expression = (Expression) argexps.get(i);
    1955                                         load(expression);
    1956
    1957                                         if (params[i].isPrimitive() /*&& !expression.getTypeClass().isPrimitive()*/) { // data always boxed
    1958                                             cast(params[i]);
    1959                                             helper.quickUnboxIfNecessary(params[i]);
    1960                                         }
    1961                                         else if (params[i].isArray() && params[i].getComponentType().isPrimitive() ) {
    1962                                             new ClassExpression(params[i].getComponentType()).visit(this);
    1963                                             convertToPrimitiveArray.call(cv);
    1964                                             cast(params[i]);
    1965                                         }
    1966                                         else {
    1967                                             if (expression.getTypeClass() == GString.class && params[i] == String.class){
    1968                                                 cast(GString.class);
    1969                                                 cv.visitMethodInsn(
    1970                                                         INVOKEVIRTUAL,
    1971                                                         "java/lang/Object",
    1972                                                         "toString",
    1973                                                         "()Ljava/lang/String;"
    1974                                                 );
    1975                                             }
    1976                                             else {
    1977                                                 cast(params[i]);
    1978                                             }
    1979                                         }
    1980                                     }
    1981                                     if (metamethod.isStatic()) {
    1982                                         cv.visitMethodInsn(INVOKESTATIC, ownerClassName, methodName, descr);
    1983                                     }
    1984                                     else if (decClass != null && decClass.isInterface()){
    1985                                         cv.visitMethodInsn(INVOKEINTERFACE, ownerClassName, methodName, descr);
    1986                                     }
    1987                                     else {
    1988                                         cv.visitMethodInsn(INVOKEVIRTUAL, ownerClassName, methodName, descr);
    1989                                     }
    1990                                     call.setTypeClass(metamethod.getReturnType());
    1991                                     if (metamethod.getReturnType().isPrimitive()
    1992                                             && metamethod.getReturnType() != void.class
    1993                                             //&& metamethod.getReturnType() != boolean.class
    1994                                     ) {
    1995                                         helper.quickBoxIfNecessary(metamethod.getReturnType());
    1996                                     }
    1997                                     if (call.isSafe()) {
    1998                                         Label l3 = new Label();
    1999                                         cv.visitJumpInsn(GOTO, l3);
    2000                                         cv.visitLabel(l2);
    2001                                         cv.visitInsn(POP);
    2002                                         cv.visitInsn(ACONST_NULL);
    2003                                         cv.visitLabel(l3);
    2004                                     }
    2005                                     return;
    2006                                 } else {
    2007                                     throw new GroovyRuntimeException("arguments type not handled. fall through to late binding");
    2008                                 }
    2009                             }
    2010                         } catch (Exception e) {
    2011 //                            System.out.println(this.classNode.getName() + ":" + this.methodNode.getName());
    2012 //                            //e.printStackTrace(); //System.out.println(e.getMessage());
    2013 //                            log.info("ignore: attempt early binding: " + e.getMessage());
    2014                             // fall through
    2015                         }
    2016                     } // end of early binding trial
    2017
    2018                     if (emptyArguments(arguments) && !call.isSafe()) {
    2019                         call.getObjectExpression().visit(this);
    2020                         cv.visitLdcInsn(method);
    2021                         invokeNoArgumentsMethod.call(cv); // todo try if we can do early binding
    2022                     }
    2023                     else {
    2024                         if (argumentsUseStack(arguments)) {
    2025
    2026                             arguments.visit(this);
    2027
    2028                             Variable tv = visitASTOREInTemp(method + "_arg");
    2029                             int paramIdx = tv.getIndex();
    2030
    2031                             call.getObjectExpression().visit(this); // xxx
    2032
    2033                             cv.visitLdcInsn(method);
    2034
    2035                             cv.visitVarInsn(ALOAD, paramIdx);
    2036                             removeVar(tv);
    2037                         }
    2038                         else {
    2039                             call.getObjectExpression().visit(this);
    2040                             cv.visitLdcInsn(method);
    2041                             arguments.visit(this);
    2042                         }
    2043
    2044                         if (call.isSafe()) {
    2045                             invokeMethodSafeMethod.call(cv);
    2046                         }
    2047                         else {
    2048                             invokeMethodMethod.call(cv);
    2049                         }
    2050                     }
    2051                 }
    2052             }
    2053         }
    2054     }
    2055
    2056     /***
    2057      * Loads and coerces the argument values for the given method call
    2058      */
    2059     protected void loadArguments(Parameter[] parameters, Expression expression) {
    2060         TupleExpression argListExp = (TupleExpression) expression;
    2061         List arguments = argListExp.getExpressions();
    2062         for (int i = 0, size = arguments.size(); i < size; i++) {
    2063             Expression argExp = argListExp.getExpression(i);
    2064             Parameter param = parameters[i];
    2065             visitAndAutoboxBoolean(argExp);
    2066
    2067             String type = param.getType();
    2068             if (BytecodeHelper.isPrimitiveType(type)) {
    2069                 helper.unbox(type);
    2070             }
    2071
    2072             String expType = getExpressionType(argExp);
    2073             if (isValidTypeForCast(type) && (expType == null || !type.equals(expType))) {
    2074                 doConvertAndCast(type);
    2075             }
    2076  //           doConvertAndCast(type, argExp);
    2077         }
    2078     }
    2079
    2080     /***
    2081      * Attempts to find the method of the given name in a super class
    2082      */
    2083     protected MethodNode findSuperMethod(MethodCallExpression call) {
    2084         String methodName = call.getMethod();
    2085         TupleExpression argExpr = (TupleExpression) call.getArguments();
    2086         int argCount = argExpr.getExpressions().size();
    2087         ClassNode superClassNode = classNode.getSuperClassNode();
    2088         if (superClassNode != null) {
    2089             List methods = superClassNode.getMethods(methodName);
    2090             for (Iterator iter = methods.iterator(); iter.hasNext(); ) {
    2091                 MethodNode method = (MethodNode) iter.next();
    2092                 if (method.getParameters().length == argCount) {
    2093                     return method;
    2094                 }
    2095             }
    2096         }
    2097         throwException("No such method: " + methodName + " for class: " + classNode.getName());
    2098         return null; // should not come here
    2099     }
    2100
    2101     /***
    2102      * Attempts to find the constructor in a super class
    2103      */
    2104     protected ConstructorNode findSuperConstructor(MethodCallExpression call) {
    2105         TupleExpression argExpr = (TupleExpression) call.getArguments();
    2106         int argCount = argExpr.getExpressions().size();
    2107         ClassNode superClassNode = classNode.getSuperClassNode();
    2108         if (superClassNode != null) {
    2109             List constructors = superClassNode.getDeclaredConstructors();
    2110             for (Iterator iter = constructors.iterator(); iter.hasNext(); ) {
    2111                 ConstructorNode constructor = (ConstructorNode) iter.next();
    2112                 if (constructor.getParameters().length == argCount) {
    2113                     return constructor;
    2114                 }
    2115             }
    2116         }
    2117         throwException("No such constructor for class: " + classNode.getName());
    2118         return null; // should not come here
    2119     }
    2120
    2121     protected boolean emptyArguments(Expression arguments) {
    2122         if (arguments instanceof TupleExpression) {
    2123             TupleExpression tupleExpression = (TupleExpression) arguments;
    2124             int size = tupleExpression.getExpressions().size();
    2125             return size == 0;
    2126         }
    2127         return false;
    2128     }
    2129
    2130     public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
    2131         this.leftHandExpression = false;
    2132
    2133         Expression arguments = call.getArguments();
    2134         if (emptyArguments(arguments)) {
    2135             cv.visitLdcInsn(call.getType());
    2136             cv.visitLdcInsn(call.getMethod());
    2137
    2138             invokeStaticNoArgumentsMethod.call(cv);
    2139         }
    2140         else {
    2141             if (arguments instanceof TupleExpression) {
    2142                 TupleExpression tupleExpression = (TupleExpression) arguments;
    2143                 int size = tupleExpression.getExpressions().size();
    2144                 if (size == 1) {
    2145                     arguments = (Expression) tupleExpression.getExpressions().get(0);
    2146                 }
    2147             }
    2148
    2149             cv.visitLdcInsn(call.getOwnerType());
    2150             cv.visitLdcInsn(call.getMethod());
    2151             arguments.visit(this);
    2152
    2153             invokeStaticMethodMethod.call(cv);
    2154         }
    2155     }
    2156
    2157     public void visitConstructorCallExpression(ConstructorCallExpression call) {
    2158         onLineNumber(call, "visitConstructorCallExpression: \"" + call.getTypeToSet() + "\":");
    2159         do {
    2160             if (ENABLE_EARLY_BINDING) {
    2161                 call.resolve(this);
    2162                 if (call.isResolveFailed() || call.getTypeClass() == null) {
    2163                     break;
    2164                 }
    2165                 else {
    2166                     try {
    2167                         Constructor ctor = call.getConstructor(); // todo change it to resolveMethodCallExpression
    2168                         if (ctor != null) {
    2169                             Class decClass = ctor.getDeclaringClass();
    2170                             String ownerClassName = null;
    2171                             if (decClass == null) {
    2172                                 // meaning the class is the current class
    2173                                 ownerClassName = BytecodeHelper.getClassInternalName(classNode.getName());
    2174                             }
    2175                             else {
    2176                                 ownerClassName = BytecodeHelper.getClassInternalName(decClass.getName());
    2177                             }
    2178
    2179                             Class[] params = ctor.getParameterTypes();
    2180                             StringBuffer argbuf = new StringBuffer("(");
    2181                             for (int i = 0; i < params.length; i++) {
    2182                                 Class arg = params[i];
    2183                                 String descr = BytecodeHelper.getTypeDescription(arg);
    2184                                 argbuf.append(descr);
    2185                             }
    2186                             argbuf.append(")V");
    2187                             //
    2188                             cv.visitTypeInsn(NEW, ownerClassName);
    2189                             cv.visitInsn(DUP);
    2190
    2191                             //
    2192                             Expression arguments = call.getArguments();
    2193                             if (arguments instanceof TupleExpression) {
    2194                                 TupleExpression tupleExpression = (TupleExpression) arguments;
    2195                                 List argexps = tupleExpression.getExpressions();
    2196                                 for (int i = 0; i < argexps.size(); i++) {
    2197                                     Expression expression = (Expression) argexps.get(i);
    2198                                     load(expression);
    2199                                     if (params[i].isPrimitive() /*&& !expression.getTypeClass().isPrimitive()*/) { // data always boxed
    2200                                         cast(params[i]);
    2201                                         helper.quickUnboxIfNecessary(params[i]);
    2202                                     }
    2203                                     else if (params[i].isArray() && params[i].getComponentType().isPrimitive() ) {
    2204                                         new ClassExpression(params[i].getComponentType()).visit(this);
    2205                                         convertToPrimitiveArray.call(cv);
    2206                                         cast(params[i]);
    2207                                     }
    2208                                     else {
    2209                                         //? if the target is String , I might as well call Object.toString() regardless
    2210                                         if (expression.getTypeClass() == GString.class && params[i] == String.class){
    2211                                             cast(GString.class);
    2212                                             cv.visitMethodInsn(
    2213                                                     INVOKEVIRTUAL,
    2214                                                     "java/lang/Object",
    2215                                                     "toString",
    2216                                                     "()Ljava/lang/String;"
    2217                                             );
    2218                                         }
    2219                                         else {
    2220                                             cast(params[i]);
    2221                                         }
    2222                                     }
    2223                                 }
    2224
    2225                                 cv.visitMethodInsn(INVOKESPECIAL, ownerClassName, "<init>", argbuf.toString());
    2226                                 return;
    2227                             } else {
    2228                                 throw new GroovyRuntimeException("arguments type not handled. fall through to late binding");
    2229                             }
    2230                         }
    2231                     } catch (Exception e) {
    2232 //                        System.out.println(this.classNode.getName() + ":" + this.methodNode.getName());
    2233                         //e.printStackTrace(); //System.out.println(e.getMessage());
    2234 //                        log.info("ignore: attempt early binding: " + e.getMessage());
    2235                         break;// fall through
    2236                     }
    2237                 }
    2238             }
    2239         } while(false);
    2240
    2241         this.leftHandExpression = false;
    2242
    2243         Expression arguments = call.getArguments();
    2244         if (arguments instanceof TupleExpression) {
    2245             TupleExpression tupleExpression = (TupleExpression) arguments;
    2246             int size = tupleExpression.getExpressions().size();
    2247             if (size == 0) {
    2248                 arguments = null;
    2249             }
    2250 //            else if (size == 1) { // why unpack the tuple of 1 component?
    2251 //                arguments = (Expression) tupleExpression.getExpressions().get(0);
    2252 //            }
    2253         }
    2254
    2255         // lets check that the type exists
    2256         String type = checkValidType(call.getType(), call, "in constructor call");
    2257
    2258         //System.out.println("Constructing: " + type);
    2259
    2260         visitClassExpression(new ClassExpression(type));
    2261         if (arguments !=null) {
    2262                arguments.visit(this);
    2263             invokeConstructorOfMethod.call(cv);     // todo subject to opti
    2264         } else {
    2265             invokeNoArgumentsConstructorOf.call(cv); // todo subject to opti
    2266         }
    2267         /*
    2268          * cv.visitLdcInsn(type);
    2269          *
    2270          * arguments.visit(this);
    2271          *
    2272          * invokeConstructorMethod.call(cv);
    2273          */
    2274     }
    2275
    2276     public void visitPropertyExpression(PropertyExpression expression) {
    2277
    2278         do {
    2279             if (true && ENABLE_EARLY_BINDING) {
    2280                 expression.resolve(this);
    2281
    2282                 if (!expression.isTypeResolved()) {
    2283                     break;
    2284                 }
    2285                 Expression ownerExp = expression.getObjectExpression();
    2286                 String propName = expression.getProperty();
    2287                 if (expression.getProperty().equals("class")) {
    2288                     break; // the default does the right thing. let it do.
    2289                 }
    2290
    2291
    2292                 String ownerType = ownerExp.getType();
    2293                 Class ownerClass = ownerExp.getTypeClass();
    2294                 if (ownerType == null || ownerType.length() == 0) {
    2295                     break;
    2296                 }
    2297
    2298                 Label l3 = new Label();
    2299                 // handle arraylength
    2300                 if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) {
    2301                     load(ownerExp);
    2302                     if (expression.isSafe()) {
    2303                         helper.dup();
    2304                         cv.visitJumpInsn(IFNULL, l3);
    2305                     }
    2306                     cast(ownerClass);
    2307                     cv.visitInsn(ARRAYLENGTH);
    2308                     helper.quickBoxIfNecessary(int.class);
    2309                     cv.visitLabel(l3);
    2310                     return;
    2311                 }
    2312
    2313
    2314                 String propertyType = expression.getType();
    2315                 if (propertyType == null || propertyType.length() == 0) {
    2316                     break;
    2317                 }
    2318                 boolean isStatic = expression.isStatic();
    2319                 if (!isThisExpression(ownerExp) && GroovyObject.class.isAssignableFrom(ownerExp.getTypeClass())) {
    2320                     // call other groovy object property via getProperty()/setProperty()
    2321                     if (!isStatic && ownerExp instanceof ClassExpression) {
    2322                         if (leftHandExpression) {
    2323                             cv.visitMethodInsn(
    2324                                     INVOKEVIRTUAL,
    2325                                     BytecodeHelper.getClassInternalName(ownerType),
    2326                                     "setProperty",
    2327                                     BytecodeHelper.getTypeDescription(propertyType));
    2328                         } else {
    2329                             cv.visitMethodInsn(
    2330                                     INVOKEVIRTUAL,
    2331                                     BytecodeHelper.getClassInternalName(ownerType),
    2332                                     "getProperty",
    2333                                     BytecodeHelper.getTypeDescription(propertyType));
    2334                         }
    2335                         return;
    2336                     } else {
    2337                         break;
    2338                     }
    2339                 }
    2340 //                else if (isThisExpression(ownerExp)){
    2341 //                    if (leftHandExpression) {
    2342 //                        helper.loadThis();
    2343 //                        cv.visitFieldInsn(
    2344 //                                PUTFIELD,
    2345 //                                BytecodeHelper.getClassInternalName(ownerType),
    2346 //                                expression.getProperty(),
    2347 //                                BytecodeHelper.getClassInternalName(propertyType));
    2348 //                    } else {
    2349 //                        cv.visitMethodInsn(
    2350 //                                INVOKEVIRTUAL,
    2351 //                                BytecodeHelper.getClassInternalName(ownerType),
    2352 //                                "getProperty",
    2353 //                                BytecodeHelper.getClassInternalName(propertyType));
    2354 //                    }
    2355 //                    return;
    2356 //                }
    2357
    2358                 // the following logic is used for this.<prop> acess too.
    2359                 else  { // none direct local access
    2360                     Field fld = expression.getField();
    2361                     Method setter = expression.getSetter();
    2362                     Method getter = expression.getGetter();
    2363
    2364                     // gate keeping
    2365                     if (leftHandExpression) {
    2366                         if (fld == null && setter == null) {
    2367                             break;
    2368                         }
    2369                     }
    2370                     else {
    2371                         if (fld == null && getter == null) {
    2372                             break;
    2373                         }
    2374                     }
    2375
    2376                     if (ownerClass == null && !isThisExpression(ownerExp)) {
    2377                         break;  // ownerClass is null only when the ownerExp is "this"
    2378                     }
    2379                     // now looking for public fields before accessors
    2380
    2381
    2382                     if (expression.isStatic()) {
    2383                         if (leftHandExpression) {
    2384                             if (fld != null) {
    2385                                 helper.quickUnboxIfNecessary(expression.getTypeClass());
    2386                                 cv.visitFieldInsn(
    2387                                         PUTSTATIC,
    2388                                         BytecodeHelper.getClassInternalName(ownerType),
    2389                                         expression.getProperty(),
    2390                                         BytecodeHelper.getTypeDescription(propertyType)
    2391                                 );
    2392                             }
    2393                             else if (setter != null) {
    2394                                 helper.quickUnboxIfNecessary(setter.getParameterTypes()[0]);
    2395                                 cast(setter.getParameterTypes()[0]);
    2396                                 helper.invoke(setter);
    2397                             }
    2398                             else {
    2399                                 throwException("no method or field is found for a resolved property access");
    2400                             }
    2401                         }
    2402                         else { // get the property
    2403                             if (fld != null){
    2404                                 cv.visitFieldInsn(
    2405                                         GETSTATIC,
    2406                                         BytecodeHelper.getClassInternalName(ownerType),
    2407                                         propName,
    2408                                         BytecodeHelper.getTypeDescription(propertyType)
    2409                                 );
    2410                                 helper.quickBoxIfNecessary(expression.getTypeClass());
    2411                             }
    2412                             else if (getter != null) {
    2413                                 helper.invoke(getter);
    2414                                 helper.quickBoxIfNecessary(expression.getTypeClass());
    2415                             }
    2416                             else {
    2417                                 throwException("no method or field is found for a resolved property access");
    2418                             }
    2419                         }
    2420                     } else { // non-static access
    2421                         if (leftHandExpression) { // set the property
    2422                               // assumption: the data on the stack are boxed if it's a number
    2423                             helper.quickUnboxIfNecessary(expression.getTypeClass());
    2424                             load(ownerExp);
    2425                             if (expression.isSafe()) {
    2426                                 helper.dup();
    2427                                 cv.visitJumpInsn(IFNULL, l3);
    2428                             }
    2429
    2430                             if (ownerClass != null)
    2431                                 cast(ownerClass);
    2432                             Class cls = expression.getTypeClass();
    2433                             if (cls == double.class || cls == long.class) {
    2434                                 cv.visitInsn(DUP_X2);
    2435                                 cv.visitInsn(POP);
    2436                             } else {
    2437                                 cv.visitInsn(SWAP);
    2438                             }
    2439
    2440                             if (fld != null) {
    2441                                 cv.visitFieldInsn(
    2442                                         PUTFIELD,
    2443                                         BytecodeHelper.getClassInternalName(ownerType),
    2444                                         propName,
    2445                                         BytecodeHelper.getTypeDescription(propertyType)
    2446                                 );
    2447                             }
    2448                             else if (setter != null) {
    2449                                 Method m = setter;
    2450                                 Class[] paramTypes = m.getParameterTypes();
    2451                                 if (paramTypes.length != 1) {
    2452                                     throw new RuntimeException("setter should take a single parameter");
    2453                                 }
    2454                                 Class paramType = paramTypes[0];
    2455                                 cast(paramType);
    2456                                 helper.invoke(setter);
    2457                             }
    2458                             else {
    2459                                 throwException("no method or field is found for a resolved property access");
    2460                             }
    2461                         }
    2462                         else { // get property
    2463                             load(ownerExp);
    2464                             if (expression.isSafe()) {
    2465                                 helper.dup();
    2466                                 cv.visitJumpInsn(IFNULL, l3);
    2467                             }
    2468                             if (ownerClass != null)
    2469                                 cast(ownerClass);
    2470                             if (fld != null) {
    2471                                 cv.visitFieldInsn(
    2472                                         GETFIELD,
    2473                                         BytecodeHelper.getClassInternalName(ownerType),
    2474                                         propName,
    2475                                         BytecodeHelper.getTypeDescription(propertyType)
    2476                                 );
    2477                                 helper.quickBoxIfNecessary(expression.getTypeClass());
    2478                             }
    2479                             else if (getter != null) {
    2480                                 helper.invoke(getter);
    2481                                 helper.quickBoxIfNecessary(expression.getTypeClass());
    2482                             }
    2483                             else {
    2484                                 throwException("no method or field is found for a resolved property access");
    2485                             }
    2486                         }
    2487                     }
    2488                     cv.visitLabel(l3);
    2489                     return;
    2490                 }
    2491             }
    2492         } while (false);
    2493
    2494         // lets check if we're a fully qualified class name
    2495         String className = null;
    2496         Expression objectExpression = expression.getObjectExpression();
    2497         if (!isThisExpression(objectExpression)) {
    2498             className = checkForQualifiedClass(expression);
    2499             if (className != null) {
    2500                 visitClassExpression(new ClassExpression(className));
    2501                 return;
    2502             }
    2503         }
    2504         if (expression.getProperty().equals("class")) {
    2505             if ((objectExpression instanceof ClassExpression)) {
    2506                 visitClassExpression((ClassExpression) objectExpression);
    2507                 return;
    2508             }
    2509             else if (objectExpression instanceof VariableExpression) {
    2510                 VariableExpression varExp = (VariableExpression) objectExpression;
    2511                 className = varExp.getVariable();
    2512                 try {
    2513                     className = resolveClassName(className);
    2514                     visitClassExpression(new ClassExpression(className));
    2515                     return;
    2516                 }
    2517                 catch (Exception e) {
    2518                     // ignore
    2519                 }
    2520             }
    2521         }
    2522
    2523         if (isThisExpression(objectExpression)) {
    2524             // lets use the field expression if its available
    2525             String name = expression.getProperty();
    2526             FieldNode field = classNode.getField(name);
    2527             if (field != null) {
    2528                 visitFieldExpression(new FieldExpression(field));
    2529                 return;
    2530             }
    2531         }
    2532
    2533         boolean left = leftHandExpression;
    2534         // we need to clear the LHS flag to avoid "this." evaluating as ASTORE
    2535         // rather than ALOAD
    2536         leftHandExpression = false;
    2537
    2538         objectExpression.visit(this);
    2539
    2540         cv.visitLdcInsn(expression.getProperty());
    2541
    2542         if (isGroovyObject(objectExpression) && ! expression.isSafe()) {
    2543             if (left) {
    2544                 setGroovyObjectPropertyMethod.call(cv);
    2545             }
    2546             else {
    2547                 getGroovyObjectPropertyMethod.call(cv);
    2548             }
    2549         }
    2550         else {
    2551             if (expression.isSafe()) {
    2552                 if (left) {
    2553                     setPropertySafeMethod2.call(cv);
    2554                 }
    2555                 else {
    2556                     getPropertySafeMethod.call(cv);
    2557                 }
    2558             }
    2559             else {
    2560                 if (left) {
    2561                     setPropertyMethod2.call(cv);
    2562                 }
    2563                 else {
    2564                     getPropertyMethod.call(cv);
    2565                 }
    2566             }
    2567         }
    2568     }
    2569
    2570     protected boolean isGroovyObject(Expression objectExpression) {
    2571         return isThisExpression(objectExpression);
    2572     }
    2573
    2574     /***
    2575      * Checks if the given property expression represents a fully qualified class name
    2576      * @return the class name or null if the property is not a valid class name
    2577      */
    2578     protected String checkForQualifiedClass(PropertyExpression expression) {
    2579         String text = expression.getText();
    2580         if (text != null && text.endsWith(".class")) {
    2581             text = text.substring(0, text.length() - 6);
    2582         }
    2583         try {
    2584             return resolveClassName(text);
    2585         }
    2586         catch (Exception e) {
    2587             return null;
    2588         }
    2589     }
    2590
    2591     public void visitFieldExpression(FieldExpression expression) {
    2592         FieldNode field = expression.getField();
    2593
    2594
    2595 	    if (field.isStatic()) {
    2596         	if (leftHandExpression) {
    2597         		storeStaticField(expression);
    2598         	}
    2599         	else {
    2600         		loadStaticField(expression);
    2601         	}
    2602         } else {
    2603         	if (leftHandExpression) {
    2604         		storeThisInstanceField(expression);
    2605         	}
    2606         	else {
    2607         		loadInstanceField(expression);
    2608         	}
    2609 		}
    2610     }
    2611
    2612     /***
    2613      *
    2614      * @param fldExp
    2615      */
    2616     public void loadStaticField(FieldExpression fldExp) {
    2617         FieldNode field = fldExp.getField();
    2618         boolean holder = field.isHolder() && !isInClosureConstructor();
    2619         String type = field.getType();
    2620
    2621         String ownerName = (field.getOwner().equals(classNode.getName()))
    2622                 ? internalClassName
    2623                 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
    2624         if (holder) {
    2625             cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
    2626             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
    2627         }
    2628         else {
    2629             cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
    2630             if (BytecodeHelper.isPrimitiveType(type)) {
    2631                 helper.box(type);
    2632 			} else {
    2633 			}
    2634         }
    2635     }
    2636
    2637 	/***
    2638 	 * RHS instance field. should move most of the code in the BytecodeHelper
    2639 	 * @param fldExp
    2640 	 */
    2641     public void loadInstanceField(FieldExpression fldExp) {
    2642     	FieldNode field = fldExp.getField();
    2643         boolean holder = field.isHolder() && !isInClosureConstructor();
    2644         String type = field.getType();
    2645         String ownerName = (field.getOwner().equals(classNode.getName()))
    2646 				? internalClassName
    2647 				: org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
    2648
    2649         cv.visitVarInsn(ALOAD, 0);
    2650 		cv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
    2651
    2652 		if (holder) {
    2653 			cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
    2654 		} else {
    2655 			if (BytecodeHelper.isPrimitiveType(type)) {
    2656 				helper.box(type);
    2657 			} else {
    2658 			}
    2659 		}
    2660     }
    2661
    2662     public void storeThisInstanceField(FieldExpression expression) {
    2663         FieldNode field = expression.getField();
    2664
    2665         boolean holder = field.isHolder() && !isInClosureConstructor();
    2666         String type = field.getType();
    2667
    2668         String ownerName =  (field.getOwner().equals(classNode.getName())) ?
    2669         		internalClassName : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
    2670         if (holder) {
    2671             Variable tv = visitASTOREInTemp(field.getName());
    2672             int tempIndex = tv.getIndex();
    2673             cv.visitVarInsn(ALOAD, 0);
    2674             cv.visitFieldInsn(GETFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    2675             cv.visitVarInsn(ALOAD, tempIndex);
    2676             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
    2677             removeVar(tv);
    2678         }
    2679         else {
    2680             if (isInClosureConstructor()) {
    2681                 helper.doCast(type);
    2682             }
    2683             else {
    2684                 if (ENABLE_EARLY_BINDING) {
    2685                     helper.doCast(type);
    2686                 }
    2687                 else {
    2688                     // this may be superfluous
    2689                     doConvertAndCast(type);
    2690                 }
    2691             }
    2692             //Variable tmpVar = defineVariable(createVariableName(field.getName()), "java.lang.Object", false);
    2693             Variable tmpVar = defineVariable(createVariableName(field.getName()), field.getType(), false);
    2694             //int tempIndex = tmpVar.getIndex();
    2695             //helper.store(field.getType(), tempIndex);
    2696             helper.store(tmpVar, MARK_START);
    2697             helper.loadThis(); //cv.visitVarInsn(ALOAD, 0);
    2698             helper.load(tmpVar);
    2699             helper.putField(field, ownerName);
    2700             //cv.visitFieldInsn(PUTFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    2701             // let's remove the temp var
    2702             removeVar(tmpVar);
    2703         }
    2704     }
    2705
    2706
    2707     public void storeStaticField(FieldExpression expression) {
    2708     	FieldNode field = expression.getField();
    2709
    2710         boolean holder = field.isHolder() && !isInClosureConstructor();
    2711
    2712         String type = field.getType();
    2713
    2714         String ownerName = (field.getOwner().equals(classNode.getName()))
    2715                 ? internalClassName
    2716                 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
    2717         if (holder) {
    2718             Variable tv = visitASTOREInTemp(field.getName());
    2719             int tempIndex = tv.getIndex();
    2720             cv.visitFieldInsn(GETSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    2721             cv.visitVarInsn(ALOAD, tempIndex);
    2722             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
    2723             removeVar(tv);
    2724         }
    2725         else {
    2726             if (isInClosureConstructor()) {
    2727                 helper.doCast(type);
    2728             }
    2729             else {
    2730                 if (ENABLE_EARLY_BINDING) {
    2731                     helper.doCast(type);
    2732                 }
    2733                 else {
    2734                     // this may be superfluous
    2735                     //doConvertAndCast(type);
    2736                     // use weaker cast
    2737                     helper.doCast(type);
    2738                 }
    2739             }
    2740             cv.visitFieldInsn(PUTSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
    2741         }
    2742     }
    2743
    2744     protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first ) {
    2745         FieldNode field = expression.getField();
    2746         boolean isStatic = field.isStatic();
    2747
    2748         Variable fieldTemp = defineVariable(createVariableName(field.getName()), "java.lang.Object", false);
    2749         int valueIdx = fieldTemp.getIndex();
    2750
    2751         if (leftHandExpression && first) {
    2752             cv.visitVarInsn(ASTORE, valueIdx);
    2753             visitVariableStartLabel(fieldTemp);
    2754         }
    2755
    2756         if (steps > 1 || !isStatic) {
    2757             cv.visitVarInsn(ALOAD, 0);
    2758             cv.visitFieldInsn(
    2759                 GETFIELD,
    2760                 internalClassName,
    2761                 "owner",
    2762                 BytecodeHelper.getTypeDescription(outerClassNode.getName()));
    2763         }
    2764
    2765         if( steps == 1 ) {
    2766             int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
    2767             String ownerName = BytecodeHelper.getClassInternalName(outerClassNode.getName());
    2768
    2769             if (leftHandExpression) {
    2770                 cv.visitVarInsn(ALOAD, valueIdx);
    2771                 boolean holder = field.isHolder() && !isInClosureConstructor();
    2772                 if ( !holder) {
    2773                     doConvertAndCast(field.getType());
    2774                 }
    2775             }
    2776             cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType()));
    2777             if (!leftHandExpression) {
    2778                 if (BytecodeHelper.isPrimitiveType(field.getType())) {
    2779                     helper.box(field.getType());
    2780                 }
    2781             }
    2782         }
    2783
    2784         else {
    2785             visitOuterFieldExpression( expression, outerClassNode.getOuterClass(), steps - 1, false );
    2786         }
    2787     }
    2788
    2789
    2790
    2791     /***
    2792      *  Visits a bare (unqualified) variable expression.
    2793      */
    2794
    2795     public void visitVariableExpression(VariableExpression expression) {
    2796
    2797         String variableName = expression.getVariable();
    2798
    2799       //-----------------------------------------------------------------------
    2800       // SPECIAL CASES
    2801
    2802         //
    2803         // "this" for static methods is the Class instance
    2804
    2805         if (isStaticMethod() && variableName.equals("this")) {
    2806             visitClassExpression(new ClassExpression(classNode.getName()));
    2807             return;                                               // <<< FLOW CONTROL <<<<<<<<<
    2808         }
    2809
    2810         //
    2811         // "super" also requires special handling
    2812
    2813         if (variableName.equals("super")) {
    2814             visitClassExpression(new ClassExpression(classNode.getSuperClass()));
    2815             return;                                               // <<< FLOW CONTROL <<<<<<<<<
    2816         }
    2817
    2818
    2819         //
    2820         // class names return a Class instance, too
    2821
    2822 //        if (!variableName.equals("this")) {
    2823 //            String className = resolveClassName(variableName);
    2824 //            if (className != null) {
    2825 //                if (leftHandExpression) {
    2826 //                    throw new RuntimeParserException(
    2827 //                        "Cannot use a class expression on the left hand side of an assignment",
    2828 //                        expression);
    2829 //                }
    2830 //                visitClassExpression(new ClassExpression(className));
    2831 //                return;                                               // <<< FLOW CONTROL <<<<<<<<<
    2832 //            }
    2833 //        }
    2834
    2835
    2836       //-----------------------------------------------------------------------
    2837       // GENERAL VARIABLE LOOKUP
    2838
    2839
    2840         //
    2841         // We are handling only unqualified variables here.  Therefore,
    2842         // we do not care about accessors, because local access doesn't
    2843         // go through them.  Therefore, precedence is as follows:
    2844         //   1) local variables, nearest block first
    2845         //   2) class fields
    2846         //   3) repeat search from 2) in next outer class
    2847
    2848         boolean  handled  = false;
    2849         Variable variable = (Variable)variableStack.get( variableName );
    2850
    2851         if( variable != null ) {
    2852
    2853             if( variable.isProperty() ) {
    2854                 processPropertyVariable(variable );
    2855             }
    2856             else {
    2857                 if (ENABLE_EARLY_BINDING && expression.isTypeResolved() && leftHandExpression) {
    2858                     // let's pass the type back to the variable
    2859                     String typeName = expression.getType();
    2860                     Type varOldType = variable.getType();
    2861                     if (varOldType.isDynamic()) {
    2862                         variable.setType(new Type(typeName, true));
    2863                     }
    2864                     else if (!varOldType.getName().equals(typeName)){
    2865                         new GroovyRuntimeException("VariableExpression data type conflicts with the existing variable. "
    2866                                 + "[" + expression.getLineNumber() + ":" + expression.getColumnNumber() + "]");
    2867                     }
    2868                 }
    2869                 processStackVariable(variable );
    2870             }
    2871
    2872             handled = true;
    2873         } else {
    2874             //
    2875             // Loop through outer classes for fields
    2876
    2877             int       steps   = 0;
    2878             ClassNode currentClassNode = classNode;
    2879             FieldNode field   = null;
    2880
    2881             do {
    2882                 if( (field = currentClassNode.getField(variableName)) != null ) {
    2883                     if (methodNode == null || !methodNode.isStatic() || field.isStatic() )
    2884                         break; //this is a match. break out. todo to be tested
    2885                 }
    2886                 steps++;
    2887
    2888             } while( (currentClassNode = currentClassNode.getOuterClass()) != null );
    2889
    2890             if( field != null ) {
    2891                 processFieldAccess( variableName, field, steps );
    2892                 handled = true;
    2893             }
    2894         }
    2895
    2896         //
    2897         // class names return a Class instance, too
    2898         if (!handled  && !variableName.equals("this")) {
    2899             String className = resolveClassName(variableName);
    2900             if (className != null) {
    2901                 if (leftHandExpression) {
    2902                     throwException("Cannot use a class expression on the left hand side of an assignment");
    2903                 }
    2904                 visitClassExpression(new ClassExpression(className));
    2905                 return;                                               // <<< FLOW CONTROL <<<<<<<<<
    2906             }
    2907         }
    2908
    2909         //
    2910         // Finally, if unhandled, create a variable for it.
    2911         // Except there a stack variable should be created,
    2912         // we define the variable as a property accessor and
    2913         // let other parts of the classgen report the error
    2914         // if the property doesn't exist.
    2915
    2916         if( !handled ) {
    2917             String variableType = expression.getType();
    2918             variable = defineVariable( variableName, variableType );
    2919
    2920             if (leftHandExpression && expression.isDynamic()) {
    2921                 variable.setDynamic(true); // false  by default
    2922             }
    2923             else {
    2924                 variable.setDynamic(false);
    2925             }
    2926
    2927             if( isInScriptBody() || !leftHandExpression ) { // todo problematic: if on right hand not defined, should I report undefined var error?
    2928                 variable.setProperty( true );
    2929                 processPropertyVariable(variable );
    2930             }
    2931             else {
    2932                 processStackVariable(variable );
    2933             }
    2934         }
    2935     }
    2936
    2937
    2938     protected void processStackVariable(Variable variable ) {
    2939         boolean holder = variable.isHolder() && !passingClosureParams;
    2940
    2941         if( leftHandExpression ) {
    2942             helper.storeVar(variable, holder);
    2943         }
    2944         else {
    2945         	helper.loadVar(variable, holder);
    2946         }
    2947         if (ASM_DEBUG) {
    2948             helper.mark("var: " + variable.getName());
    2949         }
    2950     }
    2951
    2952     private void visitVariableStartLabel(Variable variable) {
    2953         if (CREATE_DEBUG_INFO) {
    2954             Label l = variable.getStartLabel();
    2955             if (l != null) {
    2956                 cv.visitLabel(l);
    2957             } else {
    2958                 System.out.println("start label == null! what to do about this?");
    2959             }
    2960         }
    2961     }
    2962
    2963     protected void processPropertyVariable(Variable variable ) {
    2964     	String name = variable.getName();
    2965         if (variable.isHolder() && passingClosureParams && isInScriptBody() ) {
    2966             // lets create a ScriptReference to pass into the closure
    2967             cv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference");
    2968             cv.visitInsn(DUP);
    2969
    2970             loadThisOrOwner();
    2971             cv.visitLdcInsn(name);
    2972
    2973             cv.visitMethodInsn(
    2974                 INVOKESPECIAL,
    2975                 "org/codehaus/groovy/runtime/ScriptReference",
    2976                 "<init>",
    2977                 "(Lgroovy/lang/Script;Ljava/lang/String;)V");
    2978         }
    2979         else {
    2980             visitPropertyExpression(new PropertyExpression(VariableExpression.THIS_EXPRESSION, name));
    2981         }
    2982     }
    2983
    2984
    2985     protected void processFieldAccess( String name, FieldNode field, int steps ) {
    2986         FieldExpression expression = new FieldExpression(field);
    2987
    2988         if( steps == 0 ) {
    2989             visitFieldExpression( expression );
    2990         }
    2991         else {
    2992             visitOuterFieldExpression( expression, classNode.getOuterClass(), steps, true );
    2993         }
    2994     }
    2995
    2996
    2997
    2998     /***
    2999      * @return true if we are in a script body, where all variables declared are no longer
    3000      * local variables but are properties
    3001      */
    3002     protected boolean isInScriptBody() {
    3003         if (classNode.isScriptBody()) {
    3004             return true;
    3005         }
    3006         else {
    3007             return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
    3008         }
    3009     }
    3010
    3011     /***
    3012      * @return true if this expression will have left a value on the stack
    3013      * that must be popped
    3014      */
    3015     protected boolean isPopRequired(Expression expression) {
    3016         if (expression instanceof MethodCallExpression) {
    3017             if (expression.getType() != null && expression.getType().equals("void")) { // nothing on the stack
    3018                 return false;
    3019             } else {
    3020                 return !MethodCallExpression.isSuperMethodCall((MethodCallExpression) expression);
    3021             }
    3022         }
    3023         if (expression instanceof BinaryExpression) {
    3024             BinaryExpression binExp = (BinaryExpression) expression;
    3025             switch (binExp.getOperation().getType()) {   // br todo should leave a copy of the value on the stack for all the assignemnt.
    3026 //                case Types.EQUAL :   // br a copy of the right value is left on the stack (see evaluateEqual()) so a pop is required for a standalone assignment
    3027 //                case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAsignment()
    3028 //                case Types.MINUS_EQUAL :
    3029 //                case Types.MULTIPLY_EQUAL :
    3030 //                case Types.DIVIDE_EQUAL :
    3031 //                case Types.INTDIV_EQUAL :
    3032 //                case Types.MOD_EQUAL :
    3033 //                    return false;
    3034             }
    3035         }
    3036         return true;
    3037     }
    3038
    3039     protected boolean firstStatementIsSuperInit(Statement code) {
    3040         ExpressionStatement expStmt = null;
    3041         if (code instanceof ExpressionStatement) {
    3042             expStmt = (ExpressionStatement) code;
    3043         }
    3044         else if (code instanceof BlockStatement) {
    3045             BlockStatement block = (BlockStatement) code;
    3046             if (!block.getStatements().isEmpty()) {
    3047                 Object expr = block.getStatements().get(0);
    3048                 if (expr instanceof ExpressionStatement) {
    3049                     expStmt = (ExpressionStatement) expr;
    3050                 }
    3051             }
    3052         }
    3053         if (expStmt != null) {
    3054             Expression expr = expStmt.getExpression();
    3055             if (expr instanceof MethodCallExpression) {
    3056             	MethodCallExpression call = (MethodCallExpression) expr;
    3057                 if (MethodCallExpression.isSuperMethodCall(call)) {
    3058                     // not sure which one is constantly used as the super class ctor call. To cover both for now
    3059                 	return call.getMethod().equals("<init>") || call.getMethod().equals("super");
    3060                 }
    3061             }
    3062         }
    3063         return false;
    3064     }
    3065
    3066     protected void createSyntheticStaticFields() {
    3067         for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
    3068             String staticFieldName = (String) iter.next();
    3069             // generate a field node
    3070             cw.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null);
    3071         }
    3072
    3073         if (!syntheticStaticFields.isEmpty()) {
    3074             cv =
    3075                 cw.visitMethod(
    3076                     ACC_STATIC + ACC_SYNTHETIC,
    3077                     "class$",
    3078                     "(Ljava/lang/String;)Ljava/lang/Class;",
    3079                     null,
    3080                     null);
    3081             helper = new BytecodeHelper(cv);
    3082
    3083             Label l0 = new Label();
    3084             cv.visitLabel(l0);
    3085             cv.visitVarInsn(ALOAD, 0);
    3086             cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
    3087             Label l1 = new Label();
    3088             cv.visitLabel(l1);
    3089             cv.visitInsn(ARETURN);
    3090             Label l2 = new Label();
    3091             cv.visitLabel(l2);
    3092             cv.visitVarInsn(ASTORE, 1);
    3093             cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
    3094             cv.visitInsn(DUP);
    3095             cv.visitVarInsn(ALOAD, 1);
    3096             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;");
    3097             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V");
    3098             cv.visitInsn(ATHROW);
    3099             cv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
    3100             cv.visitMaxs(3, 2);
    3101
    3102             cw.visitEnd();
    3103         }
    3104     }
    3105     /*** load class object on stack */
    3106     public void visitClassExpression(ClassExpression expression) {
    3107         String type = expression.getText();
    3108         //type = checkValidType(type, expression, "Must be a valid type name for a constructor call");
    3109
    3110
    3111         if (BytecodeHelper.isPrimitiveType(type)) {
    3112             String objectType = BytecodeHelper.getObjectTypeForPrimitive(type);
    3113             cv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;");
    3114         }
    3115         else {
    3116             final String staticFieldName =
    3117                 (type.equals(classNode.getName())) ? "class$0" : "class$" + type.replace('.', '$').replace('[', '_').replace(';', '_');
    3118
    3119             syntheticStaticFields.add(staticFieldName);
    3120
    3121             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
    3122             Label l0 = new Label();
    3123             cv.visitJumpInsn(IFNONNULL, l0);
    3124             cv.visitLdcInsn(type);
    3125             cv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
    3126             cv.visitInsn(DUP);
    3127             cv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
    3128             Label l1 = new Label();
    3129             cv.visitJumpInsn(GOTO, l1);
    3130             cv.visitLabel(l0);
    3131             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
    3132             cv.visitLabel(l1);
    3133         }
    3134     }
    3135
    3136     public void visitRangeExpression(RangeExpression expression) {
    3137         leftHandExpression = false;
    3138         expression.getFrom().visit(this);
    3139
    3140         leftHandExpression = false;
    3141         expression.getTo().visit(this);
    3142
    3143         helper.pushConstant(expression.isInclusive());
    3144
    3145         createRangeMethod.call(cv);
    3146     }
    3147
    3148     public void visitMapEntryExpression(MapEntryExpression expression) {
    3149     }
    3150
    3151     public void visitMapExpression(MapExpression expression) {
    3152         List entries = expression.getMapEntryExpressions();
    3153         int size = entries.size();
    3154         helper.pushConstant(size * 2);
    3155
    3156         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    3157
    3158         int i = 0;
    3159         for (Iterator iter = entries.iterator(); iter.hasNext();) {
    3160             MapEntryExpression entry = (MapEntryExpression) iter.next();
    3161
    3162             cv.visitInsn(DUP);
    3163             helper.pushConstant(i++);
    3164             visitAndAutoboxBoolean(entry.getKeyExpression());
    3165             cv.visitInsn(AASTORE);
    3166
    3167             cv.visitInsn(DUP);
    3168             helper.pushConstant(i++);
    3169             visitAndAutoboxBoolean(entry.getValueExpression());
    3170             cv.visitInsn(AASTORE);
    3171         }
    3172         createMapMethod.call(cv);
    3173     }
    3174
    3175     public void visitTupleExpression(TupleExpression expression) {
    3176         int size = expression.getExpressions().size();
    3177
    3178         helper.pushConstant(size);
    3179
    3180         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    3181
    3182         for (int i = 0; i < size; i++) {
    3183             cv.visitInsn(DUP);
    3184             helper.pushConstant(i);
    3185             visitAndAutoboxBoolean(expression.getExpression(i));
    3186             cv.visitInsn(AASTORE);
    3187         }
    3188         //createTupleMethod.call(cv);
    3189     }
    3190
    3191     public void visitArrayExpression(ArrayExpression expression) {
    3192         String type = expression.getElementType();
    3193         String typeName = BytecodeHelper.getClassInternalName(type);
    3194         Expression sizeExpression = expression.getSizeExpression();
    3195         if (sizeExpression != null) {
    3196             // lets convert to an int
    3197             visitAndAutoboxBoolean(sizeExpression);
    3198             asIntMethod.call(cv);
    3199
    3200             cv.visitTypeInsn(ANEWARRAY, typeName);
    3201         }
    3202         else {
    3203             int size = expression.getExpressions().size();
    3204             helper.pushConstant(size);
    3205
    3206             cv.visitTypeInsn(ANEWARRAY, typeName);
    3207
    3208             for (int i = 0; i < size; i++) {
    3209                 cv.visitInsn(DUP);
    3210                 helper.pushConstant(i);
    3211                 Expression elementExpression = expression.getExpression(i);
    3212                 if (elementExpression == null) {
    3213                     ConstantExpression.NULL.visit(this);
    3214                 }
    3215                 else {
    3216
    3217                     if(!type.equals(elementExpression.getClass().getName())) {
    3218                         visitCastExpression(new CastExpression(type, elementExpression));
    3219                     }
    3220                     else {
    3221                         visitAndAutoboxBoolean(elementExpression);
    3222                     }
    3223                 }
    3224                 cv.visitInsn(AASTORE);
    3225             }
    3226         }
    3227     }
    3228
    3229     public void visitListExpression(ListExpression expression) {
    3230         int size = expression.getExpressions().size();
    3231         helper.pushConstant(size);
    3232
    3233         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    3234
    3235         for (int i = 0; i < size; i++) {
    3236             cv.visitInsn(DUP);
    3237             helper.pushConstant(i);
    3238             visitAndAutoboxBoolean(expression.getExpression(i));
    3239             cv.visitInsn(AASTORE);
    3240         }
    3241         createListMethod.call(cv);
    3242     }
    3243
    3244     public void visitGStringExpression(GStringExpression expression) {
    3245         int size = expression.getValues().size();
    3246         helper.pushConstant(size);
    3247
    3248         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
    3249
    3250         for (int i = 0; i < size; i++) {
    3251             cv.visitInsn(DUP);
    3252             helper.pushConstant(i);
    3253             visitAndAutoboxBoolean(expression.getValue(i));
    3254             cv.visitInsn(AASTORE);
    3255         }
    3256
    3257         Variable tv = visitASTOREInTemp("iterator");
    3258         int paramIdx = tv.getIndex();
    3259
    3260         ClassNode innerClass = createGStringClass(expression);
    3261         addInnerClass(innerClass);
    3262         String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName());
    3263
    3264         cv.visitTypeInsn(NEW, innerClassinternalName);
    3265         cv.visitInsn(DUP);
    3266         cv.visitVarInsn(ALOAD, paramIdx);
    3267
    3268         cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", "([Ljava/lang/Object;)V");
    3269         removeVar(tv);
    3270     }
    3271
    3272     private Variable visitASTOREInTemp(String s) {
    3273         return storeInTemp(s, "java.lang.Object");
    3274     }
    3275
    3276     // Implementation methods
    3277     //-------------------------------------------------------------------------
    3278     protected boolean addInnerClass(ClassNode innerClass) {
    3279         innerClass.setModule(classNode.getModule());
    3280         return innerClasses.add(innerClass);
    3281     }
    3282
    3283     protected ClassNode createClosureClass(ClosureExpression expression) {
    3284         ClassNode owner = getOutermostClass();
    3285         boolean parentIsInnerClass = owner instanceof InnerClassNode;
    3286         String outerClassName = owner.getName();
    3287         String name = outerClassName + "$"
    3288                 + context.getNextClosureInnerName(owner, classNode, methodNode); // br added a more infomative name
    3289         boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass();
    3290         if (staticMethodOrInStaticClass) {
    3291             outerClassName = "java.lang.Class";
    3292         }
    3293         Parameter[] parameters = expression.getParameters();
    3294         if (parameters == null || parameters.length == 0) {
    3295             // lets create a default 'it' parameter
    3296             parameters = new Parameter[] { new Parameter("it")};
    3297         }
    3298
    3299         Parameter[] localVariableParams = getClosureSharedVariables(expression);
    3300
    3301         InnerClassNode answer = new InnerClassNode(owner, name, ACC_SUPER, "groovy.lang.Closure"); // clsures are local inners and not public
    3302         answer.setEnclosingMethod(this.methodNode);
    3303         if (staticMethodOrInStaticClass) {
    3304             answer.setStaticClass(true);
    3305         }
    3306         if (isInScriptBody()) {
    3307             answer.setScriptBody(true);
    3308         }
    3309         MethodNode method =
    3310             answer.addMethod("doCall", ACC_PUBLIC, "java.lang.Object", parameters, expression.getCode());
    3311
    3312         method.setLineNumber(expression.getLineNumber());
    3313         method.setColumnNumber(expression.getColumnNumber());
    3314
    3315         VariableScope varScope = expression.getVariableScope();
    3316         if (varScope == null) {
    3317             throw new RuntimeException(
    3318                 "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
    3319         }
    3320         else {
    3321             method.setVariableScope(varScope);
    3322         }
    3323         if (parameters.length > 1
    3324             || (parameters.length == 1
    3325                 && parameters[0].getType() != null
    3326                 && !parameters[0].getType().equals("java.lang.Object"))) {
    3327
    3328             // lets add a typesafe call method
    3329             answer.addMethod(
    3330                 "call",
    3331                 ACC_PUBLIC,
    3332                 "java.lang.Object",
    3333                 parameters,
    3334                 new ReturnStatement(
    3335                     new MethodCallExpression(
    3336                         VariableExpression.THIS_EXPRESSION,
    3337                         "doCall",
    3338                         new ArgumentListExpression(parameters))));
    3339         }
    3340
    3341         FieldNode ownerField = answer.addField("owner", ACC_PRIVATE, outerClassName, null);
    3342
    3343         // lets make the constructor
    3344         BlockStatement block = new BlockStatement();
    3345         block.addStatement(
    3346             new ExpressionStatement(
    3347                 new MethodCallExpression(
    3348                     new VariableExpression("super"),
    3349                     "<init>",
    3350                     new VariableExpression("_outerInstance"))));
    3351         block.addStatement(
    3352             new ExpressionStatement(
    3353                 new BinaryExpression(
    3354                     new FieldExpression(ownerField),
    3355                     Token.newSymbol(Types.EQUAL, -1, -1),
    3356                     new VariableExpression("_outerInstance"))));
    3357
    3358         // lets assign all the parameter fields from the outer context
    3359         for (int i = 0; i < localVariableParams.length; i++) {
    3360             Parameter param = localVariableParams[i];
    3361             String paramName = param.getName();
    3362             boolean holder = mutableVars.contains(paramName);
    3363             Expression initialValue = null;
    3364             String type = param.getType();
    3365             FieldNode paramField = null;
    3366             if (holder) {
    3367             	initialValue = new VariableExpression(paramName);
    3368                 type = Reference.class.getName();
    3369                 param.makeReference();
    3370                 paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue);
    3371                 paramField.setHolder(true);
    3372                 String realType = param.getRealType();
    3373                 String methodName = Verifier.capitalize(paramName);
    3374
    3375                 // lets add a getter & setter
    3376                 Expression fieldExp = new FieldExpression(paramField);
    3377                 answer.addMethod(
    3378                     "get" + methodName,
    3379                     ACC_PUBLIC,
    3380                     realType,
    3381                     Parameter.EMPTY_ARRAY,
    3382                     new ReturnStatement(fieldExp));
    3383
    3384                 /*
    3385                 answer.addMethod(
    3386                     "set" + methodName,
    3387                     ACC_PUBLIC,
    3388                     "void",
    3389                     new Parameter[] { new Parameter(realType, "__value") },
    3390                     new ExpressionStatement(
    3391                         new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
    3392                         */
    3393             }
    3394             else {
    3395             	PropertyNode propertyNode = answer.addProperty(paramName, ACC_PUBLIC, type, initialValue, null, null);
    3396                 paramField = propertyNode.getField();
    3397                 block.addStatement(
    3398                     new ExpressionStatement(
    3399                         new BinaryExpression(
    3400                             new FieldExpression(paramField),
    3401                             Token.newSymbol(Types.EQUAL, -1, -1),
    3402                             new VariableExpression(paramName))));
    3403             }
    3404         }
    3405
    3406         Parameter[] params = new Parameter[2 + localVariableParams.length];
    3407         params[0] = new Parameter(outerClassName, "_outerInstance");
    3408         params[1] = new Parameter("java.lang.Object", "_delegate");
    3409         System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
    3410
    3411         answer.addConstructor(ACC_PUBLIC, params, block);
    3412         return answer;
    3413     }
    3414
    3415     protected ClassNode getOutermostClass() {
    3416         if (outermostClass == null) {
    3417             outermostClass = classNode;
    3418             while (outermostClass instanceof InnerClassNode) {
    3419                 outermostClass = outermostClass.getOuterClass();
    3420             }
    3421         }
    3422         return outermostClass;
    3423     }
    3424
    3425     protected ClassNode createGStringClass(GStringExpression expression) {
    3426         ClassNode owner = classNode;
    3427         if (owner instanceof InnerClassNode) {
    3428             owner = owner.getOuterClass();
    3429         }
    3430         String outerClassName = owner.getName();
    3431         String name = outerClassName + "$" + context.getNextInnerClassIdx();
    3432         InnerClassNode answer = new InnerClassNode(owner, name, ACC_SUPER, GString.class.getName());
    3433         answer.setEnclosingMethod(this.methodNode);
    3434         FieldNode stringsField =
    3435             answer.addField(
    3436                 "strings",
    3437                 ACC_PRIVATE /*| ACC_STATIC*/,
    3438                 "java.lang.String[]",
    3439                 new ArrayExpression("java.lang.String", expression.getStrings()));
    3440         answer.addMethod(
    3441             "getStrings",
    3442             ACC_PUBLIC,
    3443             "java.lang.String[]",
    3444             Parameter.EMPTY_ARRAY,
    3445             new ReturnStatement(new FieldExpression(stringsField)));
    3446         // lets make the constructor
    3447         BlockStatement block = new BlockStatement();
    3448         block.addStatement(
    3449             new ExpressionStatement(
    3450                 new MethodCallExpression(new VariableExpression("super"), "<init>", new VariableExpression("values"))));
    3451         Parameter[] contructorParams = new Parameter[] { new Parameter("java.lang.Object[]", "values")};
    3452         answer.addConstructor(ACC_PUBLIC, contructorParams, block);
    3453         return answer;
    3454     }
    3455
    3456     protected void doConvertAndCast(String type) {
    3457         if (!type.equals("java.lang.Object")) {
    3458             /*** todo should probably support array coercions */
    3459             if (!type.endsWith("[]") && isValidTypeForCast(type)) {
    3460                 visitClassExpression(new ClassExpression(type));
    3461                 asTypeMethod.call(cv);
    3462             }
    3463
    3464             helper.doCast(type);
    3465         }
    3466     }
    3467
    3468     protected void evaluateLogicalOrExpression(BinaryExpression expression) {
    3469         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
    3470         Label l0 = new Label();
    3471         Label l2 = new Label();
    3472         cv.visitJumpInsn(IFEQ, l0);
    3473
    3474         cv.visitLabel(l2);
    3475
    3476         visitConstantExpression(ConstantExpression.TRUE);
    3477
    3478         Label l1 = new Label();
    3479         cv.visitJumpInsn(GOTO, l1);
    3480         cv.visitLabel(l0);
    3481
    3482         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
    3483
    3484         cv.visitJumpInsn(IFNE, l2);
    3485
    3486         visitConstantExpression(ConstantExpression.FALSE);
    3487         cv.visitLabel(l1);
    3488     }
    3489
    3490     // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for
    3491     // consistancy.
    3492     protected void evaluateLogicalAndExpression(BinaryExpression expression) {
    3493         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
    3494         Label l0 = new Label();
    3495         cv.visitJumpInsn(IFEQ, l0);
    3496
    3497         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
    3498
    3499         cv.visitJumpInsn(IFEQ, l0);
    3500
    3501         visitConstantExpression(ConstantExpression.TRUE);
    3502
    3503         Label l1 = new Label();
    3504         cv.visitJumpInsn(GOTO, l1);
    3505         cv.visitLabel(l0);
    3506
    3507         visitConstantExpression(ConstantExpression.FALSE);
    3508
    3509         cv.visitLabel(l1);
    3510     }
    3511
    3512     protected void evaluateBinaryExpression(String method, BinaryExpression expression) {
    3513         Expression leftExpression = expression.getLeftExpression();
    3514         leftHandExpression = false;
    3515         leftExpression.visit(this);
    3516         cv.visitLdcInsn(method);
    3517         leftHandExpression = false;
    3518         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}).visit(this);
    3519         // expression.getRightExpression().visit(this);
    3520         invokeMethodMethod.call(cv);
    3521     }
    3522
    3523     protected void evaluateCompareTo(BinaryExpression expression) {
    3524         Expression leftExpression = expression.getLeftExpression();
    3525         leftHandExpression = false;
    3526         leftExpression.visit(this);
    3527         expression.getRightExpression().visit(this);
    3528         compareToMethod.call(cv);
    3529     }
    3530
    3531     protected void evaluateBinaryExpressionWithAsignment(String method, BinaryExpression expression) {
    3532         Expression leftExpression = expression.getLeftExpression();
    3533         if (leftExpression instanceof BinaryExpression) {
    3534             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
    3535             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
    3536                 // lets replace this assignment to a subscript operator with a
    3537                 // method call
    3538                 // e.g. x[5] += 10
    3539                 // -> (x, [], 5), =, x[5] + 10
    3540                 // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
    3541
    3542                 MethodCallExpression methodCall =
    3543                     new MethodCallExpression(
    3544                         expression.getLeftExpression(),
    3545                         method,
    3546                         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}));
    3547
    3548                 Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression());
    3549
    3550                 visitMethodCallExpression(
    3551                     new MethodCallExpression(
    3552                         leftBinExpr.getLeftExpression(),
    3553                         "putAt",
    3554                         new ArgumentListExpression(new Expression[] { safeIndexExpr, methodCall })));
    3555                 //cv.visitInsn(POP);
    3556                 return;
    3557             }
    3558         }
    3559
    3560         evaluateBinaryExpression(method, expression);
    3561
    3562         // br to leave a copy of rvalue on the stack. see also isPopRequired()
    3563         cv.visitInsn(DUP);
    3564
    3565         leftHandExpression = true;
    3566         evaluateExpression(leftExpression);
    3567         leftHandExpression = false;
    3568     }
    3569
    3570     private void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression bin) {
    3571         if (ENABLE_EARLY_BINDING && true) {
    3572             evalBinaryExp_EarlyBinding(compareMethod, bin);
    3573         }
    3574         else {
    3575             evalBinaryExp_LateBinding(compareMethod, bin);
    3576         }
    3577     }
    3578
    3579     protected void evalBinaryExp_LateBinding(MethodCaller compareMethod, BinaryExpression expression) {
    3580         Expression leftExp = expression.getLeftExpression();
    3581         Expression rightExp = expression.getRightExpression();
    3582         load(leftExp);
    3583         load(rightExp);
    3584         compareMethod.call(cv);
    3585     }
    3586
    3587     /***
    3588      * note: leave the primitive boolean on staock for comparison expressions. All the result types need to match the
    3589      * utility methods in the InvokerHelper.
    3590      * @param compareMethod
    3591      * @param expression
    3592      */
    3593     protected void evalBinaryExp_EarlyBinding(MethodCaller compareMethod, BinaryExpression expression) {
    3594         Expression leftExp = expression.getLeftExpression();
    3595         Expression rightExp = expression.getRightExpression();
    3596
    3597         expression.resolve(this);
    3598         if (expression.isResolveFailed() || expression.getTypeClass() == null){
    3599             evalBinaryExp_LateBinding(compareMethod, expression);
    3600             return;
    3601         }
    3602         else {
    3603             Class lclass = leftExp.getTypeClass();
    3604             Class rclass = rightExp.getTypeClass();
    3605             if (lclass == null || rclass == null) {
    3606                 if ((lclass == null && rclass != null) || (lclass != null && rclass == null)) {
    3607                     // lets treat special cases: obj == null / obj != null . leave primitive boolean on the stack, which will be boxed by visitAndAutoBox()
    3608                     if (leftExp == ConstantExpression.NULL && !rclass.isPrimitive() ||
    3609                             rightExp == ConstantExpression.NULL && !lclass.isPrimitive()) {
    3610                         Expression exp = leftExp == ConstantExpression.NULL? rightExp : leftExp;
    3611                         int type = expression.getOperation().getType();
    3612                         switch (type) {
    3613                             case Types.COMPARE_EQUAL :
    3614                                 load(exp);
    3615                                 cv.visitInsn(ICONST_1);
    3616                                 cv.visitInsn(SWAP);
    3617                                 Label l1 = new Label();
    3618                                 cv.visitJumpInsn(IFNULL, l1);
    3619                                 cv.visitInsn(POP);
    3620                                 cv.visitInsn(ICONST_0);
    3621                                 cv.visitLabel(l1);
    3622                                 return;
    3623                             case Types.COMPARE_NOT_EQUAL :
    3624                                 load(exp);
    3625                                 cv.visitInsn(ICONST_1);
    3626                                 cv.visitInsn(SWAP);
    3627                                 Label l2 = new Label();
    3628                                 cv.visitJumpInsn(IFNONNULL, l2);
    3629                                 cv.visitInsn(POP);
    3630                                 cv.visitInsn(ICONST_0);
    3631                                 cv.visitLabel(l2);
    3632                                 return;
    3633                             default:
    3634                                 evalBinaryExp_LateBinding(compareMethod, expression);
    3635                                 return;
    3636                         }
    3637                     }
    3638                     else {
    3639                         evalBinaryExp_LateBinding(compareMethod, expression);
    3640                         return;
    3641                     }
    3642                 }
    3643                 else {
    3644                     evalBinaryExp_LateBinding(compareMethod, expression);
    3645                     return;
    3646                 }
    3647             }
    3648             else if (lclass == String.class && rclass == String.class) {
    3649                 int type = expression.getOperation().getType();
    3650                 switch (type) {
    3651                     case Types.COMPARE_EQUAL : // ==
    3652                         load(leftExp); cast(String.class);
    3653                         load(rightExp); cast(String.class);
    3654                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z");
    3655                         //helper.quickBoxIfNecessary(boolean.class);
    3656                         return;
    3657                     case Types.COMPARE_NOT_EQUAL :
    3658                         load(leftExp);cast(String.class);
    3659                         load(rightExp); cast(String.class);
    3660                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z");
    3661                         cv.visitInsn(ICONST_1);
    3662                         cv.visitInsn(IXOR);
    3663                         //helper.quickBoxIfNecessary(boolean.class);
    3664                         return;
    3665                     case Types.COMPARE_TO :
    3666                         load(leftExp);cast(String.class);
    3667                         load(rightExp); cast(String.class);
    3668                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I");
    3669                         helper.quickBoxIfNecessary(int.class); // object type
    3670                         return;
    3671                     case Types.COMPARE_GREATER_THAN :
    3672                     case Types.COMPARE_GREATER_THAN_EQUAL :
    3673                     case Types.COMPARE_LESS_THAN :
    3674                     case Types.COMPARE_LESS_THAN_EQUAL :
    3675                         {
    3676                             int op;
    3677                             switch (type) {
    3678                                 case Types.COMPARE_GREATER_THAN :
    3679                                     op = IFLE;
    3680                                     break;
    3681                                 case Types.COMPARE_GREATER_THAN_EQUAL :
    3682                                     op = IFLT;
    3683                                     break;
    3684                                 case Types.COMPARE_LESS_THAN :
    3685                                     op = IFGE;
    3686                                     break;
    3687                                 case Types.COMPARE_LESS_THAN_EQUAL :
    3688                                     op = IFGT;
    3689                                     break;
    3690                                 default:
    3691                                     System.err.println("flow control error: should not be here. type: " + type);
    3692                                     return;
    3693                             }
    3694                             load(leftExp);cast(String.class);
    3695                             load(rightExp); cast(String.class);
    3696                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I");
    3697
    3698                             // set true/false on stack
    3699                             Label l4 = new Label();
    3700                             cv.visitJumpInsn(op, l4);
    3701                             // need to use primitive boolean //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
    3702                             cv.visitInsn(ICONST_1);  // true
    3703                             Label l5 = new Label();
    3704                             cv.visitJumpInsn(GOTO, l5);
    3705                             cv.visitLabel(l4);
    3706                             cv.visitInsn(ICONST_0); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
    3707                             cv.visitLabel(l5);
    3708                         }
    3709                         return;
    3710
    3711                     default:
    3712                         evalBinaryExp_LateBinding(compareMethod, expression);
    3713                         return;
    3714                 }
    3715             }
    3716             else if (Integer.class == lclass && Integer.class == rclass) {
    3717                 int type = expression.getOperation().getType();
    3718                 switch (type) {
    3719                     case Types.COMPARE_EQUAL : // ==
    3720                         load(leftExp); cast(Integer.class);
    3721                         load(rightExp);
    3722                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z");
    3723                         //helper.quickBoxIfNecessary(boolean.class);
    3724                         return;
    3725                     case Types.COMPARE_NOT_EQUAL :
    3726                         load(leftExp); cast(Integer.class);
    3727                         load(rightExp);
    3728                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z");
    3729                         cv.visitInsn(ICONST_1);
    3730                         cv.visitInsn(IXOR);
    3731                         //helper.quickBoxIfNecessary(boolean.class);
    3732                         return;
    3733                     case Types.COMPARE_TO :
    3734                         load(leftExp); cast(Integer.class);
    3735                         load(rightExp);
    3736                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I");
    3737                         helper.quickBoxIfNecessary(int.class);
    3738                         return;
    3739                     case Types.COMPARE_GREATER_THAN :
    3740                     case Types.COMPARE_GREATER_THAN_EQUAL :
    3741                     case Types.COMPARE_LESS_THAN :
    3742                     case Types.COMPARE_LESS_THAN_EQUAL :
    3743                         {
    3744                             int op;
    3745                             switch (type) {
    3746                                 case Types.COMPARE_GREATER_THAN :
    3747                                     op = IFLE;
    3748                                     break;
    3749                                 case Types.COMPARE_GREATER_THAN_EQUAL :
    3750                                     op = IFLT;
    3751                                     break;
    3752                                 case Types.COMPARE_LESS_THAN :
    3753                                     op = IFGE;
    3754                                     break;
    3755                                 case Types.COMPARE_LESS_THAN_EQUAL :
    3756                                     op = IFGT;
    3757                                     break;
    3758                                 default:
    3759                                     System.err.println("flow control error: should not be here. type: " + type);
    3760                                     return;
    3761                             }
    3762                             load(leftExp); cast(Integer.class);
    3763                             load(rightExp);
    3764                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I");
    3765
    3766                             Label l4 = new Label();
    3767                             cv.visitJumpInsn(op, l4);
    3768                             cv.visitInsn(ICONST_1); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
    3769                             Label l5 = new Label();
    3770                             cv.visitJumpInsn(GOTO, l5);
    3771                             cv.visitLabel(l4);
    3772                             cv.visitInsn(ICONST_0);//cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
    3773                             cv.visitLabel(l5);
    3774                         }
    3775                         return;
    3776
    3777                     default:
    3778                         evalBinaryExp_LateBinding(compareMethod, expression);
    3779                         return;
    3780                 }
    3781             }
    3782             else {
    3783                 evalBinaryExp_LateBinding(compareMethod, expression);
    3784                 return;
    3785             }
    3786         }
    3787     }
    3788
    3789     private void cast(Class aClass) {
    3790         if (!aClass.isPrimitive() && aClass != Object.class) {
    3791             cv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(aClass.getName()));
    3792         }
    3793     }
    3794
    3795     protected void evaluateEqual(BinaryExpression expression) {
    3796         if (ENABLE_EARLY_BINDING) {
    3797             expression.resolve(this);
    3798             if (expression.isTypeResolved()) {
    3799                 if (expression.getRightExpression().getTypeClass() == Void.TYPE) {
    3800                     throwException("void value appeared on right hand side of assignment. ");
    3801                 }
    3802             }
    3803         }
    3804
    3805         Expression leftExpression = expression.getLeftExpression();
    3806         if (leftExpression instanceof BinaryExpression) {
    3807             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
    3808             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
    3809                 // lets replace this assignment to a subscript operator with a
    3810                 // method call
    3811                 // e.g. x[5] = 10
    3812                 // -> (x, [], 5), =, 10
    3813                 // -> methodCall(x, "putAt", [5, 10])
    3814                 do {
    3815                     if (true && ENABLE_EARLY_BINDING){
    3816                         Class typeclass = leftBinExpr.getLeftExpression().getTypeClass();
    3817                         if (typeclass == null) {
    3818                             break;
    3819                         }
    3820
    3821                         if (typeclass == Map.class) {// call aMap.put()
    3822                             load(expression.getRightExpression());
    3823                             // let's leave a copy of the value on the stack.
    3824                             cv.visitInsn(DUP);
    3825                             final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType());
    3826                             // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType());
    3827                             final Class rclass = expression.getRightExpression().getTypeClass();
    3828                             BytecodeExpression loadTempByteCode = new BytecodeExpression() {
    3829                                 public void visit(GroovyCodeVisitor visitor) {
    3830                                     cv.visitVarInsn(ALOAD, rightTemp.getIndex());
    3831                                 }
    3832                                 protected void resolveType(AsmClassGenerator2 resolver) {
    3833                                     setTypeClass(rclass);
    3834                                 }
    3835                             };
    3836
    3837                             visitMethodCallExpression(
    3838                                     new MethodCallExpression(
    3839                                             leftBinExpr.getLeftExpression(),
    3840                                             "put",
    3841                                             new ArgumentListExpression(
    3842                                                     new Expression[] {
    3843                                                         leftBinExpr.getRightExpression(),
    3844                                                         loadTempByteCode})));
    3845                             cv.visitInsn(POP); // pop the put method return
    3846                             removeVar(rightTemp);
    3847                             return;
    3848                         }
    3849                         else if (typeclass == List.class){
    3850                             // call DefaultGroovyMethods.putAt()V
    3851                             // DefaultGroovyMethods.putAt(x, 5, "c"); this is faster thangoing thru metaclass
    3852                             // this method does not return any value. so indicate this fact in the expression
    3853
    3854                             load(expression.getRightExpression());
    3855                             // let's leave a copy of the value on the stack. this is really lazy.
    3856                             cv.visitInsn(DUP);
    3857                             final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType());
    3858                             // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType());
    3859                             final Class rclass = expression.getRightExpression().getTypeClass();
    3860                             BytecodeExpression loadTempBytes = new BytecodeExpression() {
    3861                                 public void visit(GroovyCodeVisitor visitor) {
    3862                                     cv.visitVarInsn(ALOAD, rightTemp.getIndex());
    3863                                 }
    3864                                 protected void resolveType(AsmClassGenerator2 resolver) {
    3865                                     setTypeClass(rclass);
    3866                                 }
    3867                             };
    3868
    3869                             visitMethodCallExpression(
    3870                                 new MethodCallExpression(
    3871                                     new ClassExpression(DefaultGroovyMethods.class),
    3872                                     "putAt",
    3873                                     new ArgumentListExpression(
    3874                                         new Expression[] {
    3875                                             leftBinExpr.getLeftExpression(),
    3876                                             leftBinExpr.getRightExpression(),
    3877                                             loadTempBytes })));
    3878                             removeVar(rightTemp);
    3879                             return;
    3880
    3881                         }
    3882                         else {
    3883                             break;
    3884                         }
    3885                     }
    3886                 } while (false);
    3887
    3888                 visitMethodCallExpression(
    3889                     new MethodCallExpression(
    3890                         leftBinExpr.getLeftExpression(),
    3891                         "putAt",
    3892                         new ArgumentListExpression(
    3893                             new Expression[] { leftBinExpr.getRightExpression(), expression.getRightExpression()})));
    3894                  // cv.visitInsn(POP); //this is realted to isPopRequired()
    3895                 return;
    3896             }
    3897         }
    3898
    3899         // lets evaluate the RHS then hopefully the LHS will be a field
    3900         leftHandExpression = false;
    3901         Expression rightExpression = expression.getRightExpression();
    3902
    3903         String type = getLHSType(leftExpression);
    3904         if (type != null) {
    3905             //System.out.println("### expression: " + leftExpression);
    3906             //System.out.println("### type: " + type);
    3907
    3908             // lets not cast for primitive types as we handle these in field setting etc
    3909             if (BytecodeHelper.isPrimitiveType(type)) {
    3910                 rightExpression.visit(this);
    3911             }
    3912             else {
    3913                 if (ENABLE_EARLY_BINDING) {
    3914                     if (leftExpression.isDynamic()) { // br the previous if() probably should check this too!
    3915                         visitAndAutoboxBoolean(rightExpression);
    3916                     }
    3917                     else {
    3918                         if (type.equals(rightExpression.getType())) {
    3919                             visitAndAutoboxBoolean(rightExpression);
    3920                         }
    3921                         else {
    3922                             if (rightExpression instanceof ConstantExpression &&
    3923                                     ((ConstantExpression)rightExpression).getValue() == null) {
    3924                                 cv.visitInsn(ACONST_NULL);
    3925                             }
    3926                             else {
    3927                                 visitCastExpression(new CastExpression(type, rightExpression));
    3928                             }
    3929                         }
    3930                     }
    3931                 }
    3932                 else if (!type.equals("java.lang.Object")){
    3933                     visitCastExpression(new CastExpression(type, rightExpression));
    3934                 }
    3935                 else {
    3936                     visitAndAutoboxBoolean(rightExpression);
    3937                 }
    3938             }
    3939         }
    3940         else {
    3941             visitAndAutoboxBoolean(rightExpression);
    3942         }
    3943
    3944
    3945         // br: attempt to pass type info from right to left for assignment
    3946         if (ENABLE_EARLY_BINDING) {
    3947             Class rc = rightExpression.getTypeClass();
    3948             if (rc != null && rc.isArray()) {
    3949                 Class elemType = rc.getComponentType();
    3950                 if (elemType.isPrimitive()) {
    3951                     visitClassExpression(new ClassExpression(elemType));
    3952                     convertPrimitiveArray.call(cv);
    3953                     cast(loadClass(BytecodeHelper.getObjectArrayTypeForPrimitiveArray(elemType.getName() + "[]")));
    3954                 }
    3955             }
    3956
    3957
    3958             if (leftExpression.isDynamic() ) {
    3959                 // propagate the type from right to left if the left is dynamic
    3960                 if (!(leftExpression instanceof FieldExpression ) && !(leftExpression instanceof PropertyExpression))
    3961                     copyTypeClass(leftExpression, rightExpression);
    3962             }
    3963             else {
    3964                 Class lc = leftExpression.getTypeClass();
    3965 //                Class rc = rightExpression.getTypeClass();
    3966                 if (lc != null && rc != null && !lc.isAssignableFrom(rc) && !lc.isPrimitive()) {
    3967                     // let's use extended conversion logic in the invoker class.
    3968                     if (!lc.isArray()) {
    3969                         visitClassExpression(new ClassExpression(lc));
    3970                         asTypeMethod.call(cv);
    3971                         helper.doCast(lc);
    3972                     }
    3973                     else {
    3974                         // may not need this, since variable type converts primitive array to object array automatically
    3975                         Class elemType = lc.getComponentType();
    3976                         if (elemType.isPrimitive()) {
    3977                             // let's allow type copy for primitive array, meaning [i can be changed to [Integer
    3978                             copyTypeClass(leftExpression, rightExpression);
    3979                         }
    3980                     }
    3981                 }
    3982             }
    3983         }
    3984         cv.visitInsn(DUP);  // to leave a copy of the rightexpression value on the stack after the assignment.
    3985         leftHandExpression = true;
    3986         leftExpression.visit(this);
    3987         leftHandExpression = false;
    3988     }
    3989
    3990     private void copyTypeClass(Expression leftExpression, Expression rightExpression) {
    3991         // copy type class from the right to the left, boxing numbers & treat ClassExpression specially
    3992         Class rclass = rightExpression.getTypeClass();
    3993         if (rightExpression instanceof ClassExpression) {
    3994             leftExpression.setTypeClass(Class.class);
    3995         }
    3996         else {
    3997             rclass = BytecodeHelper.boxOnPrimitive(rclass);
    3998             leftExpression.setTypeClass(rclass);
    3999         }
    4000     }
    4001
    4002     private boolean canBeAssignedFrom(String ltype, String rtype) {
    4003         if (rtype == null) {
    4004             return false;
    4005         }
    4006         else if (ltype == null || ltype.equals("java.lang.Object")) {
    4007             return true;
    4008         } else {
    4009             return false;
    4010         }
    4011     }
    4012
    4013     private boolean canBeAssignedFrom(Expression l, Expression r) {
    4014             if (r.getTypeClass() == null) {
    4015                 return false;
    4016             }
    4017             else if (l.isDynamic()){
    4018                 return true;
    4019             } else {
    4020                 return false;
    4021             }
    4022         }
    4023     private boolean canBeAssignedFrom(Class l, Class r) {
    4024             if (r == null) {
    4025                 return false;
    4026             }
    4027             else if (l == null || l == Object.class){
    4028                 return true;
    4029             } else {
    4030                 return false;
    4031             }
    4032         }
    4033
    4034     /***
    4035      * Deduces the type name required for some casting
    4036      *
    4037      * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
    4038      */
    4039     protected String getLHSType(Expression leftExpression) {
    4040         do {
    4041 // commented out. not quiteworking yet. would complain something like:
    4042 //java.lang.ClassFormatError: Foo$1 (Illegal Field name "class$[Ljava$lang$String;")
    4043 //
    4044 //            if (ENABLE_EARLY_BINDING) {
    4045 //                String type = leftExpression.getType();
    4046 //                if (type == null)
    4047 //                    break;
    4048 //                return isValidTypeForCast(type) ? type : null;
    4049 //            }
    4050         } while (false);
    4051
    4052         if (leftExpression instanceof VariableExpression) {
    4053             VariableExpression varExp = (VariableExpression) leftExpression;
    4054             String type = varExp.getType();
    4055             if (isValidTypeForCast(type)) {
    4056                 return type;
    4057             }
    4058             String variableName = varExp.getVariable();
    4059             Variable variable = (Variable) variableStack.get(variableName);
    4060             if (variable != null) {
    4061                 if (variable.isHolder() || variable.isProperty()) {
    4062                     return null;
    4063                 }
    4064                 type = variable.getTypeName();
    4065                 if (isValidTypeForCast(type)) {
    4066                     return type;
    4067                 }
    4068             }
    4069             else {
    4070                 FieldNode field = classNode.getField(variableName);
    4071                 if (field == null) {
    4072                     field = classNode.getOuterField(variableName);
    4073                 }
    4074                 if (field != null) {
    4075                     type = field.getType();
    4076                     if (!field.isHolder() && isValidTypeForCast(type)) {
    4077                         return type;
    4078                     }
    4079                 }
    4080             }
    4081         }
    4082         return null;
    4083     }
    4084
    4085     protected boolean isValidTypeForCast(String type) {
    4086         return type != null && !type.equals("java.lang.Object") && !type.equals("groovy.lang.Reference") && !BytecodeHelper.isPrimitiveType(type);
    4087     }
    4088
    4089     protected void visitAndAutoboxBoolean(Expression expression) {
    4090         expression.visit(this);
    4091
    4092         if (isComparisonExpression(expression)) {
    4093             helper.boxBoolean(); // convert boolean to Boolean
    4094         }
    4095     }
    4096
    4097     protected void evaluatePrefixMethod(String method, Expression expression) {
    4098         if (isNonStaticField(expression) && ! isHolderVariable(expression) && !isStaticMethod()) {
    4099             cv.visitVarInsn(ALOAD, 0);
    4100         }
    4101         expression.visit(this);
    4102         cv.visitLdcInsn(method);
    4103         invokeNoArgumentsMethod.call(cv);
    4104
    4105         leftHandExpression = true;
    4106         expression.visit(this);
    4107         leftHandExpression = false;
    4108         expression.visit(this);
    4109     }
    4110
    4111     protected void evaluatePostfixMethod(String method, Expression expression) {
    4112         leftHandExpression = false;
    4113         expression.visit(this);
    4114
    4115         Variable tv = visitASTOREInTemp("postfix_" + method);
    4116         int tempIdx  = tv.getIndex();
    4117         cv.visitVarInsn(ALOAD, tempIdx);
    4118
    4119         cv.visitLdcInsn(method);
    4120         invokeNoArgumentsMethod.call(cv);
    4121
    4122         store(expression);
    4123
    4124         cv.visitVarInsn(ALOAD, tempIdx);
    4125         removeVar(tv);
    4126     }
    4127
    4128     protected boolean isHolderVariable(Expression expression) {
    4129         if (expression instanceof FieldExpression) {
    4130             FieldExpression fieldExp = (FieldExpression) expression;
    4131             return fieldExp.getField().isHolder();
    4132         }
    4133         if (expression instanceof VariableExpression) {
    4134             VariableExpression varExp = (VariableExpression) expression;
    4135             Variable variable = (Variable) variableStack.get(varExp.getVariable());
    4136             if (variable != null) {
    4137                 return variable.isHolder();
    4138             }
    4139             FieldNode field = classNode.getField(varExp.getVariable());
    4140             if (field != null) {
    4141                 return field.isHolder();
    4142             }
    4143         }
    4144         return false;
    4145     }
    4146
    4147     protected void evaluateInstanceof(BinaryExpression expression) {
    4148         expression.getLeftExpression().visit(this);
    4149         Expression rightExp = expression.getRightExpression();
    4150         String className = null;
    4151         if (rightExp instanceof ClassExpression) {
    4152             ClassExpression classExp = (ClassExpression) rightExp;
    4153             className = classExp.getType();
    4154         }
    4155         else {
    4156             throw new RuntimeException(
    4157                 "Right hand side of the instanceof keyworld must be a class name, not: " + rightExp);
    4158         }
    4159         className = checkValidType(className, expression, "Must be a valid type name for an instanceof statement");
    4160         String classInternalName = BytecodeHelper.getClassInternalName(className);
    4161         cv.visitTypeInsn(INSTANCEOF, classInternalName);
    4162     }
    4163
    4164     /***
    4165      * @return true if the given argument expression requires the stack, in
    4166      *         which case the arguments are evaluated first, stored in the
    4167      *         variable stack and then reloaded to make a method call
    4168      */
    4169     protected boolean argumentsUseStack(Expression arguments) {
    4170         return arguments instanceof TupleExpression || arguments instanceof ClosureExpression;
    4171     }
    4172
    4173     /***
    4174      * @return true if the given expression represents a non-static field
    4175      */
    4176     protected boolean isNonStaticField(Expression expression) {
    4177         FieldNode field = null;
    4178         if (expression instanceof VariableExpression) {
    4179             VariableExpression varExp = (VariableExpression) expression;
    4180             field = classNode.getField(varExp.getVariable());
    4181         }
    4182         else if (expression instanceof FieldExpression) {
    4183             FieldExpression fieldExp = (FieldExpression) expression;
    4184             field = classNode.getField(fieldExp.getFieldName());
    4185         }
    4186         else if (expression instanceof PropertyExpression) {
    4187             PropertyExpression fieldExp = (PropertyExpression) expression;
    4188             field = classNode.getField(fieldExp.getProperty());
    4189         }
    4190         if (field != null) {
    4191             return !field.isStatic();
    4192         }
    4193         return false;
    4194     }
    4195
    4196     protected boolean isThisExpression(Expression expression) {
    4197         if (expression instanceof VariableExpression) {
    4198             VariableExpression varExp = (VariableExpression) expression;
    4199             return varExp.getVariable().equals("this");
    4200         }
    4201         return false;
    4202     }
    4203
    4204     /***
    4205      * For assignment expressions, return a safe expression for the LHS we can use
    4206      * to return the value
    4207      */
    4208     protected Expression createReturnLHSExpression(Expression expression) {
    4209         if (expression instanceof BinaryExpression) {
    4210             BinaryExpression binExpr = (BinaryExpression) expression;
    4211             if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
    4212                 return createReusableExpression(binExpr.getLeftExpression());
    4213             }
    4214         }
    4215         return null;
    4216     }
    4217
    4218     protected Expression createReusableExpression(Expression expression) {
    4219         ExpressionTransformer transformer = new ExpressionTransformer() {
    4220             public Expression transform(Expression expression) {
    4221                 if (expression instanceof PostfixExpression) {
    4222                     PostfixExpression postfixExp = (PostfixExpression) expression;
    4223                     return postfixExp.getExpression();
    4224                 }
    4225                 else if (expression instanceof PrefixExpression) {
    4226                     PrefixExpression prefixExp = (PrefixExpression) expression;
    4227                     return prefixExp.getExpression();
    4228                 }
    4229                 return expression;
    4230             }
    4231         };
    4232
    4233         // could just be a postfix / prefix expression or nested inside some other expression
    4234         return transformer.transform(expression.transformExpression(transformer));
    4235     }
    4236
    4237     protected boolean isComparisonExpression(Expression expression) {
    4238         if (expression instanceof BinaryExpression) {
    4239             BinaryExpression binExpr = (BinaryExpression) expression;
    4240             switch (binExpr.getOperation().getType()) {
    4241                 case Types.COMPARE_EQUAL :
    4242                 case Types.MATCH_REGEX :
    4243                 case Types.COMPARE_GREATER_THAN :
    4244                 case Types.COMPARE_GREATER_THAN_EQUAL :
    4245                 case Types.COMPARE_LESS_THAN :
    4246                 case Types.COMPARE_LESS_THAN_EQUAL :
    4247                 case Types.COMPARE_IDENTICAL :
    4248                 case Types.COMPARE_NOT_EQUAL :
    4249                 case Types.KEYWORD_INSTANCEOF :
    4250                     return true;
    4251             }
    4252         }
    4253         else if (expression instanceof BooleanExpression) {
    4254             return true;
    4255         }
    4256         return false;
    4257     }
    4258
    4259     protected void onLineNumber(ASTNode statement, String message) {
    4260         int line = statement.getLineNumber();
    4261         int col = statement.getColumnNumber();
    4262         this.currentASTNode = statement;
    4263
    4264         if (line >=0) {
    4265             lineNumber = line;
    4266             columnNumber = col;
    4267         }
    4268         if (CREATE_DEBUG_INFO && line >= 0 && cv != null) {
    4269             Label l = new Label();
    4270             cv.visitLabel(l);
    4271             cv.visitLineNumber(line, l);
    4272             if (ASM_DEBUG) {
    4273                 helper.mark(message + "[" + statement.getLineNumber() + ":" + statement.getColumnNumber() + "]");
    4274             }
    4275         }
    4276     }
    4277
    4278     protected VariableScope getVariableScope() {
    4279         if (variableScope == null) {
    4280             if (methodNode != null) {
    4281                 // if we're a closure method we'll have our variable scope already created
    4282                 variableScope = methodNode.getVariableScope();
    4283                 if (variableScope == null) {
    4284                     variableScope = new VariableScope();
    4285                     methodNode.setVariableScope(variableScope);
    4286                     VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
    4287                     visitor.setParameters(methodNode.getParameters());
    4288                     Statement code = methodNode.getCode();
    4289                     if (code != null) {
    4290                         code.visit(visitor);
    4291                     }
    4292                 }
    4293                 addFieldsToVisitor(variableScope);
    4294             }
    4295             else if (constructorNode != null) {
    4296                 variableScope = new VariableScope();
    4297                 constructorNode.setVariableScope(variableScope);
    4298                 VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
    4299                 visitor.setParameters(constructorNode.getParameters());
    4300                 Statement code = constructorNode.getCode();
    4301                 if (code != null) {
    4302                     code.visit(visitor);
    4303                 }
    4304                 addFieldsToVisitor(variableScope);
    4305             }
    4306             else {
    4307                 throw new RuntimeException("Can't create a variable scope outside of a method or constructor");
    4308             }
    4309         }
    4310         return variableScope;
    4311     }
    4312
    4313     /***
    4314      * @return a list of parameters for each local variable which needs to be
    4315      *         passed into a closure
    4316      */
    4317     protected Parameter[] getClosureSharedVariables(ClosureExpression expression) {
    4318         List vars = new ArrayList();
    4319
    4320         //
    4321         // First up, get the scopes for outside and inside the closure.
    4322         // The inner scope must cover all nested closures, as well, as
    4323         // everything that will be needed must be imported.
    4324
    4325         VariableScope outerScope = getVariableScope().createRecursiveParentScope();
    4326         VariableScope innerScope = expression.getVariableScope();
    4327         if (innerScope == null) {
    4328             System.out.println(
    4329                 "No variable scope for: " + expression + " method: " + methodNode + " constructor: " + constructorNode);
    4330             innerScope = new VariableScope(getVariableScope());
    4331         }
    4332         else {
    4333             innerScope = innerScope.createRecursiveChildScope();
    4334         }
    4335
    4336
    4337         //
    4338         // DeclaredVariables include any name that was assigned to within
    4339         // the scope.  ReferencedVariables include any name that was read
    4340         // from within the scope.  We get the sets from each and must piece
    4341         // together the stack variable import list for the closure.  Note
    4342         // that we don't worry about field variables here, as we don't have
    4343         // to do anything special with them.  Stack variables, on the other
    4344         // hand, have to be wrapped up in References for use.
    4345
    4346         Set outerDecls = outerScope.getDeclaredVariables();
    4347         Set outerRefs  = outerScope.getReferencedVariables();
    4348         Set innerDecls = innerScope.getDeclaredVariables();
    4349         Set innerRefs  = innerScope.getReferencedVariables();
    4350
    4351
    4352         //
    4353         // So, we care about any name referenced in the closure UNLESS:
    4354         //   1) it's not declared in the outer context;
    4355         //   2) it's a parameter;
    4356         //   3) it's a field in the context class that isn't overridden
    4357         //      by a stack variable in the outer context.
    4358         //
    4359         // BUG: We don't actually have the necessary information to do
    4360         //      this right!  The outer declarations don't distinguish
    4361         //      between assignments and variable declarations.  Therefore
    4362         //      we can't tell when field variables have been overridden
    4363         //      by stack variables in the outer context.  This must
    4364         //      be fixed!
    4365
    4366         Set varSet = new HashSet();
    4367         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
    4368             String var = (String) iter.next();
    4369             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
    4370             if (outerDecls.contains(var) && (isNotFieldOfOutermostClass(var))) {
    4371                 String type = getVariableType(var);
    4372                 vars.add(new Parameter(type, var));
    4373                 varSet.add(var);
    4374             }
    4375         }
    4376         for (Iterator iter = outerRefs.iterator(); iter.hasNext();) {
    4377             String var = (String) iter.next();
    4378             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
    4379             if (innerDecls.contains(var) && (isNotFieldOfOutermostClass(var)) && !varSet.contains(var)) {
    4380                 String type = getVariableType(var);
    4381                 vars.add(new Parameter(type, var));
    4382             }
    4383         }
    4384
    4385
    4386         Parameter[] answer = new Parameter[vars.size()];
    4387         vars.toArray(answer);
    4388         return answer;
    4389     }
    4390
    4391     protected boolean isNotFieldOfOutermostClass(String var) {
    4392         //return classNode.getField(var) == null || isInnerClass();
    4393         return getOutermostClass().getField(var) == null;
    4394     }
    4395
    4396     protected void findMutableVariables() {
    4397         /*
    4398         VariableScopeCodeVisitor outerVisitor = new VariableScopeCodeVisitor(true);
    4399         node.getCode().visit(outerVisitor);
    4400
    4401         addFieldsToVisitor(outerVisitor);
    4402
    4403         VariableScopeCodeVisitor innerVisitor = outerVisitor.getClosureVisitor();
    4404         */
    4405         VariableScope outerScope = getVariableScope();
    4406
    4407         // lets create a scope concatenating all the closure expressions
    4408         VariableScope innerScope = outerScope.createCompositeChildScope();
    4409
    4410         Set outerDecls = outerScope.getDeclaredVariables();
    4411         Set outerRefs = outerScope.getReferencedVariables();
    4412         Set innerDecls = innerScope.getDeclaredVariables();
    4413         Set innerRefs = innerScope.getReferencedVariables();
    4414
    4415         mutableVars.clear();
    4416
    4417         for (Iterator iter = innerDecls.iterator(); iter.hasNext();) {
    4418             String var = (String) iter.next();
    4419             if ((outerDecls.contains(var) || outerRefs.contains(var)) && classNode.getField(var) == null) {
    4420                 mutableVars.add(var);
    4421             }
    4422         }
    4423
    4424         // we may call the closure twice and modify the variable in the outer scope
    4425         // so for now lets assume that all variables are mutable
    4426         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
    4427             String var = (String) iter.next();
    4428             if (outerDecls.contains(var) && classNode.getField(var) == null) {
    4429                 mutableVars.add(var);
    4430             }
    4431         }
    4432
    4433         //                System.out.println();
    4434         //                System.out.println("method: " + methodNode + " classNode: " + classNode);
    4435         //                System.out.println("child scopes: " + outerScope.getChildren());
    4436         //                System.out.println("outerDecls: " + outerDecls);
    4437         //                System.out.println("outerRefs: " + outerRefs);
    4438         //                System.out.println("innerDecls: " + innerDecls);
    4439         //                System.out.println("innerRefs: " + innerRefs);
    4440     }
    4441
    4442     protected void addFieldsToVisitor(VariableScope scope) {
    4443         for (Iterator iter = classNode.getFields().iterator(); iter.hasNext();) {
    4444             FieldNode field = (FieldNode) iter.next();
    4445             String name = field.getName();
    4446
    4447             scope.getDeclaredVariables().add(name);
    4448             scope.getReferencedVariables().add(name);
    4449         }
    4450     }
    4451
    4452     private boolean isInnerClass() {
    4453         return classNode instanceof InnerClassNode;
    4454     }
    4455
    4456     protected String getVariableType(String name) {
    4457         Variable variable = (Variable) variableStack.get(name);
    4458         if (variable != null) {
    4459             return variable.getTypeName();
    4460         }
    4461         return null;
    4462     }
    4463
    4464     protected void resetVariableStack(Parameter[] parameters) {
    4465         lastVariableIndex = -1;
    4466         variableStack.clear();
    4467
    4468         scope = new BlockScope(null);
    4469         //pushBlockScope();
    4470
    4471         // lets push this onto the stack
    4472         definingParameters = true;
    4473         if (!isStaticMethod()) {
    4474             defineVariable("this", classNode.getName()).getIndex();
    4475         } // now lets create indices for the parameteres
    4476         for (int i = 0; i < parameters.length; i++) {
    4477             Parameter parameter = parameters[i];
    4478             String type = parameter.getType();
    4479             Variable v = defineVariable(parameter.getName(), type);
    4480             int idx = v.getIndex();
    4481             if (BytecodeHelper.isPrimitiveType(type)) {
    4482                 helper.load(type, idx);
    4483                 helper.box(type);
    4484                 cv.visitVarInsn(ASTORE, idx);
    4485             }
    4486         }
    4487         definingParameters = false;
    4488     }
    4489
    4490     protected void popScope() {
    4491         int lastID = scope.getFirstVariableIndex();
    4492
    4493         List removeKeys = new ArrayList();
    4494         for (Iterator iter = variableStack.entrySet().iterator(); iter.hasNext();) {
    4495             Map.Entry entry = (Map.Entry) iter.next();
    4496             String name = (String) entry.getKey();
    4497             Variable value = (Variable) entry.getValue();
    4498             if (value.getIndex() >= lastID) {
    4499                 removeKeys.add(name);
    4500             }
    4501         }
    4502         for (Iterator iter = removeKeys.iterator(); iter.hasNext();) {
    4503             Variable v  = (Variable) variableStack.remove(iter.next());
    4504             if (CREATE_DEBUG_INFO) { // set localvartable
    4505                 if (v != null) {
    4506                     visitVariableEndLabel(v);
    4507                     cv.visitLocalVariable(
    4508                             v.getName(),
    4509                             BytecodeHelper.getTypeDescription(v.getTypeName()),
    4510                             v.getStartLabel(),
    4511                             v.getEndLabel(),
    4512                             v.getIndex()
    4513                     );
    4514                 }
    4515             }
    4516         }
    4517         scope = scope.getParent();
    4518     }
    4519
    4520     void removeVar(Variable v ) {
    4521     	variableStack.remove(v.getName());
    4522         if (CREATE_DEBUG_INFO) { // set localvartable
    4523         	Label endl = new Label();
    4524         	cv.visitLabel(endl);
    4525         	cv.visitLocalVariable(
    4526         			v.getName(),
    4527 					BytecodeHelper.getTypeDescription(v.getTypeName()),
    4528 					v.getStartLabel(),
    4529 					endl,
    4530 					v.getIndex()
    4531         	);
    4532         }
    4533     }
    4534     private void visitVariableEndLabel(Variable v) {
    4535         if (CREATE_DEBUG_INFO) {
    4536             if(v.getEndLabel() == null) {
    4537                 Label end = new Label();
    4538                 v.setEndLabel(end);
    4539             }
    4540             cv.visitLabel(v.getEndLabel());
    4541         }
    4542     }
    4543
    4544     protected void pushBlockScope() {
    4545         pushBlockScope(true, true);
    4546     }
    4547
    4548     /***
    4549      * create a new scope. Set break/continue label if the canXXX parameter is true. Otherwise
    4550      * inherit parent's label.
    4551      * @param canContinue   true if the start of the scope can take continue label
    4552      * @param canBreak  true if the end of the scope can take break label
    4553      */
    4554     protected void pushBlockScope(boolean canContinue, boolean canBreak) {
    4555         BlockScope parentScope = scope;
    4556         scope = new BlockScope(parentScope);
    4557         scope.setContinueLabel(canContinue ? new Label() : (parentScope == null ? null : parentScope.getContinueLabel()));
    4558         scope.setBreakLabel(canBreak? new Label() : (parentScope == null ? null : parentScope.getBreakLabel()));
    4559         scope.setFirstVariableIndex(getNextVariableID());
    4560     }
    4561
    4562     /***
    4563      * Defines the given variable in scope and assigns it to the stack
    4564      */
    4565     protected Variable defineVariable(String name, String type) {
    4566         return defineVariable(name, type, true);
    4567     }
    4568
    4569     protected Variable defineVariable(String name, String type, boolean define) {
    4570         return defineVariable(name, new Type(type), define);
    4571     }
    4572
    4573     private Variable defineVariable(String name, Type type, boolean define) {
    4574         Variable answer = (Variable) variableStack.get(name);
    4575         if (answer == null) {
    4576             lastVariableIndex = getNextVariableID();
    4577             answer = new Variable(lastVariableIndex, type, name);
    4578             if (mutableVars.contains(name)) {
    4579                 answer.setHolder(true);
    4580             }
    4581             variableStack.put(name, answer);
    4582             Label startLabel  = new Label();
    4583             answer.setStartLabel(startLabel);
    4584             if (define) {
    4585                 if (definingParameters) {
    4586                     if (answer.isHolder()) {
    4587                         cv.visitTypeInsn(NEW, "groovy/lang/Reference"); // br todo to associate a label with the variable
    4588                         cv.visitInsn(DUP);
    4589                         cv.visitVarInsn(ALOAD, lastVariableIndex);
    4590                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V");
    4591                         cv.visitVarInsn(ASTORE, lastVariableIndex);
    4592                         cv.visitLabel(startLabel);
    4593                     }
    4594                 }
    4595                 else {
    4596                     // using new variable inside a comparison expression
    4597                     // so lets initialize it too
    4598                     if (answer.isHolder() && !isInScriptBody()) {
    4599                         //cv.visitVarInsn(ASTORE, lastVariableIndex + 1); // I might need this to set the reference value
    4600
    4601                         cv.visitTypeInsn(NEW, "groovy/lang/Reference");
    4602                         cv.visitInsn(DUP);
    4603                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "()V");
    4604
    4605                         cv.visitVarInsn(ASTORE, lastVariableIndex);
    4606                         cv.visitLabel(startLabel);
    4607                         //cv.visitVarInsn(ALOAD, idx + 1);
    4608                     }
    4609                     else {
    4610                         if (!leftHandExpression) { // new var on the RHS: init with null
    4611                             cv.visitInsn(ACONST_NULL);
    4612                             cv.visitVarInsn(ASTORE, lastVariableIndex);
    4613                             cv.visitLabel(startLabel);
    4614                         }
    4615                     }
    4616                 }
    4617             }
    4618         }
    4619         return answer;
    4620     }
    4621
    4622     private int getNextVariableID() {
    4623         //return Math.max(lastVariableIndex + 1, variableStack.size());
    4624         return variableStack.size(); // todo : rework
    4625     }
    4626
    4627     /*** @return true if the given name is a local variable or a field */
    4628     protected boolean isFieldOrVariable(String name) {
    4629         return variableStack.containsKey(name) || classNode.getField(name) != null;
    4630     }
    4631
    4632     protected Type checkValidType(Type type, ASTNode node, String message) {
    4633         if (type.isDynamic()) {
    4634             return type;
    4635         }
    4636         String name = checkValidType(type.getName(), node, message);
    4637         if (type.getName().equals(name)) {
    4638             return type;
    4639         }
    4640         return new Type(name);
    4641     }
    4642
    4643     protected String checkValidType(String type, ASTNode node, String message) {
    4644         if (type!= null && type.length() == 0)
    4645             return "java.lang.Object";
    4646         if (type.endsWith("[]")) {
    4647             String postfix = "[]";
    4648             String prefix = type.substring(0, type.length() - 2);
    4649             return checkValidType(prefix, node, message) + postfix;
    4650         }
    4651         int idx = type.indexOf('$');
    4652         if (idx > 0) {
    4653             String postfix = type.substring(idx);
    4654             String prefix = type.substring(0, idx);
    4655             return checkValidType(prefix, node, message) + postfix;
    4656         }
    4657         if (BytecodeHelper.isPrimitiveType(type) || "void".equals(type)) {
    4658             return type;
    4659         }
    4660         String original = type;
    4661         type = resolveClassName(type);
    4662         if (type != null) {
    4663             return type;
    4664         }
    4665
    4666         throw new MissingClassException(original, node, message + " for class: " + classNode.getName());
    4667     }
    4668
    4669     protected String resolveClassName(String type) {
    4670         return classNode.resolveClassName(type);
    4671     }
    4672
    4673     protected String createVariableName(String type) {
    4674         return "__" + type + (++tempVariableNameCounter);
    4675     }
    4676
    4677     /***
    4678      * @return if the type of the expression can be determined at compile time
    4679      *         then this method returns the type - otherwise null
    4680      */
    4681     protected String getExpressionType(Expression expression) {
    4682         if (isComparisonExpression(expression)) {
    4683             return "boolean";
    4684         }
    4685         if (expression instanceof VariableExpression) {
    4686             VariableExpression varExpr = (VariableExpression) expression;
    4687             Variable variable = (Variable) variableStack.get(varExpr.getVariable());
    4688             if (variable != null && !variable.isHolder()) {
    4689                 Type type = variable.getType();
    4690                 if (! type.isDynamic()) {
    4691                     return type.getName();
    4692                 }
    4693             }
    4694         }
    4695         return null;
    4696     }
    4697
    4698     /***
    4699      * @return true if the value is an Integer, a Float, a Long, a Double or a
    4700      *         String .
    4701      */
    4702     protected static boolean isPrimitiveFieldType(String type) {
    4703         return type.equals("java.lang.String")
    4704             || type.equals("java.lang.Integer")
    4705             || type.equals("java.lang.Double")
    4706             || type.equals("java.lang.Long")
    4707             || type.equals("java.lang.Float");
    4708     }
    4709
    4710     protected boolean isInClosureConstructor() {
    4711         return constructorNode != null
    4712             && classNode.getOuterClass() != null
    4713             && classNode.getSuperClass().equals(Closure.class.getName());
    4714     }
    4715
    4716     protected boolean isStaticMethod() {
    4717         if (methodNode == null) { // we're in a constructor
    4718             return false;
    4719         }
    4720         return methodNode.isStatic();
    4721     }
    4722
    4723     Map classCache = new HashMap();
    4724     {
    4725         classCache.put("int", Integer.TYPE);
    4726         classCache.put("byte", Byte.TYPE);
    4727         classCache.put("short", Short.TYPE);
    4728         classCache.put("char", Character.TYPE);
    4729         classCache.put("boolean", Boolean.TYPE);
    4730         classCache.put("long", Long.TYPE);
    4731         classCache.put("double", Double.TYPE);
    4732         classCache.put("float", Float.TYPE);
    4733         classCache.put("void", Void.TYPE);
    4734     }
    4735     /***
    4736      * @return loads the given type name
    4737      */
    4738     protected Class loadClass(String name) {
    4739
    4740         if (name.equals(this.classNode.getName())) {
    4741             return Object.class;
    4742         }
    4743
    4744         if (name == null) {
    4745             return null;
    4746         }
    4747         else if (name.length() == 0) {
    4748             return Object.class;
    4749         }
    4750
    4751         name = BytecodeHelper.formatNameForClassLoading(name);
    4752
    4753     	try {
    4754     		Class cls = (Class)classCache.get(name);
    4755     		if (cls != null)
    4756     			return cls;
    4757
    4758     		CompileUnit compileUnit = getCompileUnit();
    4759             if (compileUnit != null) {
    4760             	cls = compileUnit.loadClass(name);
    4761                 classCache.put(name, cls);
    4762             	return cls;
    4763             }
    4764             else {
    4765                 throw new ClassGeneratorException("Could not load class: " + name);
    4766             }
    4767         }
    4768         catch (ClassNotFoundException e) {
    4769             throw new ClassGeneratorException("Error when compiling class: " + classNode.getName() + ". Reason: could not load class: " + name + " reason: " + e, e);
    4770         }
    4771     }
    4772
    4773     protected CompileUnit getCompileUnit() {
    4774         CompileUnit answer = classNode.getCompileUnit();
    4775         if (answer == null) {
    4776             answer = context.getCompileUnit();
    4777         }
    4778         return answer;
    4779     }
    4780
    4781     /***
    4782      * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding.
    4783      * @param call
    4784      */
    4785     public void resolve(MethodCallExpression call) {
    4786         if (call.isResolveFailed()) {
    4787             return;
    4788         }
    4789         else if (call.isTypeResolved()) {
    4790             return;
    4791         }
    4792
    4793         Expression obj = call.getObjectExpression();
    4794         String meth = call.getMethod();
    4795         Class ownerClass = null;
    4796         boolean isStaticCall = false;
    4797         boolean isSuperCall = false;
    4798
    4799         List arglist = new ArrayList();
    4800         Expression args = call.getArguments();
    4801         if (args instanceof TupleExpression) {
    4802             TupleExpression tupleExpression = (TupleExpression) args;
    4803             List argexps = tupleExpression.getExpressions();
    4804             for (int i = 0; i < argexps.size(); i++) {
    4805                 Expression expression = (Expression) argexps.get(i);
    4806                 Class cls = expression.getTypeClass();
    4807                 if (cls == null) {
    4808                     call.setResolveFailed(true);
    4809                     return ;
    4810                 }
    4811                 else {
    4812                     arglist.add(cls);
    4813                 }
    4814             }
    4815         } else if (args instanceof ClosureExpression) {
    4816             call.setResolveFailed(true);
    4817             return ;// todo
    4818         } else {
    4819             call.setResolveFailed(true);
    4820             return ;
    4821         }
    4822
    4823
    4824         Class[] argsArray = new Class[arglist.size()];
    4825         arglist.toArray(argsArray);
    4826
    4827
    4828         if (obj instanceof ClassExpression) {
    4829             // static call
    4830             //ClassExpression cls = (ClassExpression)obj;
    4831             //String type = cls.getType();
    4832             ownerClass = obj.getTypeClass(); //loadClass(type);
    4833             isStaticCall = true;
    4834         } else if (obj instanceof VariableExpression) {
    4835             VariableExpression var = (VariableExpression) obj;
    4836             if (var.getVariable().equals("this") && (methodNode == null? true : !methodNode.isStatic()) ) {
    4837                 isStaticCall = false;
    4838                 if (methodNode != null) {
    4839                     isStaticCall = Modifier.isStatic(methodNode.getModifiers());
    4840                 }
    4841                 MetaMethod mmeth = getMethodOfThisAndSuper(meth, argsArray, isStaticCall);
    4842                 if (mmeth != null) {
    4843                     call.setMethod(mmeth);
    4844                     return ;
    4845                 }
    4846                 else {
    4847                     call.setResolveFailed(true);
    4848                     return ;
    4849                 }
    4850             }
    4851             else if (var.getVariable().equals("super") ) {
    4852                 isSuperCall = true;
    4853                 ownerClass = var.getTypeClass();
    4854             }
    4855             else {
    4856                 ownerClass = var.getTypeClass();
    4857             }
    4858         }
    4859         else /*if (obj instanceof PropertyExpression)*/ {
    4860             ownerClass = obj.getTypeClass();
    4861             if (ownerClass == null) {
    4862                 call.setResolveFailed(true);
    4863                 call.setFailure("target class is null");
    4864                 return ;  // take care other cases later version
    4865             }
    4866         }
    4867
    4868         if (ownerClass == Object.class) {
    4869             call.setResolveFailed(true);
    4870             return ; // use late binding for dynamic types
    4871         }
    4872         else if (ownerClass == null)  {
    4873             call.setResolveFailed(true);
    4874             return ; // use dynamic dispatching for GroovyObject
    4875         }
    4876         else
    4877             if (!isSuperCall && !isStaticCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // not optimize GroovyObject meth call for now
    4878             call.setResolveFailed(true);
    4879             return ;
    4880         }
    4881         else
    4882             if (ownerClass.isPrimitive()) {
    4883             call.setResolveFailed(true);
    4884             return ; // todo handle primitives
    4885         }
    4886
    4887
    4888         //MetaMethod mmethod = InvokerHelper.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
    4889         // todo is this thread safe?
    4890         MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
    4891         if (mmethod!= null) {
    4892             call.setMethod(mmethod);
    4893         }
    4894         else {
    4895             call.setResolveFailed(true);
    4896         }
    4897         return ;
    4898     }
    4899     /***
    4900      * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding.
    4901      * @param call
    4902      */
    4903     public void resolve(ConstructorCallExpression call) {
    4904         if (call.isResolveFailed()) {
    4905             return ;
    4906         }
    4907         else if (call.isTypeResolved()) {
    4908             return ;
    4909         }
    4910
    4911         String declaredType = call.getTypeToSet();
    4912         if (declaredType.equals(classNode.getName())) {
    4913             call.setResolveFailed(true);
    4914             call.setFailure("cannot resolve on the current class itself. ");
    4915             return;
    4916         }
    4917         else {
    4918             call.setType(declaredType);
    4919             if (call.getTypeClass() == null) {
    4920                 call.setResolveFailed(true);
    4921                 call.setFailure("type name cannot be resolved. ");
    4922                 return;
    4923             }
    4924         }
    4925
    4926         boolean isSuperCall = false;
    4927
    4928         List arglist = new ArrayList();
    4929         Expression args = call.getArguments();
    4930         if (args instanceof TupleExpression) {
    4931             TupleExpression tupleExpression = (TupleExpression) args;
    4932             List argexps = tupleExpression.getExpressions();
    4933             for (int i = 0; i < argexps.size(); i++) {
    4934                 Expression expression = (Expression) argexps.get(i);
    4935                 Class cls = expression.getTypeClass();
    4936                 if (cls == null) {
    4937                     call.setResolveFailed(true);
    4938                     return ;
    4939                 }
    4940                 else {
    4941                     arglist.add(cls);
    4942                 }
    4943             }
    4944         } else if (args instanceof ClosureExpression) {
    4945             call.setResolveFailed(true);
    4946             call.setFailure("don't know how to handle closure arg. ");
    4947             return ;// todo
    4948         } else {
    4949             call.setResolveFailed(true);
    4950             call.setFailure("unknown arg type: " + args.getClass().getName());
    4951             return ;
    4952         }
    4953
    4954
    4955         Class[] argsArray = new Class[arglist.size()];
    4956         arglist.toArray(argsArray);
    4957
    4958         Class ownerClass = call.getTypeClass();
    4959         if (ownerClass == null) {
    4960             String typeName = call.getType();
    4961             if (typeName.equals(this.classNode.getName())) {
    4962                 // this is a ctor call to this class
    4963                 call.setResolveFailed(true);
    4964                 call.setFailure("invoke constructor for this. no optimization for now");
    4965                 return ;
    4966             }
    4967             else {
    4968                 try {
    4969                     ownerClass = loadClass(typeName);
    4970                     if (ownerClass == null) {
    4971                         call.setResolveFailed(true);
    4972                         call.setFailure("owner class type is null. ");
    4973                         return ;
    4974                     }
    4975                 }
    4976                 catch (Throwable th) {
    4977                     call.setResolveFailed(true);
    4978                     call.setFailure("Exception: " + th);
    4979                     return ;
    4980                 }
    4981             }
    4982         }
    4983
    4984         if (ownerClass == Object.class) {
    4985             call.setResolveFailed(true);
    4986             call.setFailure("owner class type java.lang.Object.  use late binding for dynamic types");
    4987             return ; //
    4988         }
    4989         else if (ownerClass == null)  {
    4990             call.setResolveFailed(true);
    4991             call.setFailure("owner class type is null. use dynamic dispatching for GroovyObject");
    4992             return; // use dynamic dispatching for GroovyObject
    4993         }
    4994 // safe to call groovyobject ctor
    4995 //        else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call
    4996 //            call.setResolveFailed(true);
    4997 //            return null;
    4998 //        }
    4999         else if (ownerClass.isPrimitive()) {
    5000             call.setResolveFailed(true);
    5001             throwException("The owner of the constructor is primitive.");
    5002             return ;
    5003         }
    5004
    5005         Constructor ctor = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedConstructor(ownerClass, argsArray);
    5006         if (ctor!= null) {
    5007             call.setConstructor(ctor);
    5008         }
    5009         else {
    5010             call.setResolveFailed(true);
    5011         }
    5012         return ;
    5013     }
    5014
    5015     public void resolve(PropertyExpression propertyExpression)  {
    5016         if (propertyExpression.getTypeClass() != null)
    5017             return ;
    5018         if (propertyExpression.isResolveFailed())
    5019             return ;
    5020
    5021         Expression ownerExp = propertyExpression.getObjectExpression();
    5022         Class ownerClass = ownerExp.getTypeClass();
    5023         String propName = propertyExpression.getProperty();
    5024         if (propName.equals("class")) {
    5025             propertyExpression.setTypeClass(Class.class);
    5026             return ;
    5027         }
    5028
    5029         // handle arraylength
    5030         if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) {
    5031             propertyExpression.setTypeClass(int.class);
    5032             return;
    5033         }
    5034
    5035         if (isThisExpression(ownerExp)) {
    5036             // lets use the field expression if its available
    5037             if (classNode == null) {
    5038                 propertyExpression.setResolveFailed(true);
    5039                 return ;
    5040             }
    5041             FieldNode field   = null;
    5042             ownerExp.setType(classNode.getName());
    5043             try {
    5044                 if( (field = classNode.getField(propName)) != null ) {
    5045 //                    Class cls =loadClass(field.getType());
    5046 //                    propertyExpression.setAccess(PropertyExpression.LOCAL_FIELD_ACCESS);
    5047 //                    propertyExpression.setTypeClass(cls);
    5048 // local property access. to be determined in the future
    5049                     propertyExpression.setResolveFailed(true);
    5050                     propertyExpression.setFailure("local property access. to be determined in the future.");
    5051                     return ;
    5052                 } else {
    5053                     // search for super classes/interface
    5054                     // interface first first
    5055                     String[] interfaces = classNode.getInterfaces();
    5056                     String[] supers = new String[interfaces.length + 1];
    5057
    5058                     int i = 0;
    5059                     for (; i < interfaces.length; i++) {
    5060                         supers[i] = interfaces[i];
    5061                     }
    5062                     supers[i] = classNode.getSuperClass();
    5063                     for (int j = 0; j < supers.length; j++) {
    5064                         String aSuper = supers[j];
    5065                         Class superClass = loadClass(aSuper);
    5066                         Field fld  = superClass.getDeclaredField(propName);
    5067                         if (fld != null && !Modifier.isPrivate(fld.getModifiers())) {
    5068                             propertyExpression.setField(fld);
    5069                             return ;
    5070                         }
    5071                     }
    5072                 }
    5073             } catch (Exception e) {
    5074                 propertyExpression.setResolveFailed(true);
    5075                 propertyExpression.setFailure(e.getMessage());
    5076                 return ;
    5077             }
    5078         }
    5079         else if (ownerExp instanceof ClassExpression) {
    5080             if (ownerClass != null) {
    5081                 Field fld  = null;
    5082                 try {
    5083                     fld = ownerClass.getDeclaredField(propName);
    5084                     if (!Modifier.isPrivate(fld.getModifiers())) {
    5085                         propertyExpression.setField(fld);
    5086                         return ;
    5087                     }
    5088                 } catch (NoSuchFieldException e) {
    5089                     propertyExpression.setResolveFailed(true);
    5090                     return ;
    5091                 }
    5092             }
    5093         }
    5094         else { // search public field and then setter/getter
    5095             if (ownerClass != null) {
    5096                 propertyExpression.setResolveFailed(true); // will get reset if property/getter/setter were found
    5097                 Field fld  = null;
    5098                 try {
    5099                     fld = ownerClass.getDeclaredField(propName);
    5100                 } catch (NoSuchFieldException e) {}
    5101
    5102                 if (fld != null && Modifier.isPublic(fld.getModifiers())) {
    5103                     propertyExpression.setField(fld);
    5104                 }
    5105
    5106                 // let's get getter and setter
    5107                 String getterName = "get" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
    5108                 String setterName = "set" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
    5109
    5110                 Method[] meths = ownerClass.getMethods();
    5111                 for (int i = 0; i < meths.length; i++) {
    5112                     Method method = meths[i];
    5113                     String methName =method.getName();
    5114                     Class[] paramClasses = method.getParameterTypes();
    5115                     if (methName.equals(getterName) && paramClasses.length == 0) {
    5116                         propertyExpression.setGetter(method);
    5117                     } else if (methName.equals(setterName) && paramClasses.length == 1) {
    5118                         propertyExpression.setSetter(method);
    5119                     }
    5120                 }
    5121                 return ;
    5122             }
    5123         }
    5124         propertyExpression.setResolveFailed(true);
    5125         return ;
    5126     }
    5127     /*** search in the current classNode and super class for matching method */
    5128     private MetaMethod getMethodOfThisAndSuper(String methName, Class[] argsArray, boolean isStaticCall) {
    5129         MethodNode candidate = null;
    5130         List meths = classNode.getMethods();
    5131         Class[] candidateParamClasses = null;
    5132         for (int i = 0; i < meths.size(); i++) {
    5133             MethodNode meth = (MethodNode) meths.get(i);
    5134             if (meth.getName().equals(methName)) {
    5135                 Parameter[] params = meth.getParameters();
    5136                 if  (params.length == argsArray.length) {
    5137                     Class[] paramClasses = new Class[params.length];
    5138                     for (int j = 0; j < params.length; j++) {
    5139                         Parameter param = params[j];
    5140                         String type = param.getType();
    5141                         Class paramClass = null;
    5142                         try {
    5143                             paramClass = loadClass(type);
    5144                         } catch (Exception e) {
    5145                             log.warning(e.getMessage());
    5146                             return null;
    5147                         }
    5148                         paramClasses[j] = paramClass;
    5149                     }
    5150                     if (MetaClass.isValidMethod(paramClasses, argsArray, false)) {
    5151                         candidateParamClasses = paramClasses;
    5152                         candidate = meth;
    5153                         break;
    5154                     }
    5155                     else {
    5156                         if (MetaClass.isValidMethod(paramClasses, argsArray, true)){
    5157                             candidateParamClasses = paramClasses;
    5158                             candidate = meth;
    5159                             break;
    5160                         }
    5161                     }
    5162                 }
    5163             }
    5164         }
    5165
    5166         if (candidate != null && candidateParamClasses != null) {
    5167             // let's synth a MetaMethod from the MethodNode
    5168             try {
    5169                 return new MetaMethod(methName, null, candidateParamClasses, loadClass(candidate.getReturnType()), candidate.getModifiers());
    5170             } catch (Exception e) {
    5171                 log.warning(e.getMessage());
    5172                 return null;
    5173             }
    5174         }
    5175         else {
    5176             // try super class
    5177             Class superClass = null;
    5178             try {
    5179                 superClass = loadClass(classNode.getSuperClass());
    5180             }
    5181             catch(Exception e) {
    5182                 // the super may be a groovy class that's not compiled yet
    5183                 log.warning(e.getMessage());
    5184             }
    5185             // should I filter out GroovyObject super class here?
    5186             if (superClass != null ) {
    5187                 MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(superClass, methName, argsArray, isStaticCall);
    5188                 if (mmethod == null)
    5189                     return null;
    5190                 int modies = mmethod.getModifiers();
    5191                 if (Modifier.isPrivate(modies)) {
    5192                     return null;
    5193                 }
    5194                 else if(modies == 0) {
    5195                     // match package
    5196                     int pThis = classNode.getName().lastIndexOf(".");
    5197                     String packageNameThis = pThis > 0? classNode.getName().substring(0, pThis) : "";
    5198
    5199                     int pSuper = classNode.getSuperClass().lastIndexOf(".");
    5200                     String packageNameSuper = pSuper > 0? classNode.getSuperClass().substring(0, pSuper) : "";
    5201                     if (packageNameThis.equals(packageNameSuper)) {
    5202                         return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers());
    5203                     }
    5204                     else {
    5205                         return null;
    5206                     }
    5207                 }
    5208                 else {
    5209                     // let changes the declaring class back to null (meaning "this"), so that proper class inheritance permission control is obeyed
    5210                     return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers());
    5211                 }
    5212             }
    5213             return null;
    5214         }
    5215     }
    5216
    5217
    5218     /***
    5219      *  to find out the real type of a Variable Object
    5220      */
    5221     public void resolve(VariableExpression expression) {
    5222
    5223         String variableName = expression.getVariable();
    5224 // todo process arrays!
    5225       //-----------------------------------------------------------------------
    5226       // SPECIAL CASES
    5227
    5228         //
    5229         // "this" for static methods is the Class instance
    5230
    5231         if (isStaticMethod() && variableName.equals("this")) {
    5232             expression.setTypeClass(Class.class);
    5233             return;
    5234         } else if (variableName.equals("super")) {
    5235             if (isStaticMethod() ) {
    5236                 expression.setTypeClass(Class.class);
    5237                 return;
    5238             }
    5239             else {
    5240                 try {
    5241                     Class cls = loadClass(classNode.getSuperClass());
    5242                     expression.setTypeClass(cls);
    5243                     return ;
    5244                 }
    5245                 catch (Exception e) {
    5246                     expression.setResolveFailed(true);
    5247                     expression.setFailure(e.getMessage());
    5248                     return ;
    5249                 }
    5250             }
    5251         } else if (variableName.equals("this")){
    5252             return ;
    5253         } else {
    5254 //            String className = resolveClassName(variableName);
    5255 //            if (className != null) {
    5256 //                return loadClass(className);
    5257 //            }
    5258         }
    5259
    5260
    5261       //-----------------------------------------------------------------------
    5262       // GENERAL VARIABLE LOOKUP
    5263
    5264         //
    5265         // We are handling only unqualified variables here.  Therefore,
    5266         // we do not care about accessors, because local access doesn't
    5267         // go through them.  Therefore, precedence is as follows:
    5268         //   1) local variables, nearest block first
    5269         //   2) class fields
    5270         //   3) repeat search from 2) in next outer class
    5271
    5272         boolean  handled  = false;
    5273         Variable variable = (Variable)variableStack.get( variableName );
    5274
    5275         try {
    5276             if( variable != null ) {
    5277                 Type t = variable.getType();
    5278                 if (t.getRealName().length() == 0) {
    5279                     String tname = t.getName();
    5280                     if (tname.endsWith("[]")) {
    5281                         expression.setResolveFailed(true);
    5282                         expression.setFailure("array type to be supported later");
    5283                         return ;  // todo hanlde array
    5284                     }
    5285                     else if (tname.equals(classNode.getName())){
    5286                         expression.setResolveFailed(true);
    5287                         return ;
    5288                     }
    5289                     else if (classNode.getOuterClass() != null && tname.equals(classNode.getOuterClass().getName())){
    5290                         expression.setResolveFailed(true);
    5291                         return ;
    5292                     }
    5293                     Class cls = loadClass(tname);
    5294                     expression.setTypeClass(cls);
    5295                     expression.setDynamic(t.isDynamic());
    5296                     return ;
    5297                 } else {
    5298                     String tname = t.getRealName();
    5299                     if (tname.endsWith("[]")) {
    5300                         expression.setResolveFailed(true);
    5301                         expression.setFailure("array type to be supported later");
    5302                         return ;  // todo hanlde array
    5303                     }
    5304                     Class cls = loadClass(tname);
    5305                     expression.setTypeClass(cls);
    5306                     expression.setDynamic(t.isDynamic());
    5307                     return ;
    5308                 }
    5309
    5310 //            if( variable.isProperty() ) {
    5311 //                processPropertyVariable(variable );
    5312 //            }
    5313 //            else {
    5314 //                processStackVariable(variable );
    5315 //            }
    5316 //
    5317             } else {
    5318                 int       steps   = 0;
    5319                 ClassNode currentClassNode = classNode;
    5320                 FieldNode field   = null;
    5321                 do {
    5322                     if( (field = currentClassNode.getField(variableName)) != null ) {
    5323                         if (methodNode == null || !methodNode.isStatic() || field.isStatic() ) {
    5324                             if (/*field.isDynamicType() || */field.isHolder()) {
    5325                                 expression.setResolveFailed(true);
    5326                                 expression.setFailure("reference type to be supported later");
    5327                                 return ;
    5328                             } else {
    5329                                 String type = field.getType();
    5330                                 Class cls = loadClass(type);
    5331                                 expression.setTypeClass(cls);
    5332                                 expression.setDynamic(field.isDynamicType());
    5333                                 return ;
    5334                             }
    5335                         }
    5336                     }
    5337                     steps++;
    5338                 } while( (currentClassNode = currentClassNode.getOuterClass()) != null );
    5339             }
    5340
    5341             //
    5342             // Finally, a new variable
    5343
    5344             String variableType = expression.getType();
    5345             if (variableType.length() > 0 && !variableType.equals("java.lang.Object")) {
    5346                 Class cls = loadClass(variableType);
    5347                 expression.setTypeClass(cls);
    5348                 return ;
    5349             }
    5350         } catch (Exception e) {
    5351             log.warning(e.getMessage());
    5352             expression.setResolveFailed(true);
    5353             expression.setFailure(e.getMessage());
    5354         }
    5355         return ;
    5356     }
    5357
    5358     public MetaMethod resolve(StaticMethodCallExpression staticCall) {
    5359        if (staticCall.isResolveFailed()) {
    5360             return null;
    5361         }
    5362         else if (staticCall.isTypeResolved()) {
    5363             return staticCall.getMetaMethod();
    5364         }
    5365
    5366         String ownerTypeName = staticCall.getOwnerType();
    5367         String meth = staticCall.getMethod();
    5368         Class ownerClass = null;
    5369         try {
    5370             ownerClass = loadClass(ownerTypeName);
    5371         } catch (Exception e) {
    5372             staticCall.setResolveFailed(true);
    5373             staticCall.setFailure("Owner type could not be resolved: " + e);
    5374             return null;
    5375         }
    5376
    5377         boolean isStaticCall = true;
    5378         boolean isSuperCall = false;
    5379
    5380         List arglist = new ArrayList();
    5381         Expression args = staticCall.getArguments();
    5382         if (args instanceof TupleExpression) {
    5383             TupleExpression tupleExpression = (TupleExpression) args;
    5384             List argexps = tupleExpression.getExpressions();
    5385             for (int i = 0; i < argexps.size(); i++) {
    5386                 Expression expression = (Expression) argexps.get(i);
    5387                 Class cls = expression.getTypeClass();
    5388                 if (cls == null) {
    5389                     staticCall.setResolveFailed(true);
    5390                     staticCall.setFailure("Argument type could not be resolved.");
    5391                     return null;
    5392                 }
    5393                 else {
    5394                     arglist.add(cls);
    5395                 }
    5396             }
    5397         } else if (args instanceof ClosureExpression) {
    5398             staticCall.setResolveFailed(true);
    5399             staticCall.setFailure("Resolving on Closure call not implemented yet. ");
    5400             return null;// todo
    5401         } else {
    5402             staticCall.setResolveFailed(true);
    5403             staticCall.setFailure("Unknown argument expression type.");
    5404             return null;
    5405         }
    5406
    5407
    5408         Class[] argsArray = new Class[arglist.size()];
    5409         arglist.toArray(argsArray);
    5410
    5411         if (ownerClass == Object.class) {
    5412             staticCall.setResolveFailed(true);
    5413             staticCall.setFailure("Resolving on java.lang.Object static call not supported. ");
    5414             return null; // use late binding for dynamic types
    5415         }
    5416         else if (ownerClass == null)  {
    5417             staticCall.setResolveFailed(true);
    5418             staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. ");
    5419             return null; // use dynamic dispatching for GroovyObject
    5420         }
    5421         else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call
    5422             staticCall.setResolveFailed(true);
    5423             staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. ");
    5424             return null;
    5425         }
    5426         else if (ownerClass.isPrimitive()) {
    5427             staticCall.setResolveFailed(true);
    5428             staticCall.setFailure("Could not use primitive as method owner");
    5429             return null; // todo handle primitives
    5430         }
    5431
    5432
    5433         //MetaMethod mmethod = InvokerHelper.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
    5434         // todo is this thread safe?
    5435         MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
    5436         if (mmethod!= null) {
    5437             staticCall.setMetaMethod(mmethod);
    5438         }
    5439         else {
    5440             staticCall.setResolveFailed(true);
    5441             staticCall.setFailure("Could not find MetaMethod in the MetaClass.");
    5442         }
    5443         return mmethod;
    5444     }
    5445
    5446     // the fowllowing asXXX() methods are copied from the Invoker class, to avoid initilization of an invoker instance,
    5447     // which has lots of baggage with it, notably the meta class stuff.
    5448     private static Object asType(Object object, Class type) {
    5449         if (object == null) {
    5450             return null;
    5451         }
    5452         if (type.isInstance(object)) {
    5453             return object;
    5454         }
    5455         if (type.equals(String.class)) {
    5456             return object.toString();
    5457         }
    5458         if (type.equals(Character.class)) {
    5459             if (object instanceof Number) {
    5460                 return asCharacter((Number) object);
    5461             }
    5462             else {
    5463                 String text = object.toString();
    5464                 if (text.length() == 1) {
    5465                     return new Character(text.charAt(0));
    5466                 }
    5467                 else {
    5468                     throw new ClassCastException("Cannot cast: " + text + " to a Character");
    5469                 }
    5470             }
    5471         }
    5472         if (Number.class.isAssignableFrom(type)) {
    5473             if (object instanceof Character) {
    5474                 return new Integer(((Character) object).charValue());
    5475             }
    5476             else if (object instanceof String) {
    5477                 String c = (String) object;
    5478                 if (c.length() == 1) {
    5479                     return new Integer(c.charAt(0));
    5480                 }
    5481                 else {
    5482                     throw new ClassCastException("Cannot cast: '" + c + "' to an Integer");
    5483                 }
    5484             }
    5485         }
    5486         if (object instanceof Number) {
    5487             Number n = (Number) object;
    5488             if (type.isPrimitive()) {
    5489                 if (type == byte.class) {
    5490                     return new Byte(n.byteValue());
    5491                 }
    5492                 if (type == char.class) {
    5493                     return new Character((char) n.intValue());
    5494                 }
    5495                 if (type == short.class) {
    5496                     return new Short(n.shortValue());
    5497                 }
    5498                 if (type == int.class) {
    5499                     return new Integer(n.intValue());
    5500                 }
    5501                 if (type == long.class) {
    5502                     return new Long(n.longValue());
    5503                 }
    5504                 if (type == float.class) {
    5505                     return new Float(n.floatValue());
    5506                 }
    5507                 if (type == double.class) {
    5508                     Double answer = new Double(n.doubleValue());
    5509                     //throw a runtime exception if conversion would be out-of-range for the type.
    5510                     if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
    5511                             || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
    5512                         throw new  GroovyRuntimeException("Automatic coercion of "+n.getClass().getName()
    5513                                 +" value "+n+" to double failed.  Value is out of range.");
    5514                     }
    5515                     return answer;
    5516                 }
    5517             }
    5518             else {
    5519                 if (Number.class.isAssignableFrom(type)) {
    5520                     if (type == Byte.class) {
    5521                         return new Byte(n.byteValue());
    5522                     }
    5523                     if (type == Character.class) {
    5524                         return new Character((char) n.intValue());
    5525                     }
    5526                     if (type == Short.class) {
    5527                         return new Short(n.shortValue());
    5528                     }
    5529                     if (type == Integer.class) {
    5530                         return new Integer(n.intValue());
    5531                     }
    5532                     if (type == Long.class) {
    5533                         return new Long(n.longValue());
    5534                     }
    5535                     if (type == Float.class) {
    5536                         return new Float(n.floatValue());
    5537                     }
    5538                     if (type == Double.class) {
    5539                         Double answer = new Double(n.doubleValue());
    5540                         //throw a runtime exception if conversion would be out-of-range for the type.
    5541                         if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
    5542                                 || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
    5543                             throw new  GroovyRuntimeException("Automatic coercion of "+n.getClass().getName()
    5544                                     +" value "+n+" to double failed.  Value is out of range.");
    5545                         }
    5546                         return answer;
    5547                     }
    5548
    5549                 }
    5550             }
    5551         }
    5552         if (type == Boolean.class) {
    5553             return asBool(object) ? Boolean.TRUE : Boolean.FALSE;
    5554         }
    5555         return object;
    5556     }
    5557
    5558     private static boolean asBool(Object object) {
    5559        if (object instanceof Boolean) {
    5560             Boolean booleanValue = (Boolean) object;
    5561             return booleanValue.booleanValue();
    5562         }
    5563         else if (object instanceof Matcher) {
    5564             Matcher matcher = (Matcher) object;
    5565             RegexSupport.setLastMatcher(matcher);
    5566             return matcher.find();
    5567         }
    5568         else if (object instanceof Collection) {
    5569             Collection collection = (Collection) object;
    5570             return !collection.isEmpty();
    5571         }
    5572         else if (object instanceof Number) {
    5573             Number n = (Number) object;
    5574             return n.intValue() != 0;
    5575         }
    5576         else {
    5577             return object != null;
    5578         }
    5579     }
    5580     private static Character asCharacter(Number value) {
    5581         return new Character((char) value.intValue());
    5582     }
    5583
    5584     private static Character asCharacter(String text) {
    5585         return new Character(text.charAt(0));
    5586     }
    5587 }