View Javadoc

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