001    /*
002    $Id: AsmClassGenerator.java 4598 2006-12-22 20:21:21Z blackdrag $
003    
004    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006    Redistribution and use of this software and associated documentation
007    ("Software"), with or without modification, are permitted provided
008    that the following conditions are met:
009    
010    1. Redistributions of source code must retain copyright
011       statements and notices.  Redistributions must also contain a
012       copy of this document.
013    
014    2. Redistributions in binary form must reproduce the
015       above copyright notice, this list of conditions and the
016       following disclaimer in the documentation and/or other
017       materials provided with the distribution.
018    
019    3. The name "groovy" must not be used to endorse or promote
020       products derived from this Software without prior written
021       permission of The Codehaus.  For written permission,
022       please contact info@codehaus.org.
023    
024    4. Products derived from this Software may not be called "groovy"
025       nor may "groovy" appear in their names without prior written
026       permission of The Codehaus. "groovy" is a registered
027       trademark of The Codehaus.
028    
029    5. Due credit should be given to The Codehaus -
030       http://groovy.codehaus.org/
031    
032    THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033    ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034    NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036    THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043    OF THE POSSIBILITY OF SUCH DAMAGE.
044    */
045    
046    package org.codehaus.groovy.classgen;
047    
048    import groovy.lang.GroovyObject;
049    import groovy.lang.GroovyRuntimeException;
050    
051    import java.util.ArrayList;
052    import java.util.Collections;
053    import java.util.Comparator;
054    import java.util.HashSet;
055    import java.util.Iterator;
056    import java.util.LinkedList;
057    import java.util.List;
058    import java.util.Map;
059    import java.util.Set;
060    import java.util.logging.Logger;
061    
062    import org.codehaus.groovy.GroovyBugError;
063    import org.codehaus.groovy.ast.ASTNode;
064    import org.codehaus.groovy.ast.AnnotatedNode;
065    import org.codehaus.groovy.ast.AnnotationNode;
066    import org.codehaus.groovy.ast.ClassHelper;
067    import org.codehaus.groovy.ast.ClassNode;
068    import org.codehaus.groovy.ast.CompileUnit;
069    import org.codehaus.groovy.ast.ConstructorNode;
070    import org.codehaus.groovy.ast.FieldNode;
071    import org.codehaus.groovy.ast.InnerClassNode;
072    import org.codehaus.groovy.ast.MethodNode;
073    import org.codehaus.groovy.ast.Parameter;
074    import org.codehaus.groovy.ast.PropertyNode;
075    import org.codehaus.groovy.ast.VariableScope;
076    import org.codehaus.groovy.ast.expr.ArgumentListExpression;
077    import org.codehaus.groovy.ast.expr.ArrayExpression;
078    import org.codehaus.groovy.ast.expr.AttributeExpression;
079    import org.codehaus.groovy.ast.expr.BinaryExpression;
080    import org.codehaus.groovy.ast.expr.BitwiseNegExpression;
081    import org.codehaus.groovy.ast.expr.BooleanExpression;
082    import org.codehaus.groovy.ast.expr.CastExpression;
083    import org.codehaus.groovy.ast.expr.ClassExpression;
084    import org.codehaus.groovy.ast.expr.ClosureExpression;
085    import org.codehaus.groovy.ast.expr.ConstantExpression;
086    import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
087    import org.codehaus.groovy.ast.expr.DeclarationExpression;
088    import org.codehaus.groovy.ast.expr.Expression;
089    import org.codehaus.groovy.ast.expr.ExpressionTransformer;
090    import org.codehaus.groovy.ast.expr.FieldExpression;
091    import org.codehaus.groovy.ast.expr.GStringExpression;
092    import org.codehaus.groovy.ast.expr.ListExpression;
093    import org.codehaus.groovy.ast.expr.MapEntryExpression;
094    import org.codehaus.groovy.ast.expr.MapExpression;
095    import org.codehaus.groovy.ast.expr.MethodCallExpression;
096    import org.codehaus.groovy.ast.expr.MethodPointerExpression;
097    import org.codehaus.groovy.ast.expr.NegationExpression;
098    import org.codehaus.groovy.ast.expr.NotExpression;
099    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    }