View Javadoc

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