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