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 }