View Javadoc

1   /*
2    * $Id: AsmClassGenerator.java,v 1.24 2005/02/24 15:33:31 jstrachan 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.*;
37  
38  import java.lang.reflect.Constructor;
39  import java.lang.reflect.Field;
40  import java.lang.reflect.Method;
41  import java.lang.reflect.Modifier;
42  import java.util.*;
43  import java.util.regex.Matcher;
44  import java.util.logging.Logger;
45  import java.security.AccessController;
46  import java.security.PrivilegedAction;
47  
48  import org.codehaus.groovy.ast.ASTNode;
49  import org.codehaus.groovy.ast.ClassNode;
50  import org.codehaus.groovy.ast.CompileUnit;
51  import org.codehaus.groovy.ast.ConstructorNode;
52  import org.codehaus.groovy.ast.FieldNode;
53  import org.codehaus.groovy.ast.GroovyCodeVisitor;
54  import org.codehaus.groovy.ast.InnerClassNode;
55  import org.codehaus.groovy.ast.MethodNode;
56  import org.codehaus.groovy.ast.Parameter;
57  import org.codehaus.groovy.ast.PropertyNode;
58  import org.codehaus.groovy.ast.Type;
59  import org.codehaus.groovy.ast.VariableScope;
60  import org.codehaus.groovy.ast.expr.*;
61  import org.codehaus.groovy.ast.stmt.AssertStatement;
62  import org.codehaus.groovy.ast.stmt.BlockStatement;
63  import org.codehaus.groovy.ast.stmt.BreakStatement;
64  import org.codehaus.groovy.ast.stmt.CaseStatement;
65  import org.codehaus.groovy.ast.stmt.CatchStatement;
66  import org.codehaus.groovy.ast.stmt.ContinueStatement;
67  import org.codehaus.groovy.ast.stmt.DoWhileStatement;
68  import org.codehaus.groovy.ast.stmt.ExpressionStatement;
69  import org.codehaus.groovy.ast.stmt.ForStatement;
70  import org.codehaus.groovy.ast.stmt.IfStatement;
71  import org.codehaus.groovy.ast.stmt.ReturnStatement;
72  import org.codehaus.groovy.ast.stmt.Statement;
73  import org.codehaus.groovy.ast.stmt.SwitchStatement;
74  import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
75  import org.codehaus.groovy.ast.stmt.ThrowStatement;
76  import org.codehaus.groovy.ast.stmt.TryCatchStatement;
77  import org.codehaus.groovy.ast.stmt.WhileStatement;
78  import org.codehaus.groovy.runtime.DefaultGroovyMethods;
79  import org.codehaus.groovy.runtime.RegexSupport;
80  import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
81  import org.codehaus.groovy.syntax.Token;
82  import org.codehaus.groovy.syntax.Types;
83  import org.codehaus.groovy.syntax.parser.RuntimeParserException;
84  import org.objectweb.asm.ClassVisitor;
85  import org.objectweb.asm.CodeVisitor;
86  import org.objectweb.asm.Label;
87  import org.objectweb.asm.ClassWriter;
88  
89  /***
90   * Generates Java class versions of Groovy classes using ASM.
91   *
92   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
93   * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
94   *
95   * @version $Revision: 1.24 $
96   */
97  public class AsmClassGenerator extends ClassGenerator {
98  
99      private Logger log = Logger.getLogger(getClass().getName());
100 
101     private ClassVisitor cw;
102     private CodeVisitor cv;
103     private GeneratorContext context;
104 
105     private String sourceFile;
106 
107     // current class details
108     private ClassNode classNode;
109     private ClassNode outermostClass;
110     private String internalClassName;
111     private String internalBaseClassName;
112 
113     /*** maps the variable names to the JVM indices */
114     private Map variableStack = new HashMap();
115 
116     /*** have we output a return statement yet */
117     private boolean outputReturn;
118 
119     /*** are we on the left or right of an expression */
120     private boolean leftHandExpression;
121 
122     // cached values
123     MethodCaller invokeMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethod");
124     MethodCaller invokeMethodSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethodSafe");
125     MethodCaller invokeStaticMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeStaticMethod");
126     MethodCaller invokeConstructorMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeConstructor");
127     MethodCaller invokeConstructorOfMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeConstructorOf");
128     MethodCaller invokeNoArgumentsConstructorOf = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsConstructorOf");
129     MethodCaller invokeClosureMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeClosure");
130     MethodCaller invokeSuperMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeSuperMethod");
131     MethodCaller invokeNoArgumentsMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsMethod");
132     MethodCaller invokeStaticNoArgumentsMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeStaticNoArgumentsMethod");
133 
134     MethodCaller asIntMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asInt");
135     MethodCaller asTypeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asType");
136 
137     MethodCaller getAttributeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttribute");
138     MethodCaller getAttributeSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttributeSafe");
139     MethodCaller setAttributeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setAttribute2");
140     MethodCaller setAttributeSafeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setAttributeSafe2");
141 
142     MethodCaller getPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getProperty");
143     MethodCaller getPropertySafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getPropertySafe");
144     MethodCaller setPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setProperty");
145     MethodCaller setPropertyMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setProperty2");
146     MethodCaller setPropertySafeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setPropertySafe2");
147 
148     MethodCaller getGroovyObjectPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getGroovyObjectProperty");
149     MethodCaller setGroovyObjectPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setGroovyObjectProperty");
150     MethodCaller asIteratorMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asIterator");
151     MethodCaller asBool = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asBool");
152     MethodCaller notBoolean = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "notBoolean");
153     MethodCaller notObject = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "notObject");
154     MethodCaller regexPattern = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "regexPattern");
155     MethodCaller negation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "negate");
156     MethodCaller bitNegation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "bitNegate");
157     MethodCaller convertPrimitiveArray = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "convertPrimitiveArray");
158     MethodCaller convertToPrimitiveArray = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "convertToPrimitiveArray");
159 
160     MethodCaller compareIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareIdentical");
161     MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual");
162     MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual");
163     MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo");
164     MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex");
165     MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex");
166     MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan");
167     MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual");
168     MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan");
169     MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual");
170     MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");
171 
172     MethodCaller createListMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createList");
173     MethodCaller createTupleMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createTuple");
174     MethodCaller createMapMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createMap");
175     MethodCaller createRangeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createRange");
176 
177     MethodCaller assertFailedMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "assertFailed");
178 
179     MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
180     MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
181 
182 
183     // current stack index
184     private int lastVariableIndex;
185     private static int tempVariableNameCounter;
186 
187     // exception blocks list
188     private List exceptionBlocks = new ArrayList();
189 
190     private boolean definingParameters;
191     private Set syntheticStaticFields = new HashSet();
192     private Set mutableVars = new HashSet();
193     private boolean passingClosureParams;
194 
195     private ConstructorNode constructorNode;
196     private MethodNode methodNode;
197     //private PropertyNode propertyNode;
198     private BlockScope scope;
199     private BytecodeHelper helper = new BytecodeHelper(null);
200 
201     private VariableScope variableScope;
202     public static final boolean CREATE_DEBUG_INFO = false;
203     public static final boolean CREATE_LINE_NUMBER_INFO = true;
204     private static final boolean MARK_START = true;
205 
206     public static final String EB_SWITCH_NAME = "static.dispatching";
207     public boolean ENABLE_EARLY_BINDING;
208     {    //
209         String ebSwitch = (String) AccessController.doPrivileged(new PrivilegedAction() {
210             public Object run() {
211                 return System.getProperty(EB_SWITCH_NAME, "false"); // set default to true if early binding is on by default.
212             }
213         });
214         //System.out.println("ebSwitch = " + ebSwitch);
215         if (ebSwitch.equals("true")) {
216             ENABLE_EARLY_BINDING  = true;
217         }
218         else if (ebSwitch.equals("false")) {
219             ENABLE_EARLY_BINDING  = false;
220         }
221         else {
222             ENABLE_EARLY_BINDING  = false;
223             log.warning("The value of system property " + EB_SWITCH_NAME + " is not recognized. Late dispatching is assumed. ");
224         }
225     }
226     public static final boolean ASM_DEBUG = false; // add marker in the bytecode to show source-byecode relationship
227     private int lineNumber = -1;
228     private int columnNumber = -1;
229     private ASTNode currentASTNode = null;
230 
231     private DummyClassGenerator dummyGen = null;
232     private ClassWriter dummyClassWriter = null;
233 
234     public AsmClassGenerator(
235         GeneratorContext context,
236         ClassVisitor classVisitor,
237         ClassLoader classLoader,
238         String sourceFile) {
239         super(classLoader);
240         this.context = context;
241         this.cw = classVisitor;
242         this.sourceFile = sourceFile;
243 
244         this.dummyClassWriter = new ClassWriter(true);
245         dummyGen  = new DummyClassGenerator(context, dummyClassWriter, classLoader, sourceFile);
246 
247     }
248 
249     // GroovyClassVisitor interface
250     //-------------------------------------------------------------------------
251     public void visitClass(ClassNode classNode) {
252         // todo to be tested
253         // createDummyClass(classNode);
254 
255         try {
256             syntheticStaticFields.clear();
257             this.classNode = classNode;
258             this.outermostClass = null;
259             this.internalClassName = BytecodeHelper.getClassInternalName(classNode.getName());
260 
261             //System.out.println("Generating class: " + classNode.getName());
262 
263             // lets check that the classes are all valid
264             classNode.setSuperClass(checkValidType(classNode.getSuperClass(), classNode, "Must be a valid base class"));
265             String[] interfaces = classNode.getInterfaces();
266             for (int i = 0; i < interfaces.length; i++ ) {
267                 interfaces[i] = checkValidType(interfaces[i], classNode, "Must be a valid interface name");
268             }
269 
270             this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
271 
272             cw.visit(
273                 asmJDKVersion,
274                 classNode.getModifiers(),
275                 internalClassName,
276                 internalBaseClassName,
277                 BytecodeHelper.getClassInternalNames(classNode.getInterfaces()),
278                 sourceFile);
279 
280             // set the optional enclosing method attribute of the current inner class
281 //          br comment out once Groovy uses the latest CVS HEAD of ASM
282 //            MethodNode enclosingMethod = classNode.getEnclosingMethod();
283 //            String ownerName = BytecodeHelper.getClassInternalName(enclosingMethod.getDeclaringClass().getName());
284 //            String descriptor = BytecodeHelper.getMethodDescriptor(enclosingMethod.getReturnType(), enclosingMethod.getParameters());
285 //            EnclosingMethodAttribute attr = new EnclosingMethodAttribute(ownerName,enclosingMethod.getName(),descriptor);
286 //            cw.visitAttribute(attr);
287 
288             classNode.visitContents(this);
289 
290             createSyntheticStaticFields();
291 
292             for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
293                 ClassNode innerClass = (ClassNode) iter.next();
294                 String innerClassName = innerClass.getName();
295                 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
296                 String outerClassName = internalClassName; // default for inner classes
297                 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
298                 if (enclosingMethod != null) {
299                     // local inner classes do not specify the outer class name
300                     outerClassName = null;
301                 }
302                 cw.visitInnerClass(
303                     innerClassInternalName,
304                     outerClassName,
305                     innerClassName,
306                     innerClass.getModifiers());
307             }
308 // br TODO an inner class should have an entry of itself
309             cw.visitEnd();
310         }
311         catch (GroovyRuntimeException e) {
312             e.setModule(classNode.getModule());
313             throw e;
314         }
315     }
316 
317     // create a surrogate class that represents the classNode
318     // the surrogate has the "face" of the real class. It's used for
319     // type resolving "this"
320     private void createDummyClass(ClassNode classNode) {
321         dummyGen.visitClass(classNode);
322         byte[] code = dummyClassWriter.toByteArray();
323 
324         ClassLoader parentLoader = getClass().getClassLoader();
325         GroovyClassLoader groovyLoader = new GroovyClassLoader(parentLoader);
326         Class theClass = groovyLoader.defineClass(classNode.getName(), code);
327 
328         if (theClass != null) {
329             classCache.put(classNode.getName(), theClass);
330         }
331     }
332 
333     public void visitConstructor(ConstructorNode node) {
334         // creates a MethodWriter for the (implicit) constructor
335         //String methodType = Type.getMethodDescriptor(VOID_TYPE, )
336 
337         this.constructorNode = node;
338         this.methodNode = null;
339         this.variableScope = null;
340 
341         visitParameters(node, node.getParameters());
342 
343         String methodType = BytecodeHelper.getMethodDescriptor("void", node.getParameters());
344         cv = cw.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
345         helper = new BytecodeHelper(cv);
346 
347         findMutableVariables();
348         resetVariableStack(node.getParameters());
349 
350         Statement code = node.getCode();
351         if (code == null || !firstStatementIsSuperInit(code)) {
352             // invokes the super class constructor
353             cv.visitVarInsn(ALOAD, 0);
354             cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "()V");
355         }
356         if (code != null) {
357             code.visit(this);
358         }
359 
360         cv.visitInsn(RETURN);
361         cv.visitMaxs(0, 0);
362     }
363 
364     public void visitMethod(MethodNode node) {
365         //System.out.println("Visiting method: " + node.getName() + " with
366         // return type: " + node.getReturnType());
367         this.constructorNode = null;
368         this.methodNode = node;
369         this.variableScope = null;
370 
371         visitParameters(node, node.getParameters());
372         node.setReturnType(checkValidType(node.getReturnType(), node, "Must be a valid return type"));
373 
374         String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
375         cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
376         Label labelStart = new Label();
377         cv.visitLabel(labelStart);
378         helper = new BytecodeHelper(cv);
379 
380         findMutableVariables();
381         resetVariableStack(node.getParameters());
382 
383 
384         outputReturn = false;
385 
386         node.getCode().visit(this);
387 
388         if (!outputReturn) {
389             cv.visitInsn(RETURN);
390         }
391 
392         // lets do all the exception blocks
393         for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) {
394             Runnable runnable = (Runnable) iter.next();
395             runnable.run();
396         }
397         exceptionBlocks.clear();
398 
399         Label labelEnd = new Label();
400         cv.visitLabel(labelEnd);
401 
402         // br experiment with local var table so debuggers can retrieve variable names
403         if (CREATE_DEBUG_INFO) {
404             Set vars = this.variableStack.keySet();
405             for (Iterator iterator = vars.iterator(); iterator.hasNext();) {
406                 String varName = (String) iterator.next();
407                 Variable v = (Variable)variableStack.get(varName);
408                 String type = v.getTypeName();
409                 type = BytecodeHelper.getTypeDescription(type);
410                 Label start = v.getStartLabel() != null ? v.getStartLabel() : labelStart;
411                 Label end = v.getEndLabel() != null ? v.getEndLabel() : labelEnd;
412                 cv.visitLocalVariable(varName, type, start, end, v.getIndex());
413             }
414         }
415         cv.visitMaxs(0, 0);
416     }
417 
418     protected void visitParameters(ASTNode node, Parameter[] parameters) {
419         for (int i = 0, size = parameters.length; i < size; i++ ) {
420             visitParameter(node, parameters[i]);
421         }
422     }
423 
424     protected void visitParameter(ASTNode node, Parameter parameter) {
425         if (! parameter.isDynamicType()) {
426             parameter.setType(checkValidType(parameter.getType(), node, "Must be a valid parameter class"));
427         }
428     }
429 
430     public void visitField(FieldNode fieldNode) {
431         onLineNumber(fieldNode, "visitField: " + fieldNode.getName());
432 
433         // lets check that the classes are all valid
434         fieldNode.setType(checkValidType(fieldNode.getType(), fieldNode, "Must be a valid field class for field: " + fieldNode.getName()));
435 
436         //System.out.println("Visiting field: " + fieldNode.getName() + " on
437         // class: " + classNode.getName());
438 
439         Object fieldValue = null;
440         Expression expression = fieldNode.getInitialValueExpression();
441         if (expression instanceof ConstantExpression) {
442             ConstantExpression constantExp = (ConstantExpression) expression;
443             Object value = constantExp.getValue();
444             if (isPrimitiveFieldType(fieldNode.getType())) {
445                 // lets convert any primitive types
446                 Class type = null;
447                 try {
448                     type = loadClass(fieldNode.getType());
449                     fieldValue = /*ScriptBytecodeAdapter.*/asType(value, type);
450                 }
451                 catch (Exception e) {
452                     log.warning("Caught unexpected: " + e);
453                 }
454             }
455         }
456         cw.visitField(
457             fieldNode.getModifiers(),
458             fieldNode.getName(),
459             BytecodeHelper.getTypeDescription(fieldNode.getType()),
460             null, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
461             null);
462     }
463 
464     /***
465      * Creates a getter, setter and field
466      */
467     public void visitProperty(PropertyNode statement) {
468         onLineNumber(statement, "visitProperty:" + statement.getField().getName());
469         //this.propertyNode = statement;
470         this.methodNode = null;
471     }
472 
473     // GroovyCodeVisitor interface
474     //-------------------------------------------------------------------------
475 
476     // Statements
477     //-------------------------------------------------------------------------
478 
479     public void visitForLoop(ForStatement loop) {
480         onLineNumber(loop, "visitForLoop");
481         Class elemType = null;
482         if (ENABLE_EARLY_BINDING) {
483             Expression collectionExp = loop.getCollectionExpression();
484             collectionExp.resolve(this);
485             Class cls = collectionExp.getTypeClass();
486             if (cls != null) {
487                 if (cls.isArray()) {
488                     elemType = cls.getComponentType();
489                     if (elemType != null) {
490                         Type varType = new Type(elemType.getName());
491                         loop.setVariableType(varType);
492                     }
493                 }
494                 else if (collectionExp instanceof ListExpression) {
495                     elemType = ((ListExpression)collectionExp).getComponentTypeClass();
496                     if (elemType != null) {
497                         Type varType = new Type(elemType.getName());
498                         loop.setVariableType(varType);
499                     }
500                 }
501                 else if (collectionExp instanceof RangeExpression) {
502                     // use the from type class. assuming both from and to are of the same type
503                     elemType = ((RangeExpression)collectionExp).getFrom().getTypeClass();
504                     if (elemType != null) {
505                         Type varType = new Type(elemType.getName());
506                         loop.setVariableType(varType);
507                     }
508                 }
509             }
510         }
511 
512 
513         //
514         // Declare the loop counter.
515         Type variableType = checkValidType(loop.getVariableType(), loop, "for loop variable");
516         Variable variable = defineVariable(loop.getVariable(), variableType, true);
517 
518         if( isInScriptBody() ) {
519             variable.setProperty( true );
520         }
521 
522 
523         //
524         // Then initialize the iterator and generate the loop control
525 
526         loop.getCollectionExpression().visit(this);
527 
528         asIteratorMethod.call(cv);
529 
530         final Variable iterTemp = storeInTemp("iterator", "java.util.Iterator");
531         final int iteratorIdx = iterTemp.getIndex();
532 
533         // to push scope here allows the iterator available after the loop, such as the i in: for (i in 1..5)
534         // move it to the top will make the iterator a local var in the for loop.
535         pushBlockScope();
536 
537         Label continueLabel = scope.getContinueLabel();
538         cv.visitJumpInsn(GOTO, continueLabel);
539         Label label2 = new Label();
540         cv.visitLabel(label2);
541 
542         final Class elemClass = elemType;
543         BytecodeExpression expression = new BytecodeExpression() {
544             public void visit(GroovyCodeVisitor visitor) {
545                 cv.visitVarInsn(ALOAD, iteratorIdx);
546                 iteratorNextMethod.call(cv);
547             }
548 
549             protected void resolveType(AsmClassGenerator resolver) {
550                 setTypeClass(elemClass);
551             }
552         };
553 
554         evaluateEqual( BinaryExpression.newAssignmentExpression(loop.getVariable(), expression) );
555         cv.visitInsn(POP); // br now the evaluateEqual() will leave a value on the stack. pop it.
556 
557         //
558         // Generate the loop body
559 
560         loop.getLoopBlock().visit(this);
561 
562 
563         //
564         // Generate the loop tail
565 
566         cv.visitLabel(continueLabel);
567         cv.visitVarInsn(ALOAD, iteratorIdx);
568 
569         iteratorHasNextMethod.call(cv);
570 
571         cv.visitJumpInsn(IFNE, label2);
572 
573         cv.visitLabel(scope.getBreakLabel());
574         popScope();
575     }
576 
577     public void visitWhileLoop(WhileStatement loop) {
578         onLineNumber(loop, "visitWhileLoop");
579 
580         pushBlockScope();
581 
582         Label continueLabel = scope.getContinueLabel();
583 
584         cv.visitJumpInsn(GOTO, continueLabel);
585         Label l1 = new Label();
586         cv.visitLabel(l1);
587 
588         loop.getLoopBlock().visit(this);
589 
590         cv.visitLabel(continueLabel);
591 
592         loop.getBooleanExpression().visit(this);
593 
594         cv.visitJumpInsn(IFNE, l1);
595 
596         cv.visitLabel(scope.getBreakLabel());
597         popScope();
598     }
599 
600     public void visitDoWhileLoop(DoWhileStatement loop) {
601         onLineNumber(loop, "visitDoWhileLoop");
602 
603         pushBlockScope();
604 
605         Label breakLabel = scope.getBreakLabel();
606 
607         Label continueLabel = scope.getContinueLabel();
608         cv.visitLabel(continueLabel);
609         Label l1 = new Label();
610 
611         loop.getLoopBlock().visit(this);
612 
613         cv.visitLabel(l1);
614 
615         loop.getBooleanExpression().visit(this);
616 
617         cv.visitJumpInsn(IFNE, continueLabel);
618 
619         cv.visitLabel(breakLabel);
620         popScope();
621     }
622 
623     public void visitIfElse(IfStatement ifElse) {
624         onLineNumber(ifElse, "visitIfElse");
625 
626         ifElse.getBooleanExpression().visit(this);
627 
628         Label l0 = new Label();
629         cv.visitJumpInsn(IFEQ, l0);
630         pushBlockScope(false, false);
631         ifElse.getIfBlock().visit(this);
632         popScope();
633 
634         Label l1 = new Label();
635         cv.visitJumpInsn(GOTO, l1);
636         cv.visitLabel(l0);
637 
638         pushBlockScope(false, false);
639         ifElse.getElseBlock().visit(this);
640         cv.visitLabel(l1);
641         popScope();
642     }
643 
644     public void visitTernaryExpression(TernaryExpression expression) {
645         onLineNumber(expression, "visitTernaryExpression");
646 
647         expression.getBooleanExpression().visit(this);
648 
649         Label l0 = new Label();
650         cv.visitJumpInsn(IFEQ, l0);
651         expression.getTrueExpression().visit(this);
652 
653         Label l1 = new Label();
654         cv.visitJumpInsn(GOTO, l1);
655         cv.visitLabel(l0);
656 
657         expression.getFalseExpression().visit(this);
658         cv.visitLabel(l1);
659     }
660 
661     public void visitAssertStatement(AssertStatement statement) {
662         onLineNumber(statement, "visitAssertStatement");
663 
664         //System.out.println("Assert: " + statement.getLineNumber() + " for: "
665         // + statement.getText());
666 
667         BooleanExpression booleanExpression = statement.getBooleanExpression();
668         booleanExpression.visit(this);
669 
670         Label l0 = new Label();
671         cv.visitJumpInsn(IFEQ, l0);
672 
673         // do nothing
674 
675         Label l1 = new Label();
676         cv.visitJumpInsn(GOTO, l1);
677         cv.visitLabel(l0);
678 
679         // push expression string onto stack
680         String expressionText = booleanExpression.getText();
681         List list = new ArrayList();
682         addVariableNames(booleanExpression, list);
683         if (list.isEmpty()) {
684             cv.visitLdcInsn(expressionText);
685         }
686         else {
687             boolean first = true;
688 
689             // lets create a new expression
690             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
691             cv.visitInsn(DUP);
692             cv.visitLdcInsn(expressionText + ". Values: ");
693 
694             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V");
695 
696             Variable assertTemp = visitASTOREInTemp("assert");
697             int tempIndex  = assertTemp.getIndex();
698 
699             for (Iterator iter = list.iterator(); iter.hasNext();) {
700                 String name = (String) iter.next();
701                 String text = name + " = ";
702                 if (first) {
703                     first = false;
704                 }
705                 else {
706                     text = ", " + text;
707                 }
708 
709                 cv.visitVarInsn(ALOAD, tempIndex);
710                 cv.visitLdcInsn(text);
711                 cv.visitMethodInsn(
712                     INVOKEVIRTUAL,
713                     "java/lang/StringBuffer",
714                     "append",
715                     "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
716                 cv.visitInsn(POP);
717 
718                 cv.visitVarInsn(ALOAD, tempIndex);
719                 new VariableExpression(name).visit(this);
720                 cv.visitMethodInsn(
721                     INVOKEVIRTUAL,
722                     "java/lang/StringBuffer",
723                     "append",
724                     "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
725                 cv.visitInsn(POP);
726 
727             }
728             cv.visitVarInsn(ALOAD, tempIndex);
729             removeVar(assertTemp);
730         }
731         // now the optional exception expression
732         statement.getMessageExpression().visit(this);
733 
734         assertFailedMethod.call(cv);
735         cv.visitLabel(l1);
736     }
737 
738     private void addVariableNames(Expression expression, List list) {
739         if (expression instanceof BooleanExpression) {
740             BooleanExpression boolExp = (BooleanExpression) expression;
741             addVariableNames(boolExp.getExpression(), list);
742         }
743         else if (expression instanceof BinaryExpression) {
744             BinaryExpression binExp = (BinaryExpression) expression;
745             addVariableNames(binExp.getLeftExpression(), list);
746             addVariableNames(binExp.getRightExpression(), list);
747         }
748         else if (expression instanceof VariableExpression) {
749             VariableExpression varExp = (VariableExpression) expression;
750             list.add(varExp.getVariable());
751         }
752     }
753 
754     public void visitTryCatchFinally(TryCatchStatement statement) {
755         onLineNumber(statement, "visitTryCatchFinally");
756 // todo need to add blockscope handling
757         CatchStatement catchStatement = statement.getCatchStatement(0);
758 
759         Statement tryStatement = statement.getTryStatement();
760 
761         if (tryStatement.isEmpty() || catchStatement == null) {
762             final Label l0 = new Label();
763             cv.visitLabel(l0);
764 
765             tryStatement.visit(this);
766 
767 
768             int index1 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
769             int index2 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
770 
771             final Label l1 = new Label();
772             cv.visitJumpInsn(JSR, l1);
773             final Label l2 = new Label();
774             cv.visitLabel(l2);
775             final Label l3 = new Label();
776             cv.visitJumpInsn(GOTO, l3);
777             final Label l4 = new Label();
778             cv.visitLabel(l4);
779             cv.visitVarInsn(ASTORE, index1);
780             cv.visitJumpInsn(JSR, l1);
781             final Label l5 = new Label();
782             cv.visitLabel(l5);
783             cv.visitVarInsn(ALOAD, index1);
784             cv.visitInsn(ATHROW);
785             cv.visitLabel(l1);
786             cv.visitVarInsn(ASTORE, index2);
787 
788             statement.getFinallyStatement().visit(this);
789 
790             cv.visitVarInsn(RET, index2);
791             cv.visitLabel(l3);
792 
793             exceptionBlocks.add(new Runnable() {
794                 public void run() {
795                     cv.visitTryCatchBlock(l0, l2, l4, null);
796                     cv.visitTryCatchBlock(l4, l5, l4, null);
797                 }
798             });
799 
800         }
801         else {
802             int finallySubAddress = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
803             int anyExceptionIndex = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex();
804 
805             // start try block, label needed for exception table
806             final Label tryStart = new Label();
807             cv.visitLabel(tryStart);
808             tryStatement.visit(this);
809             // goto finally part
810             final Label finallyStart = new Label();
811             cv.visitJumpInsn(GOTO, finallyStart);
812             // marker needed for Exception table
813             final Label tryEnd = new Label();
814             cv.visitLabel(tryEnd);
815             
816             for (Iterator it=statement.getCatchStatements().iterator(); it.hasNext();) {
817                 catchStatement = (CatchStatement) it.next();
818                 String exceptionType =
819                     checkValidType(catchStatement.getExceptionType(), catchStatement, "in catch statement");
820                 int exceptionIndex = defineVariable(catchStatement.getVariable(), exceptionType, false).getIndex();
821                 
822                 // start catch block, label needed for exception table
823                 final Label catchStart = new Label();
824                 cv.visitLabel(catchStart);
825                 // store the exception 
826                 cv.visitVarInsn(ASTORE, exceptionIndex);
827                 catchStatement.visit(this);
828                 // goto finally start
829                 cv.visitJumpInsn(GOTO, finallyStart);
830                 // add exception to table
831                 final String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType);
832                 exceptionBlocks.add(new Runnable() {
833                     public void run() {
834                         cv.visitTryCatchBlock(tryStart, tryEnd, catchStart, exceptionTypeInternalName);
835                     }
836                 });
837             }
838             
839             // marker needed for the exception table
840             final Label endOfAllCatches = new Label();
841             cv.visitLabel(endOfAllCatches);
842             
843             // start finally
844             cv.visitLabel(finallyStart);
845             Label finallySub = new Label();
846             // run finally sub
847             cv.visitJumpInsn(JSR, finallySub);
848             // goto end of finally
849             Label afterFinally = new Label();
850             cv.visitJumpInsn(GOTO, afterFinally);
851             
852             // start a block catching any Exception
853             final Label catchAny = new Label();
854             cv.visitLabel(catchAny);
855             //store exception
856             cv.visitVarInsn(ASTORE, anyExceptionIndex);
857             // run finally subroutine
858             cv.visitJumpInsn(JSR, finallySub);
859             // load the exception and rethrow it
860             cv.visitVarInsn(ALOAD, anyExceptionIndex);
861             cv.visitInsn(ATHROW);
862             
863             // start the finally subroutine
864             cv.visitLabel(finallySub);
865             // store jump address
866             cv.visitVarInsn(ASTORE, finallySubAddress);
867             if (!statement.getFinallyStatement().isEmpty())
868                 statement.getFinallyStatement().visit(this);
869             // return from subroutine
870             cv.visitVarInsn(RET, finallySubAddress);
871             
872             // end of all catches and finally parts
873             cv.visitLabel(afterFinally);
874             
875             // add catch any block to exception table
876             exceptionBlocks.add(new Runnable() {
877                 public void run() {
878                     cv.visitTryCatchBlock(tryStart, endOfAllCatches, catchAny, null);
879                 }
880             });
881         }
882     }
883 
884     private Variable storeInTemp(String name, String type) {
885         Variable var  = defineVariable(createVariableName(name), type, false);
886         int varIdx = var.getIndex();
887         cv.visitVarInsn(ASTORE, varIdx);
888         if (CREATE_DEBUG_INFO) cv.visitLabel(var.getStartLabel());
889         return var;
890     }
891 
892     public void visitSwitch(SwitchStatement statement) {
893         onLineNumber(statement, "visitSwitch");
894 
895         statement.getExpression().visit(this);
896 
897         // switch does not have a continue label. use its parent's for continue
898         pushBlockScope(false, true);
899         //scope.setContinueLabel(scope.getParent().getContinueLabel());
900 
901 
902         int switchVariableIndex = defineVariable(createVariableName("switch"), "java.lang.Object").getIndex();
903         cv.visitVarInsn(ASTORE, switchVariableIndex);
904 
905         List caseStatements = statement.getCaseStatements();
906         int caseCount = caseStatements.size();
907         Label[] labels = new Label[caseCount + 1];
908         for (int i = 0; i < caseCount; i++) {
909             labels[i] = new Label();
910         }
911 
912         int i = 0;
913         for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
914             CaseStatement caseStatement = (CaseStatement) iter.next();
915             visitCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]);
916         }
917 
918         statement.getDefaultStatement().visit(this);
919 
920         cv.visitLabel(scope.getBreakLabel());
921 
922         popScope();
923     }
924 
925     public void visitCaseStatement(CaseStatement statement) {
926     }
927 
928     public void visitCaseStatement(
929         CaseStatement statement,
930         int switchVariableIndex,
931         Label thisLabel,
932         Label nextLabel) {
933 
934         onLineNumber(statement, "visitCaseStatement");
935 
936         cv.visitVarInsn(ALOAD, switchVariableIndex);
937         statement.getExpression().visit(this);
938 
939         isCaseMethod.call(cv);
940 
941         Label l0 = new Label();
942         cv.visitJumpInsn(IFEQ, l0);
943 
944         cv.visitLabel(thisLabel);
945 
946         statement.getCode().visit(this);
947 
948         // now if we don't finish with a break we need to jump past
949         // the next comparison
950         if (nextLabel != null) {
951             cv.visitJumpInsn(GOTO, nextLabel);
952         }
953 
954         cv.visitLabel(l0);
955     }
956 
957     public void visitBreakStatement(BreakStatement statement) {
958         onLineNumber(statement, "visitBreakStatement");
959 
960         Label breakLabel = scope.getBreakLabel();
961         if (breakLabel != null ) {
962             cv.visitJumpInsn(GOTO, breakLabel);
963         } else {
964             // should warn that break is not allowed in the context.
965         }
966     }
967 
968     public void visitContinueStatement(ContinueStatement statement) {
969         onLineNumber(statement, "visitContinueStatement");
970 
971         Label continueLabel = scope.getContinueLabel();
972         if (continueLabel != null ) {
973             cv.visitJumpInsn(GOTO, continueLabel);
974         } else {
975             // should warn that continue is not allowed in the context.
976         }
977     }
978 
979     public void visitSynchronizedStatement(SynchronizedStatement statement) {
980         onLineNumber(statement, "visitSynchronizedStatement");
981 
982         statement.getExpression().visit(this);
983 
984         int index = defineVariable(createVariableName("synchronized"), "java.lang.Integer").getIndex();
985 
986         cv.visitVarInsn(ASTORE, index);
987         cv.visitInsn(MONITORENTER);
988         final Label l0 = new Label();
989         cv.visitLabel(l0);
990 
991         statement.getCode().visit(this);
992 
993         cv.visitVarInsn(ALOAD, index);
994         cv.visitInsn(MONITOREXIT);
995         final Label l1 = new Label();
996         cv.visitJumpInsn(GOTO, l1);
997         final Label l2 = new Label();
998         cv.visitLabel(l2);
999         cv.visitVarInsn(ALOAD, index);
1000         cv.visitInsn(MONITOREXIT);
1001         cv.visitInsn(ATHROW);
1002         cv.visitLabel(l1);
1003 
1004         exceptionBlocks.add(new Runnable() {
1005             public void run() {
1006                 cv.visitTryCatchBlock(l0, l2, l2, null);
1007             }
1008         });
1009     }
1010 
1011     public void visitThrowStatement(ThrowStatement statement) {
1012         statement.getExpression().visit(this);
1013 
1014         // we should infer the type of the exception from the expression
1015         cv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
1016 
1017         cv.visitInsn(ATHROW);
1018     }
1019 
1020     public void visitReturnStatement(ReturnStatement statement) {
1021         onLineNumber(statement, "visitReturnStatement");
1022         String returnType = methodNode.getReturnType();
1023         if (returnType.equals("void")) {
1024         	if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) {
1025                 throwException("Cannot use return statement with an expression on a method that returns void");
1026         	}
1027             cv.visitInsn(RETURN);
1028             outputReturn = true;
1029             return;
1030         }
1031 
1032         Expression expression = statement.getExpression();
1033         evaluateExpression(expression);
1034         if (returnType.equals("java.lang.Object") && expression.getType() != null && expression.getType().equals("void")) {
1035             cv.visitInsn(ACONST_NULL); // cheat the caller
1036             cv.visitInsn(ARETURN);
1037         } else {
1038             //return is based on class type
1039             //TODO: make work with arrays
1040             // we may need to cast
1041             helper.unbox(returnType);
1042             if (returnType.equals("double")) {
1043                 cv.visitInsn(DRETURN);
1044             }
1045             else if (returnType.equals("float")) {
1046                 cv.visitInsn(FRETURN);
1047             }
1048             else if (returnType.equals("long")) {
1049                 cv.visitInsn(LRETURN);
1050             }
1051             else if (returnType.equals("boolean")) {
1052                 cv.visitInsn(IRETURN);
1053             }
1054             else if (
1055                     returnType.equals("char")
1056                     || returnType.equals("byte")
1057                     || returnType.equals("int")
1058                     || returnType.equals("short")) { //byte,short,boolean,int are
1059                 // all IRETURN
1060                 cv.visitInsn(IRETURN);
1061             }
1062             else {
1063                 doConvertAndCast(returnType, expression, false);
1064                 cv.visitInsn(ARETURN);
1065 
1066                 /*
1067                 if (c == Boolean.class) {
1068                 Label l0 = new Label();
1069                 cv.visitJumpInsn(IFEQ, l0);
1070                 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
1071                 cv.visitInsn(ARETURN);
1072                 cv.visitLabel(l0);
1073                 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
1074                 cv.visitInsn(ARETURN);
1075                 }
1076                 else {
1077                 if (isValidTypeForCast(returnType) && !returnType.equals(c.getName())) {
1078                 doConvertAndCast(returnType, expression);
1079                 }
1080                 cv.visitInsn(ARETURN);
1081                 }
1082                 */
1083             }
1084         }
1085         outputReturn = true;
1086     }
1087 
1088     /***
1089      * Casts to the given type unless it can be determined that the cast is unnecessary
1090      */
1091     protected void doConvertAndCast(String type, Expression expression, boolean ignoreAutoboxing) {
1092         String expType = getExpressionType(expression);
1093         // temp resolution: convert all primitive casting to corresponsing Object type
1094         if (!ignoreAutoboxing && BytecodeHelper.isPrimitiveType(type)) {
1095             type = BytecodeHelper.getObjectTypeForPrimitive(type);
1096         }
1097         if (expType == null || !type.equals(expType)) {
1098             doConvertAndCast(type);
1099         }
1100     }
1101 
1102     /***
1103      * @param expression
1104      */
1105     protected void evaluateExpression(Expression expression) {
1106         visitAndAutoboxBoolean(expression);
1107         //expression.visit(this);
1108 
1109         Expression assignExpr = createReturnLHSExpression(expression);
1110         if (assignExpr != null) {
1111             leftHandExpression = false;
1112             assignExpr.visit(this);
1113         }
1114     }
1115 
1116     public void visitExpressionStatement(ExpressionStatement statement) {
1117         onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName());
1118 
1119         Expression expression = statement.getExpression();
1120 // disabled in favor of JIT resolving
1121 //        if (ENABLE_EARLY_BINDING)
1122 //            expression.resolve(this);
1123 
1124         visitAndAutoboxBoolean(expression);
1125 
1126         if (isPopRequired(expression)) {
1127             cv.visitInsn(POP);
1128         }
1129     }
1130 
1131     // Expressions
1132     //-------------------------------------------------------------------------
1133 
1134     public void visitBinaryExpression(BinaryExpression expression) {
1135         onLineNumber(expression, "visitBinaryExpression: \"" + expression.getOperation().getText() + "\" ");
1136         switch (expression.getOperation().getType()) {
1137             case Types.EQUAL : // = assignment
1138                 evaluateEqual(expression);
1139                 break;
1140 
1141             case Types.COMPARE_IDENTICAL : // ===
1142                 evaluateBinaryExpression(compareIdenticalMethod, expression);
1143                 break;
1144 
1145             case Types.COMPARE_EQUAL : // ==
1146                 evaluateBinaryExpression(compareEqualMethod, expression);
1147                 break;
1148 
1149             case Types.COMPARE_NOT_EQUAL :
1150                 evaluateBinaryExpression(compareNotEqualMethod, expression);
1151                 break;
1152 
1153             case Types.COMPARE_TO :
1154                 evaluateCompareTo(expression);
1155                 break;
1156 
1157             case Types.COMPARE_GREATER_THAN :
1158                 evaluateBinaryExpression(compareGreaterThanMethod, expression);
1159                 break;
1160 
1161             case Types.COMPARE_GREATER_THAN_EQUAL :
1162                 evaluateBinaryExpression(compareGreaterThanEqualMethod, expression);
1163                 break;
1164 
1165             case Types.COMPARE_LESS_THAN :
1166                 evaluateBinaryExpression(compareLessThanMethod, expression);
1167                 break;
1168 
1169             case Types.COMPARE_LESS_THAN_EQUAL :
1170                 evaluateBinaryExpression(compareLessThanEqualMethod, expression);
1171                 break;
1172 
1173             case Types.LOGICAL_AND :
1174                 evaluateLogicalAndExpression(expression);
1175                 break;
1176 
1177             case Types.LOGICAL_OR :
1178                 evaluateLogicalOrExpression(expression);
1179                 break;
1180 
1181 	    case Types.BITWISE_AND :
1182                 evaluateBinaryExpression("and", expression);
1183                 break;
1184 
1185             case Types.BITWISE_AND_EQUAL :
1186                 evaluateBinaryExpressionWithAsignment("and", expression);
1187                 break;
1188 
1189             case Types.BITWISE_OR :
1190                 evaluateBinaryExpression("or", expression);
1191                 break;
1192 
1193             case Types.BITWISE_OR_EQUAL :
1194                 evaluateBinaryExpressionWithAsignment("or", expression);
1195                 break;
1196 
1197             case Types.BITWISE_XOR :
1198                 evaluateBinaryExpression("xor", expression);
1199                 break;
1200 
1201             case Types.BITWISE_XOR_EQUAL :
1202                 evaluateBinaryExpressionWithAsignment("xor", expression);
1203                 break;
1204 
1205             case Types.PLUS :
1206                 {
1207                     if (ENABLE_EARLY_BINDING) {
1208                         expression.resolve(this);
1209                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
1210                             evaluateBinaryExpression("plus", expression);
1211                             break;
1212                         }
1213                         Expression leftExpression = expression.getLeftExpression();
1214                         Expression rightExpression = expression.getRightExpression();
1215                         Class lclass = leftExpression.getTypeClass();
1216                         Class rclass = rightExpression.getTypeClass();
1217                         if (lclass == null || rclass == null) {
1218                             evaluateBinaryExpression("plus", expression);
1219                             break;
1220                         }
1221                         if (lclass == String.class && rclass == String.class) {
1222 //                            MethodCallExpression call = new MethodCallExpression(
1223 //                                    leftExpression,
1224 //                                    "concat",
1225 //                                    new ArgumentListExpression(new Expression[] {rightExpression}));
1226 //                            call.setTypeClass(String.class); // must do to avoid excessive resolving
1227 //                            visitMethodCallExpression(call);
1228                             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
1229                             cv.visitInsn(DUP);
1230                             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
1231                             load(leftExpression);
1232                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
1233                             load(rightExpression);
1234                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
1235                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
1236                         }
1237                         else if (lclass == String.class && Number.class.isAssignableFrom(rclass) ) {
1238                             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
1239                             cv.visitInsn(DUP);
1240                             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
1241                             load(leftExpression);
1242                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
1243                             load(rightExpression);
1244                             // will Object.toString() work here?
1245                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
1246                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
1247                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
1248                         }
1249                         else if (rclass == String.class && Number.class.isAssignableFrom(lclass) ) {
1250                             cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
1251                             cv.visitInsn(DUP);
1252                             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V");
1253                             load(leftExpression);
1254                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
1255                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
1256                             load(rightExpression);
1257                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); // note the arg is object type for safety
1258                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
1259                         }
1260                         else if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
1261                             // assuming all return boxed version for primitives
1262                             load(leftExpression);
1263                             helper.quickUnboxIfNecessary(int.class);
1264                             load(rightExpression);
1265                             helper.quickUnboxIfNecessary(int.class);
1266                             cv.visitInsn(IADD);
1267                             helper.quickBoxIfNecessary(int.class);
1268                         }
1269                         else if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
1270                             // let's use groovy utilities in the DefaultGroovyMethods
1271                             load(leftExpression);
1272                             load(rightExpression);
1273                             cv.visitMethodInsn(
1274                                     INVOKESTATIC,
1275                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1276                                     "plus",
1277                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
1278                         }
1279                         else { // todo add more more number optimiztion
1280                             evaluateBinaryExpression("plus", expression);
1281                         }
1282 
1283                     } else {
1284                         evaluateBinaryExpression("plus", expression);
1285                     }
1286                 }
1287                 break;
1288 
1289             case Types.PLUS_EQUAL :
1290                 evaluateBinaryExpressionWithAsignment("plus", expression);
1291                 break;
1292             case Types.MINUS :
1293                 {
1294                     if (ENABLE_EARLY_BINDING) {
1295                         expression.resolve(this);
1296                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
1297                             evaluateBinaryExpression("minus", expression);
1298                             break;
1299                         }
1300                         Expression leftExpression = expression.getLeftExpression();
1301                         Expression rightExpression = expression.getRightExpression();
1302                         Class lclass = leftExpression.getTypeClass();
1303                         Class rclass = rightExpression.getTypeClass();
1304                         if (lclass == null || rclass == null) {
1305                             evaluateBinaryExpression("minus", expression);
1306                             break;
1307                         }
1308                         if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
1309                             // assuming all return boxed version for primitives
1310                             load(leftExpression);
1311                             helper.quickUnboxIfNecessary(int.class);
1312                             load(rightExpression);
1313                             helper.quickUnboxIfNecessary(int.class);
1314                             cv.visitInsn(ISUB);
1315                             helper.quickBoxIfNecessary(int.class);
1316                         }
1317                         else
1318                         if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
1319                             // let's use groovy utilities in the DefaultGroovyMethods
1320                             load(leftExpression);
1321                             load(rightExpression);
1322                             cv.visitMethodInsn(
1323                                     INVOKESTATIC,
1324                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1325                                     "minus",
1326                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
1327                         }
1328                         else { // todo add more more number optimiztion
1329                             evaluateBinaryExpression("minus", expression);
1330                         }
1331                     } else {
1332                         evaluateBinaryExpression("minus", expression);
1333                     }
1334                 }
1335                 break;
1336             case Types.MINUS_EQUAL :
1337                 evaluateBinaryExpressionWithAsignment("minus", expression);
1338                 break;
1339 
1340             case Types.MULTIPLY :
1341                 {
1342                     if (ENABLE_EARLY_BINDING) {
1343                         expression.resolve(this);
1344                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
1345                             evaluateBinaryExpression("multiply", expression);
1346                             break;
1347                         }
1348                         Expression leftExpression = expression.getLeftExpression();
1349                         Expression rightExpression = expression.getRightExpression();
1350                         Class lclass = leftExpression.getTypeClass();
1351                         Class rclass = rightExpression.getTypeClass();
1352                         if (lclass == null || rclass == null) {
1353                             evaluateBinaryExpression("multiply", expression);
1354                             break;
1355                         }
1356                         if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
1357                             // assuming all return boxed version for primitives
1358                             load(leftExpression);
1359                             helper.quickUnboxIfNecessary(int.class);
1360                             load(rightExpression);
1361                             helper.quickUnboxIfNecessary(int.class);
1362                             cv.visitInsn(IMUL);
1363                             helper.quickBoxIfNecessary(int.class);
1364                         }
1365                         else if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
1366                             // let's use groovy utilities in the DefaultGroovyMethods
1367                             load(leftExpression);
1368                             load(rightExpression);
1369                             cv.visitMethodInsn(
1370                                     INVOKESTATIC,
1371                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1372                                     "multiply",
1373                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
1374                         }
1375                         else { // todo add more more number optimiztion
1376                             evaluateBinaryExpression("multiply", expression);
1377                         }
1378                     } else {
1379                         evaluateBinaryExpression("multiply", expression);
1380                     }
1381                 }
1382 
1383                 break;
1384 
1385             case Types.MULTIPLY_EQUAL :
1386                 evaluateBinaryExpressionWithAsignment("multiply", expression);
1387                 break;
1388 
1389             case Types.DIVIDE :
1390                 //SPG don't use divide since BigInteger implements directly
1391                 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
1392                 {
1393                     if (ENABLE_EARLY_BINDING) {
1394                         expression.resolve(this);
1395                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
1396                             evaluateBinaryExpression("div", expression);
1397                             break;
1398                         }
1399                         Expression leftExpression = expression.getLeftExpression();
1400                         Expression rightExpression = expression.getRightExpression();
1401                         Class lclass = leftExpression.getTypeClass();
1402                         Class rclass = rightExpression.getTypeClass();
1403                         if (lclass == null || rclass == null) {
1404                             evaluateBinaryExpression("div", expression);
1405                             break;
1406                         }
1407 //
1408 //                        if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) {
1409 //                            // assuming all return boxed version for primitives
1410 //                            load(leftExpression);
1411 //                            helper.quickUnboxIfNecessary(int.class);
1412 //                            cv.visitInsn(I2D);
1413 //                            load(rightExpression);
1414 //                            helper.quickUnboxIfNecessary(int.class);
1415 //                            cv.visitInsn(I2D);
1416 //                            cv.visitInsn(DDIV);
1417 //                            helper.quickBoxIfNecessary(double.class);
1418 //                        }
1419 //                        else
1420                             if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
1421                             // let's use groovy utilities in the DefaultGroovyMethods
1422                             load(leftExpression);
1423                             load(rightExpression);
1424                             cv.visitMethodInsn(
1425                                     INVOKESTATIC,
1426                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1427                                     "div",
1428                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
1429                         }
1430                         else { // todo add more more number optimiztion
1431                             evaluateBinaryExpression("div", expression);
1432                         }
1433                     } else {
1434                         evaluateBinaryExpression("div", expression);
1435                     }
1436                 }
1437 
1438                 break;
1439 
1440             case Types.DIVIDE_EQUAL :
1441                 //SPG don't use divide since BigInteger implements directly
1442                 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
1443                 evaluateBinaryExpressionWithAsignment("div", expression);
1444                 break;
1445 
1446             case Types.INTDIV :
1447                 {
1448                     if (ENABLE_EARLY_BINDING) {
1449                         expression.resolve(this);
1450                         if (expression.isResolveFailed() || !expression.isTypeResolved()) {
1451                             evaluateBinaryExpression("intdiv", expression);
1452                             break;
1453                         }
1454                         Expression leftExpression = expression.getLeftExpression();
1455                         Expression rightExpression = expression.getRightExpression();
1456                         Class lclass = leftExpression.getTypeClass();
1457                         Class rclass = rightExpression.getTypeClass();
1458                         if (lclass == null || rclass == null) {
1459                             evaluateBinaryExpression("intdiv", expression);
1460                             break;
1461                         }
1462                         if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) {
1463                             // let's use groovy utilities in the DefaultGroovyMethods
1464                             load(leftExpression);
1465                             load(rightExpression);
1466                             cv.visitMethodInsn(
1467                                     INVOKESTATIC,
1468                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1469                                     "intdiv",
1470                                     "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;");
1471                         }
1472                         else { // todo add more more number optimiztion
1473                             evaluateBinaryExpression("intdiv", expression);
1474                         }
1475                     } else {
1476                         evaluateBinaryExpression("intdiv", expression);
1477                     }
1478                 }
1479                 break;
1480 
1481             case Types.INTDIV_EQUAL :
1482                 evaluateBinaryExpressionWithAsignment("intdiv", expression);
1483                 break;
1484 
1485             case Types.MOD :
1486                 evaluateBinaryExpression("mod", expression);
1487                 break;
1488 
1489             case Types.MOD_EQUAL :
1490                 evaluateBinaryExpressionWithAsignment("mod", expression);
1491                 break;
1492 
1493             case Types.POWER :
1494                 evaluateBinaryExpression("power", expression);
1495                 break;
1496 
1497             case Types.POWER_EQUAL :
1498                 evaluateBinaryExpressionWithAsignment("power", expression);
1499                 break;
1500 
1501             case Types.LEFT_SHIFT :
1502                 evaluateBinaryExpression("leftShift", expression);
1503                 break;
1504 
1505             case Types.LEFT_SHIFT_EQUAL :
1506                 evaluateBinaryExpressionWithAsignment("leftShift", expression);
1507                 break;
1508 
1509             case Types.RIGHT_SHIFT :
1510                 evaluateBinaryExpression("rightShift", expression);
1511                 break;
1512 
1513             case Types.RIGHT_SHIFT_EQUAL :
1514                 evaluateBinaryExpressionWithAsignment("rightShift", expression);
1515                 break;
1516 
1517             case Types.RIGHT_SHIFT_UNSIGNED :
1518                 evaluateBinaryExpression("rightShiftUnsigned", expression);
1519                 break;
1520 
1521             case Types.RIGHT_SHIFT_UNSIGNED_EQUAL :
1522                 evaluateBinaryExpressionWithAsignment("rightShiftUnsigned", expression);
1523                 break;
1524 
1525             case Types.KEYWORD_INSTANCEOF :
1526                 evaluateInstanceof(expression);
1527                 break;
1528 
1529             case Types.FIND_REGEX :
1530                 evaluateBinaryExpression(findRegexMethod, expression);
1531                 break;
1532 
1533             case Types.MATCH_REGEX :
1534                 evaluateBinaryExpression(matchRegexMethod, expression);
1535                 break;
1536 
1537             case Types.LEFT_SQUARE_BRACKET :
1538                 if (leftHandExpression) {
1539                     throwException("Should not be called here. Possible reason: postfix operation on array.");
1540                     // This is handled right now in the evaluateEqual()
1541                     // should support this here later
1542                     //evaluateBinaryExpression("putAt", expression);
1543                 }
1544                 else if (ENABLE_EARLY_BINDING) {
1545                     expression.resolve(this);
1546                     if (expression.isResolveFailed() || !expression.isTypeResolved()) {
1547                         evaluateBinaryExpression("getAt", expression);
1548                         break;
1549                     }
1550                     Expression leftExpression = expression.getLeftExpression();
1551                     Expression rightExpression = expression.getRightExpression();
1552                     Class lclass = leftExpression.getTypeClass();
1553                     Class rclass = rightExpression.getTypeClass();
1554                     if (lclass == null || rclass == null) {
1555                         evaluateBinaryExpression("getAt", expression);
1556                         break;
1557                     }
1558                     if (lclass == String.class && rclass == Integer.class) {
1559                         load(leftExpression); cast(String.class);
1560                         load(rightExpression); helper.quickUnboxIfNecessary(int.class);
1561                         cv.visitMethodInsn(
1562                                 INVOKESTATIC,
1563                                 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1564                                 "getAt",
1565                                 "([Ljava/lang/String;I)Ljava/lang/String;");
1566                         break;
1567                     }
1568                     else if (lclass.isArray() && rclass == Integer.class) {
1569                         load(leftExpression); // cast it?
1570                         load(rightExpression); helper.quickUnboxIfNecessary(int.class);
1571                         Class elemType = lclass.getComponentType();
1572                         if (!elemType.isPrimitive()) {
1573                             cv.visitMethodInsn(
1574                                     INVOKESTATIC,
1575                                     BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1576                                     "getAt",
1577                                     "([Ljava/lang/Object;I)Ljava/lang/Object;");
1578                             cast(elemType);
1579                         }
1580                         else {
1581                             evaluateBinaryExpression("getAt", expression); // todo more optim
1582                         }
1583                         break;
1584                     }
1585                     else if (List.class == lclass && rclass == Integer.class){
1586                         // there is special logic in treating list subscript
1587                         load(leftExpression); cast(List.class);
1588                         load(rightExpression); helper.quickUnboxIfNecessary(int.class);
1589                             //INVOKESTATIC org/codehaus/groovy/runtime/DefaultGroovyMethods getAt (Ljava/util/List;I)Ljava/lang/Object;
1590                         cv.visitMethodInsn(
1591                                 INVOKESTATIC,
1592                                 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1593                                 "getAt",
1594                                 "(Ljava/util/List;I)Ljava/lang/Object;");
1595                         break;
1596                     }
1597                     else if (Map.class.isAssignableFrom(lclass)){ // todo test this
1598                         visitMethodCallExpression(
1599                                 new MethodCallExpression(
1600                                         leftExpression,
1601                                         "get",
1602                                         new ArgumentListExpression(
1603                                                 new Expression[] { rightExpression})));
1604                         break;
1605                     }
1606                     else {
1607                         evaluateBinaryExpression("getAt", expression); // todo more optim
1608                         break;
1609                     }
1610                 }
1611                 else {
1612                     evaluateBinaryExpression("getAt", expression);
1613                 }
1614                 break;
1615 
1616             default :
1617                 throwException("Operation: " + expression.getOperation() + " not supported");
1618         }
1619     }
1620 
1621     private void load(Expression exp) {
1622 
1623         boolean wasLeft = leftHandExpression;
1624         leftHandExpression = false;
1625 //        if (CREATE_DEBUG_INFO)
1626 //            helper.mark("-- loading expression: " + exp.getClass().getName() +
1627 //                    " at [" + exp.getLineNumber() + ":" + exp.getColumnNumber() + "]");
1628         //exp.visit(this);
1629         visitAndAutoboxBoolean(exp);
1630 //        if (CREATE_DEBUG_INFO)
1631 //            helper.mark(" -- end of loading --");
1632 
1633 
1634         if (ENABLE_EARLY_BINDING){
1635 // casting might be expensive. should do JIT casting
1636 
1637 //            Class cls = exp.getTypeClass();
1638 //            if (cls != null && !cls.isPrimitive() && cls != Object.class) {
1639 //                cast(cls);
1640 //            }
1641         }
1642         //evaluateExpression(exp);
1643         leftHandExpression  = wasLeft;
1644     }
1645 
1646     public void visitPostfixExpression(PostfixExpression expression) {
1647         if (ENABLE_EARLY_BINDING) {
1648             int type = expression.getOperation().getType();
1649             expression.resolve(this);
1650             if (expression.isResolveFailed() || !expression.isTypeResolved()) {
1651                 evaluatePostfixMethod("next", expression.getExpression());
1652                 return;
1653             }
1654             Class lclass = expression.getTypeClass();
1655             Expression exp = expression.getExpression();
1656             String func = type == Types.PLUS_PLUS ? "next" : "previous";
1657             int op = type == Types.PLUS_PLUS ? IADD : ISUB;
1658 
1659             if (lclass == Integer.class) {
1660                 load(exp);
1661                 cv.visitInsn(DUP); // leave the old value on the stack;
1662                 helper.quickUnboxIfNecessary(int.class);
1663                 cv.visitInsn(ICONST_1);
1664                 cv.visitInsn(op);
1665                 helper.quickBoxIfNecessary(int.class);
1666                 store(exp);
1667             }
1668             else if (Number.class.isAssignableFrom(lclass)) {
1669                 // let's use groovy utilities in the DefaultGroovyMethods
1670                 load(exp);
1671                 cv.visitInsn(DUP); // leave the old value on the stack;
1672                 cv.visitMethodInsn(
1673                         INVOKESTATIC,
1674                         BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()),
1675                         func,
1676                         "(Ljava/lang/Number;)Ljava/lang/Number;");
1677                 store(exp);
1678             }
1679             else { // todo add more more number optimiztion
1680                 evaluatePostfixMethod(func, exp);
1681             }
1682 
1683         } else {
1684             switch (expression.getOperation().getType()) {
1685                 case Types.PLUS_PLUS :
1686                     evaluatePostfixMethod("next", expression.getExpression());
1687                     break;
1688                 case Types.MINUS_MINUS :
1689                     evaluatePostfixMethod("previous", expression.getExpression());
1690                     break;
1691             }
1692         }
1693     }
1694 
1695     // store the data on the stack to the expression (variablem, property, field, etc.
1696     private void store(Expression expression) {
1697         if (expression instanceof BinaryExpression) {
1698             throwException("BinaryExpression appeared on LHS. ");
1699         }
1700         if (ASM_DEBUG) {
1701             if (expression instanceof VariableExpression) {
1702                 helper.mark(((VariableExpression)expression).getVariable());
1703             }
1704         }
1705         boolean wasLeft = leftHandExpression;
1706         leftHandExpression = true;
1707         expression.visit(this);
1708         //evaluateExpression(expression);
1709         leftHandExpression = wasLeft;
1710         return;
1711     }
1712 
1713     private void throwException(String s) {
1714         //throw new ClassGeneratorException(s + ". Source: " + classNode.getName() + ":[" + this.lineNumber + ":" + this.columnNumber + "]");
1715         throw new RuntimeParserException(s, currentASTNode);
1716     }
1717 
1718     public void visitPrefixExpression(PrefixExpression expression) {
1719         switch (expression.getOperation().getType()) {
1720             case Types.PLUS_PLUS :
1721                 evaluatePrefixMethod("next", expression.getExpression());
1722                 break;
1723             case Types.MINUS_MINUS :
1724                 evaluatePrefixMethod("previous", expression.getExpression());
1725                 break;
1726         }
1727     }
1728 
1729     public void visitClosureExpression(ClosureExpression expression) {
1730         ClassNode innerClass = createClosureClass(expression);
1731         addInnerClass(innerClass);
1732         String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName());
1733 
1734         ClassNode owner = innerClass.getOuterClass();
1735         String ownerTypeName = owner.getName();
1736 /*
1737         if (classNode.isStaticClass() || isStaticMethod()) {
1738             ownerTypeName = "java.lang.Class";
1739         }
1740 */
1741         passingClosureParams = true;
1742         List constructors = innerClass.getDeclaredConstructors();
1743         ConstructorNode node = (ConstructorNode) constructors.get(0);
1744         Parameter[] localVariableParams = node.getParameters();
1745 
1746 
1747         //
1748         // Define in the context any variables that will be
1749         // created inside the closure.  Note that the first two
1750         // parameters are always _outerInstance and _delegate,
1751         // so we don't worry about them.
1752 
1753         for (int i = 2; i < localVariableParams.length; i++) {
1754             Parameter param = localVariableParams[i];
1755             String name = param.getName();
1756 
1757             if (variableStack.get(name) == null && classNode.getField(name) == null) {
1758                 defineVariable(name, "java.lang.Object"); // todo  should use param type is available
1759             }
1760         }
1761 
1762         cv.visitTypeInsn(NEW, innerClassinternalName);
1763         cv.visitInsn(DUP);
1764         if (isStaticMethod() || classNode.isStaticClass()) {
1765             visitClassExpression(new ClassExpression(ownerTypeName));
1766         }
1767         else {
1768             loadThisOrOwner();
1769         }
1770 
1771         if (innerClass.getSuperClass().equals("groovy.lang.Closure")) {
1772             if (isStaticMethod()) {
1773                 /***
1774                  * todo could maybe stash this expression in a JVM variable
1775                  * from previous statement above
1776                  */
1777                 visitClassExpression(new ClassExpression(ownerTypeName));
1778             }
1779             else {
1780                 loadThisOrOwner();
1781             }
1782         }
1783 
1784         //String prototype = "(L" + BytecodeHelper.getClassInternalName(ownerTypeName) + ";Ljava/lang/Object;";
1785 
1786         // now lets load the various parameters we're passing
1787         for (int i = 2; i < localVariableParams.length; i++) {
1788             Parameter param = localVariableParams[i];
1789             String name = param.getName();
1790 
1791             if (variableStack.get(name) == null) {
1792                 visitFieldExpression(new FieldExpression(classNode.getField(name)));
1793             }
1794             else {
1795                 visitVariableExpression(new VariableExpression(name));
1796             }
1797             //prototype = prototype + "L" + BytecodeHelper.getClassInternalName(param.getType()) + ";";
1798         }
1799         passingClosureParams = false;
1800 
1801         // we may need to pass in some other constructors
1802         //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
1803         cv.visitMethodInsn(
1804             INVOKESPECIAL,
1805             innerClassinternalName,
1806             "<init>",
1807             BytecodeHelper.getMethodDescriptor("void", localVariableParams));
1808     }
1809 
1810     /***
1811      * Loads either this object or if we're inside a closure then load the top level owner
1812      */
1813     protected void loadThisOrOwner() {
1814         if (isInnerClass()) {
1815             visitFieldExpression(new FieldExpression(classNode.getField("owner")));
1816         }
1817         else {
1818             cv.visitVarInsn(ALOAD, 0);
1819         }
1820     }
1821 
1822     public void visitRegexExpression(RegexExpression expression) {
1823         expression.getRegex().visit(this);
1824         regexPattern.call(cv);
1825     }
1826 
1827     /***
1828      * Generate byte code for constants
1829      * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a>
1830      */
1831     public void visitConstantExpression(ConstantExpression expression) {
1832         Object value = expression.getValue();
1833         helper.loadConstant(value);
1834     }
1835 
1836     public void visitNegationExpression(NegationExpression expression) {
1837         Expression subExpression = expression.getExpression();
1838         subExpression.visit(this);
1839         negation.call(cv);
1840     }
1841 
1842     public void visitBitwiseNegExpression(BitwiseNegExpression expression) {
1843         Expression subExpression = expression.getExpression();
1844         subExpression.visit(this);
1845         bitNegation.call(cv);
1846     }
1847 
1848     public void visitCastExpression(CastExpression expression) {
1849         String type = expression.getType();
1850         type = checkValidType(type, expression, "in cast");
1851 
1852         visitAndAutoboxBoolean(expression.getExpression());
1853 
1854         doConvertAndCast(type, expression.getExpression(), expression.isIgnoringAutoboxing());
1855     }
1856 
1857     public void visitNotExpression(NotExpression expression) {
1858         Expression subExpression = expression.getExpression();
1859         subExpression.visit(this);
1860 
1861         // This is not the best way to do this. Javac does it by reversing the
1862         // underlying expressions but that proved
1863         // fairly complicated for not much gain. Instead we'll just use a
1864         // utility function for now.
1865         if (isComparisonExpression(expression.getExpression())) {
1866             notBoolean.call(cv);
1867         }
1868         else {
1869             notObject.call(cv);
1870         }
1871     }
1872 
1873     /***
1874      * return a primitive boolean value of the BooleanExpresion.
1875      * @param expression
1876      */
1877     public void visitBooleanExpression(BooleanExpression expression) {
1878         expression.getExpression().visit(this);
1879 
1880         if (!isComparisonExpression(expression.getExpression())) {
1881 // comment out for optimization when boolean values are not autoboxed for eg. function calls.
1882 //           Class typeClass = expression.getExpression().getTypeClass();
1883 //           if (typeClass != null && typeClass != boolean.class) {
1884                 asBool.call(cv); // to return a primitive boolean
1885 //            }
1886         }
1887     }
1888 
1889     public void visitMethodCallExpression(MethodCallExpression call) {
1890         onLineNumber(call, "visitMethodCallExpression: \"" + call.getMethod() + "\":");
1891         if (ENABLE_EARLY_BINDING)
1892             call.resolve(this);
1893 
1894         this.leftHandExpression = false;
1895 
1896         Expression arguments = call.getArguments();
1897         /*
1898          * if (arguments instanceof TupleExpression) { TupleExpression
1899          * tupleExpression = (TupleExpression) arguments; int size =
1900          * tupleExpression.getExpressions().size(); if (size == 0) { arguments =
1901          * ConstantExpression.EMPTY_ARRAY; } }
1902          */
1903         boolean superMethodCall = MethodCallExpression.isSuperMethodCall(call);
1904         String method = call.getMethod();
1905         if (superMethodCall && method.equals("<init>")) {
1906             /*** todo handle method types! */
1907             cv.visitVarInsn(ALOAD, 0);
1908             if (isInClosureConstructor()) { // br use the second param to init the super class (Closure)
1909                 cv.visitVarInsn(ALOAD, 2);
1910                 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
1911             }
1912             else {
1913                 cv.visitVarInsn(ALOAD, 1);
1914                 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
1915             }
1916         }
1917         else {
1918             // are we a local variable
1919             if (isThisExpression(call.getObjectExpression()) && isFieldOrVariable(method) && ! classNode.hasPossibleMethod(method, arguments)) {
1920                 /*
1921                  * if (arguments instanceof TupleExpression) { TupleExpression
1922                  * tupleExpression = (TupleExpression) arguments; int size =
1923                  * tupleExpression.getExpressions().size(); if (size == 1) {
1924                  * arguments = (Expression)
1925                  * tupleExpression.getExpressions().get(0); } }
1926                  */
1927 
1928                 // lets invoke the closure method
1929                 visitVariableExpression(new VariableExpression(method));
1930                 arguments.visit(this);
1931                 invokeClosureMethod.call(cv);
1932             }
1933             else {
1934                 if (superMethodCall) {
1935                     if (method.equals("super") || method.equals("<init>")) {
1936                         ConstructorNode superConstructorNode = findSuperConstructor(call);
1937 
1938                         cv.visitVarInsn(ALOAD, 0);
1939 
1940                         loadArguments(superConstructorNode.getParameters(), arguments);
1941 
1942                         String descriptor = BytecodeHelper.getMethodDescriptor("void", superConstructorNode.getParameters());
1943                         cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", descriptor);
1944                     }
1945                     else {
1946                         MethodNode superMethodNode = findSuperMethod(call);
1947 
1948                         cv.visitVarInsn(ALOAD, 0);
1949 
1950                         loadArguments(superMethodNode.getParameters(), arguments);
1951 
1952                         String descriptor = BytecodeHelper.getMethodDescriptor(superMethodNode.getReturnType(), superMethodNode.getParameters());
1953                         cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superMethodNode.getDeclaringClass().getName()), method, descriptor);
1954                     }
1955                 }
1956                 else {
1957                     // let's try early binding
1958                     if (ENABLE_EARLY_BINDING) {
1959                         try {
1960                             MetaMethod metamethod = call.getMetaMethod(); // todo change it to resolveMethodCallExpression
1961                             if (metamethod != null) {
1962                                 Class decClass = metamethod.getDeclaringClass();
1963                                 String ownerClassName = null;
1964                                 if (decClass == null) {
1965                                     // meaning the class is the current class
1966                                     ownerClassName = BytecodeHelper.getClassInternalName(classNode.getName());
1967                                 }
1968                                 else {
1969                                     ownerClassName = BytecodeHelper.getClassInternalName(decClass.getName());
1970                                 }
1971 
1972                                 String methodName = call.getMethod();
1973                                 String descr = BytecodeHelper.getMethodDescriptor(metamethod);
1974                                 Class[] params = metamethod.getParameterTypes();
1975                                 //
1976                                 Label l2 = new Label();
1977 
1978                                 if (metamethod.isStatic()) {
1979                                 } else {
1980                                     boolean wasLeft = leftHandExpression;
1981                                     leftHandExpression = false;
1982                                     call.getObjectExpression().visit(this);
1983 
1984                                     if (call.isSafe()) {
1985                                         helper.dup();
1986                                         cv.visitJumpInsn(IFNULL, l2);
1987                                     }
1988 
1989                                     cv.visitTypeInsn(CHECKCAST, ownerClassName);
1990                                     leftHandExpression = wasLeft;
1991                                 }
1992                                 //
1993                                 if (arguments instanceof TupleExpression) {
1994                                     TupleExpression tupleExpression = (TupleExpression) arguments;
1995                                     List argexps = tupleExpression.getExpressions();
1996                                     for (int i = 0; i < argexps.size(); i++) {
1997                                         Expression expression = (Expression) argexps.get(i);
1998                                         load(expression);
1999 
2000                                         if (params[i].isPrimitive() /*&& !expression.getTypeClass().isPrimitive()*/) { // data always boxed
2001                                             cast(params[i]);
2002                                             helper.quickUnboxIfNecessary(params[i]);
2003                                         }
2004                                         else if (params[i].isArray() && params[i].getComponentType().isPrimitive() ) {
2005                                             new ClassExpression(params[i].getComponentType()).visit(this);
2006                                             convertToPrimitiveArray.call(cv);
2007                                             cast(params[i]);
2008                                         }
2009                                         else {
2010                                             if (expression.getTypeClass() == GString.class && params[i] == String.class){
2011                                                 cast(GString.class);
2012                                                 cv.visitMethodInsn(
2013                                                         INVOKEVIRTUAL,
2014                                                         "java/lang/Object",
2015                                                         "toString",
2016                                                         "()Ljava/lang/String;"
2017                                                 );
2018                                             }
2019                                             else {
2020                                                 cast(params[i]);
2021                                             }
2022                                         }
2023                                     }
2024                                     if (metamethod.isStatic()) {
2025                                         cv.visitMethodInsn(INVOKESTATIC, ownerClassName, methodName, descr);
2026                                     }
2027                                     else if (decClass != null && decClass.isInterface()){
2028                                         cv.visitMethodInsn(INVOKEINTERFACE, ownerClassName, methodName, descr);
2029                                     }
2030                                     else {
2031                                         cv.visitMethodInsn(INVOKEVIRTUAL, ownerClassName, methodName, descr);
2032                                     }
2033                                     call.setTypeClass(metamethod.getReturnType());
2034                                     if (metamethod.getReturnType().isPrimitive()
2035                                             && metamethod.getReturnType() != void.class
2036                                             //&& metamethod.getReturnType() != boolean.class
2037                                     ) {
2038                                         helper.quickBoxIfNecessary(metamethod.getReturnType());
2039                                     }
2040                                     if (call.isSafe()) {
2041                                         Label l3 = new Label();
2042                                         cv.visitJumpInsn(GOTO, l3);
2043                                         cv.visitLabel(l2);
2044                                         cv.visitInsn(POP);
2045                                         cv.visitInsn(ACONST_NULL);
2046                                         cv.visitLabel(l3);
2047                                     }
2048                                     return;
2049                                 } else {
2050                                     throw new GroovyRuntimeException("arguments type not handled. fall through to late binding");
2051                                 }
2052                             }
2053                         } catch (Exception e) {
2054 //                            System.out.println(this.classNode.getName() + ":" + this.methodNode.getName());
2055 //                            //e.printStackTrace(); //System.out.println(e.getMessage());
2056 //                            log.info("ignore: attempt early binding: " + e.getMessage());
2057                             // fall through
2058                         }
2059                     } // end of early binding trial
2060 
2061                     if (emptyArguments(arguments) && !call.isSafe()) {
2062                         call.getObjectExpression().visit(this);
2063                         cv.visitLdcInsn(method);
2064                         invokeNoArgumentsMethod.call(cv); // todo try if we can do early binding
2065                     }
2066                     else {
2067                         if (argumentsUseStack(arguments)) {
2068 
2069                             arguments.visit(this);
2070 
2071                             Variable tv = visitASTOREInTemp(method + "_arg");
2072                             int paramIdx = tv.getIndex();
2073 
2074                             call.getObjectExpression().visit(this); // xxx
2075 
2076                             cv.visitLdcInsn(method);
2077 
2078                             cv.visitVarInsn(ALOAD, paramIdx);
2079                             removeVar(tv);
2080                         }
2081                         else {
2082                             call.getObjectExpression().visit(this);
2083                             cv.visitLdcInsn(method);
2084                             arguments.visit(this);
2085                         }
2086 
2087                         if (call.isSafe()) {
2088                             invokeMethodSafeMethod.call(cv);
2089                         }
2090                         else {
2091                             invokeMethodMethod.call(cv);
2092                         }
2093                     }
2094                 }
2095             }
2096         }
2097     }
2098 
2099     /***
2100      * Loads and coerces the argument values for the given method call
2101      */
2102     protected void loadArguments(Parameter[] parameters, Expression expression) {
2103         TupleExpression argListExp = (TupleExpression) expression;
2104         List arguments = argListExp.getExpressions();
2105         for (int i = 0, size = arguments.size(); i < size; i++) {
2106             Expression argExp = argListExp.getExpression(i);
2107             Parameter param = parameters[i];
2108             visitAndAutoboxBoolean(argExp);
2109 
2110             String type = param.getType();
2111             if (BytecodeHelper.isPrimitiveType(type)) {
2112                 helper.unbox(type);
2113             }
2114 
2115             String expType = getExpressionType(argExp);
2116             if (isValidTypeForCast(type) && (expType == null || !type.equals(expType))) {
2117                 doConvertAndCast(type);
2118             }
2119  //           doConvertAndCast(type, argExp);
2120         }
2121     }
2122 
2123     /***
2124      * Attempts to find the method of the given name in a super class
2125      */
2126     protected MethodNode findSuperMethod(MethodCallExpression call) {
2127         String methodName = call.getMethod();
2128         TupleExpression argExpr = (TupleExpression) call.getArguments();
2129         int argCount = argExpr.getExpressions().size();
2130         ClassNode superClassNode = classNode.getSuperClassNode();
2131         if (superClassNode != null) {
2132             List methods = superClassNode.getMethods(methodName);
2133             for (Iterator iter = methods.iterator(); iter.hasNext(); ) {
2134                 MethodNode method = (MethodNode) iter.next();
2135                 if (method.getParameters().length == argCount) {
2136                     return method;
2137                 }
2138             }
2139         }
2140         throwException("No such method: " + methodName + " for class: " + classNode.getName());
2141         return null; // should not come here
2142     }
2143 
2144     /***
2145      * Attempts to find the constructor in a super class
2146      */
2147     protected ConstructorNode findSuperConstructor(MethodCallExpression call) {
2148         TupleExpression argExpr = (TupleExpression) call.getArguments();
2149         int argCount = argExpr.getExpressions().size();
2150         ClassNode superClassNode = classNode.getSuperClassNode();
2151         if (superClassNode != null) {
2152             List constructors = superClassNode.getDeclaredConstructors();
2153             for (Iterator iter = constructors.iterator(); iter.hasNext(); ) {
2154                 ConstructorNode constructor = (ConstructorNode) iter.next();
2155                 if (constructor.getParameters().length == argCount) {
2156                     return constructor;
2157                 }
2158             }
2159         }
2160         throwException("No such constructor for class: " + classNode.getName());
2161         return null; // should not come here
2162     }
2163 
2164     protected boolean emptyArguments(Expression arguments) {
2165         if (arguments instanceof TupleExpression) {
2166             TupleExpression tupleExpression = (TupleExpression) arguments;
2167             int size = tupleExpression.getExpressions().size();
2168             return size == 0;
2169         }
2170         return false;
2171     }
2172 
2173     public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
2174         this.leftHandExpression = false;
2175 
2176         Expression arguments = call.getArguments();
2177         if (emptyArguments(arguments)) {
2178             cv.visitLdcInsn(call.getType());
2179             cv.visitLdcInsn(call.getMethod());
2180 
2181             invokeStaticNoArgumentsMethod.call(cv);
2182         }
2183         else {
2184             if (arguments instanceof TupleExpression) {
2185                 TupleExpression tupleExpression = (TupleExpression) arguments;
2186                 int size = tupleExpression.getExpressions().size();
2187                 if (size == 1) {
2188                     arguments = (Expression) tupleExpression.getExpressions().get(0);
2189                 }
2190             }
2191 
2192             cv.visitLdcInsn(call.getOwnerType());
2193             cv.visitLdcInsn(call.getMethod());
2194             arguments.visit(this);
2195 
2196             invokeStaticMethodMethod.call(cv);
2197         }
2198     }
2199 
2200     public void visitConstructorCallExpression(ConstructorCallExpression call) {
2201         onLineNumber(call, "visitConstructorCallExpression: \"" + call.getTypeToSet() + "\":");
2202         do {
2203             if (ENABLE_EARLY_BINDING) {
2204                 call.resolve(this);
2205                 if (call.isResolveFailed() || call.getTypeClass() == null) {
2206                     break;
2207                 }
2208                 else {
2209                     try {
2210                         Constructor ctor = call.getConstructor(); // todo change it to resolveMethodCallExpression
2211                         if (ctor != null) {
2212                             Class decClass = ctor.getDeclaringClass();
2213                             String ownerClassName = null;
2214                             if (decClass == null) {
2215                                 // meaning the class is the current class
2216                                 ownerClassName = BytecodeHelper.getClassInternalName(classNode.getName());
2217                             }
2218                             else {
2219                                 ownerClassName = BytecodeHelper.getClassInternalName(decClass.getName());
2220                             }
2221 
2222                             Class[] params = ctor.getParameterTypes();
2223                             StringBuffer argbuf = new StringBuffer("(");
2224                             for (int i = 0; i < params.length; i++) {
2225                                 Class arg = params[i];
2226                                 String descr = BytecodeHelper.getTypeDescription(arg);
2227                                 argbuf.append(descr);
2228                             }
2229                             argbuf.append(")V");
2230                             //
2231                             cv.visitTypeInsn(NEW, ownerClassName);
2232                             cv.visitInsn(DUP);
2233 
2234                             //
2235                             Expression arguments = call.getArguments();
2236                             if (arguments instanceof TupleExpression) {
2237                                 TupleExpression tupleExpression = (TupleExpression) arguments;
2238                                 List argexps = tupleExpression.getExpressions();
2239                                 for (int i = 0; i < argexps.size(); i++) {
2240                                     Expression expression = (Expression) argexps.get(i);
2241                                     load(expression);
2242                                     if (params[i].isPrimitive() /*&& !expression.getTypeClass().isPrimitive()*/) { // data always boxed
2243                                         cast(params[i]);
2244                                         helper.quickUnboxIfNecessary(params[i]);
2245                                     }
2246                                     else if (params[i].isArray() && params[i].getComponentType().isPrimitive() ) {
2247                                         new ClassExpression(params[i].getComponentType()).visit(this);
2248                                         convertToPrimitiveArray.call(cv);
2249                                         cast(params[i]);
2250                                     }
2251                                     else {
2252                                         //? if the target is String , I might as well call Object.toString() regardless
2253                                         if (expression.getTypeClass() == GString.class && params[i] == String.class){
2254                                             cast(GString.class);
2255                                             cv.visitMethodInsn(
2256                                                     INVOKEVIRTUAL,
2257                                                     "java/lang/Object",
2258                                                     "toString",
2259                                                     "()Ljava/lang/String;"
2260                                             );
2261                                         }
2262                                         else {
2263                                             cast(params[i]);
2264                                         }
2265                                     }
2266                                 }
2267 
2268                                 cv.visitMethodInsn(INVOKESPECIAL, ownerClassName, "<init>", argbuf.toString());
2269                                 return;
2270                             } else {
2271                                 throw new GroovyRuntimeException("arguments type not handled. fall through to late binding");
2272                             }
2273                         }
2274                     } catch (Exception e) {
2275 //                        System.out.println(this.classNode.getName() + ":" + this.methodNode.getName());
2276                         //e.printStackTrace(); //System.out.println(e.getMessage());
2277 //                        log.info("ignore: attempt early binding: " + e.getMessage());
2278                         break;// fall through
2279                     }
2280                 }
2281             }
2282         } while(false);
2283 
2284         this.leftHandExpression = false;
2285 
2286         Expression arguments = call.getArguments();
2287         if (arguments instanceof TupleExpression) {
2288             TupleExpression tupleExpression = (TupleExpression) arguments;
2289             int size = tupleExpression.getExpressions().size();
2290             if (size == 0) {
2291                 arguments = null;
2292             }
2293 //            else if (size == 1) { // why unpack the tuple of 1 component?
2294 //                arguments = (Expression) tupleExpression.getExpressions().get(0);
2295 //            }
2296         }
2297 
2298         // lets check that the type exists
2299         String type = checkValidType(call.getType(), call, "in constructor call");
2300 
2301         //System.out.println("Constructing: " + type);
2302 
2303         visitClassExpression(new ClassExpression(type));
2304         if (arguments !=null) {
2305                arguments.visit(this);
2306             invokeConstructorOfMethod.call(cv);     // todo subject to opti
2307         } else {
2308             invokeNoArgumentsConstructorOf.call(cv); // todo subject to opti
2309         }
2310         /*
2311          * cv.visitLdcInsn(type);
2312          *
2313          * arguments.visit(this);
2314          *
2315          * invokeConstructorMethod.call(cv);
2316          */
2317     }
2318 
2319     public void visitPropertyExpression(PropertyExpression expression) {
2320 
2321         do {
2322             if (true && ENABLE_EARLY_BINDING) {
2323                 expression.resolve(this);
2324 
2325                 if (!expression.isTypeResolved()) {
2326                     break;
2327                 }
2328                 Expression ownerExp = expression.getObjectExpression();
2329                 String propName = expression.getProperty();
2330                 if (expression.getProperty().equals("class")) {
2331                     break; // the default does the right thing. let it do.
2332                 }
2333 
2334 
2335                 String ownerType = ownerExp.getType();
2336                 Class ownerClass = ownerExp.getTypeClass();
2337                 if (ownerType == null || ownerType.length() == 0) {
2338                     break;
2339                 }
2340 
2341                 Label l3 = new Label();
2342                 // handle arraylength
2343                 if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) {
2344                     load(ownerExp);
2345                     if (expression.isSafe()) {
2346                         helper.dup();
2347                         cv.visitJumpInsn(IFNULL, l3);
2348                     }
2349                     cast(ownerClass);
2350                     cv.visitInsn(ARRAYLENGTH);
2351                     helper.quickBoxIfNecessary(int.class);
2352                     cv.visitLabel(l3);
2353                     return;
2354                 }
2355 
2356 
2357                 String propertyType = expression.getType();
2358                 if (propertyType == null || propertyType.length() == 0) {
2359                     break;
2360                 }
2361                 boolean isStatic = expression.isStatic();
2362                 if (!isThisExpression(ownerExp) && GroovyObject.class.isAssignableFrom(ownerExp.getTypeClass())) {
2363                     // call other groovy object property via getProperty()/setProperty()
2364                     if (!isStatic && ownerExp instanceof ClassExpression) {
2365                         if (leftHandExpression) {
2366                             cv.visitMethodInsn(
2367                                     INVOKEVIRTUAL,
2368                                     BytecodeHelper.getClassInternalName(ownerType),
2369                                     "setProperty",
2370                                     BytecodeHelper.getTypeDescription(propertyType));
2371                         } else {
2372                             cv.visitMethodInsn(
2373                                     INVOKEVIRTUAL,
2374                                     BytecodeHelper.getClassInternalName(ownerType),
2375                                     "getProperty",
2376                                     BytecodeHelper.getTypeDescription(propertyType));
2377                         }
2378                         return;
2379                     } else {
2380                         break;
2381                     }
2382                 }
2383 //                else if (isThisExpression(ownerExp)){
2384 //                    if (leftHandExpression) {
2385 //                        helper.loadThis();
2386 //                        cv.visitFieldInsn(
2387 //                                PUTFIELD,
2388 //                                BytecodeHelper.getClassInternalName(ownerType),
2389 //                                expression.getProperty(),
2390 //                                BytecodeHelper.getClassInternalName(propertyType));
2391 //                    } else {
2392 //                        cv.visitMethodInsn(
2393 //                                INVOKEVIRTUAL,
2394 //                                BytecodeHelper.getClassInternalName(ownerType),
2395 //                                "getProperty",
2396 //                                BytecodeHelper.getClassInternalName(propertyType));
2397 //                    }
2398 //                    return;
2399 //                }
2400 
2401                 // the following logic is used for this.<prop> acess too.
2402                 else  { // none direct local access
2403                     Field fld = expression.getField();
2404                     Method setter = expression.getSetter();
2405                     Method getter = expression.getGetter();
2406 
2407                     // gate keeping
2408                     if (leftHandExpression) {
2409                         if (fld == null && setter == null) {
2410                             break;
2411                         }
2412                     }
2413                     else {
2414                         if (fld == null && getter == null) {
2415                             break;
2416                         }
2417                     }
2418 
2419                     if (ownerClass == null && !isThisExpression(ownerExp)) {
2420                         break;  // ownerClass is null only when the ownerExp is "this"
2421                     }
2422                     // now looking for public fields before accessors
2423 
2424 
2425                     if (expression.isStatic()) {
2426                         if (leftHandExpression) {
2427                             if (fld != null) {
2428                                 helper.quickUnboxIfNecessary(expression.getTypeClass());
2429                                 cv.visitFieldInsn(
2430                                         PUTSTATIC,
2431                                         BytecodeHelper.getClassInternalName(ownerType),
2432                                         expression.getProperty(),
2433                                         BytecodeHelper.getTypeDescription(propertyType)
2434                                 );
2435                             }
2436                             else if (setter != null) {
2437                                 helper.quickUnboxIfNecessary(setter.getParameterTypes()[0]);
2438                                 cast(setter.getParameterTypes()[0]);
2439                                 helper.invoke(setter);
2440                             }
2441                             else {
2442                                 throwException("no method or field is found for a resolved property access");
2443                             }
2444                         }
2445                         else { // get the property
2446                             if (fld != null){
2447                                 cv.visitFieldInsn(
2448                                         GETSTATIC,
2449                                         BytecodeHelper.getClassInternalName(ownerType),
2450                                         propName,
2451                                         BytecodeHelper.getTypeDescription(propertyType)
2452                                 );
2453                                 helper.quickBoxIfNecessary(expression.getTypeClass());
2454                             }
2455                             else if (getter != null) {
2456                                 helper.invoke(getter);
2457                                 helper.quickBoxIfNecessary(expression.getTypeClass());
2458                             }
2459                             else {
2460                                 throwException("no method or field is found for a resolved property access");
2461                             }
2462                         }
2463                     } else { // non-static access
2464                         if (leftHandExpression) { // set the property
2465                               // assumption: the data on the stack are boxed if it's a number
2466                             helper.quickUnboxIfNecessary(expression.getTypeClass());
2467                             load(ownerExp);
2468                             if (expression.isSafe()) {
2469                                 helper.dup();
2470                                 cv.visitJumpInsn(IFNULL, l3);
2471                             }
2472 
2473                             if (ownerClass != null)
2474                                 cast(ownerClass);
2475                             Class cls = expression.getTypeClass();
2476                             if (cls == double.class || cls == long.class) {
2477                                 cv.visitInsn(DUP_X2);
2478                                 cv.visitInsn(POP);
2479                             } else {
2480                                 cv.visitInsn(SWAP);
2481                             }
2482 
2483                             if (fld != null) {
2484                                 cv.visitFieldInsn(
2485                                         PUTFIELD,
2486                                         BytecodeHelper.getClassInternalName(ownerType),
2487                                         propName,
2488                                         BytecodeHelper.getTypeDescription(propertyType)
2489                                 );
2490                             }
2491                             else if (setter != null) {
2492                                 Method m = setter;
2493                                 Class[] paramTypes = m.getParameterTypes();
2494                                 if (paramTypes.length != 1) {
2495                                     throw new RuntimeException("setter should take a single parameter");
2496                                 }
2497                                 Class paramType = paramTypes[0];
2498                                 cast(paramType);
2499                                 helper.invoke(setter);
2500                             }
2501                             else {
2502                                 throwException("no method or field is found for a resolved property access");
2503                             }
2504                         }
2505                         else { // get property
2506                             load(ownerExp);
2507                             if (expression.isSafe()) {
2508                                 helper.dup();
2509                                 cv.visitJumpInsn(IFNULL, l3);
2510                             }
2511                             if (ownerClass != null)
2512                                 cast(ownerClass);
2513                             if (fld != null) {
2514                                 cv.visitFieldInsn(
2515                                         GETFIELD,
2516                                         BytecodeHelper.getClassInternalName(ownerType),
2517                                         propName,
2518                                         BytecodeHelper.getTypeDescription(propertyType)
2519                                 );
2520                                 helper.quickBoxIfNecessary(expression.getTypeClass());
2521                             }
2522                             else if (getter != null) {
2523                                 helper.invoke(getter);
2524                                 helper.quickBoxIfNecessary(expression.getTypeClass());
2525                             }
2526                             else {
2527                                 throwException("no method or field is found for a resolved property access");
2528                             }
2529                         }
2530                     }
2531                     cv.visitLabel(l3);
2532                     return;
2533                 }
2534             }
2535         } while (false);
2536 
2537         // lets check if we're a fully qualified class name
2538         String className = null;
2539         Expression objectExpression = expression.getObjectExpression();
2540         if (!isThisExpression(objectExpression)) {
2541             className = checkForQualifiedClass(expression);
2542             if (className != null) {
2543                 visitClassExpression(new ClassExpression(className));
2544                 return;
2545             }
2546         }
2547         if (expression.getProperty().equals("class")) {
2548             if ((objectExpression instanceof ClassExpression)) {
2549                 visitClassExpression((ClassExpression) objectExpression);
2550                 return;
2551             }
2552             else if (objectExpression instanceof VariableExpression) {
2553                 VariableExpression varExp = (VariableExpression) objectExpression;
2554                 className = varExp.getVariable();
2555                 try {
2556                     className = resolveClassName(className);
2557                     visitClassExpression(new ClassExpression(className));
2558                     return;
2559                 }
2560                 catch (Exception e) {
2561                     // ignore
2562                 }
2563             }
2564         }
2565 
2566         if (isThisExpression(objectExpression)) {
2567             // lets use the field expression if its available
2568             String name = expression.getProperty();
2569             FieldNode field = classNode.getField(name);
2570             if (field != null) {
2571                 visitFieldExpression(new FieldExpression(field));
2572                 return;
2573             }
2574         }
2575 
2576         // we need to clear the LHS flag to avoid "this." evaluating as ASTORE
2577         // rather than ALOAD
2578         boolean left = leftHandExpression;
2579         leftHandExpression = false;
2580         objectExpression.visit(this);
2581         leftHandExpression = left;
2582 
2583         cv.visitLdcInsn(expression.getProperty());
2584 
2585         if (isGroovyObject(objectExpression) && ! expression.isSafe()) {
2586             if (left) {
2587                 setGroovyObjectPropertyMethod.call(cv);
2588             }
2589             else {
2590                 getGroovyObjectPropertyMethod.call(cv);
2591             }
2592         }
2593         else {
2594             if (expression.isSafe()) {
2595                 if (left) {
2596                     setPropertySafeMethod2.call(cv);
2597                 }
2598                 else {
2599                     getPropertySafeMethod.call(cv);
2600                 }
2601             }
2602             else {
2603                 if (left) {
2604                     setPropertyMethod2.call(cv);
2605                 }
2606                 else {
2607                     getPropertyMethod.call(cv);
2608                 }
2609             }
2610         }
2611     }
2612 
2613     public void visitAttributeExpression(AttributeExpression expression) {
2614         Expression objectExpression = expression.getObjectExpression();
2615         if (isThisExpression(objectExpression)) {
2616             // lets use the field expression if its available
2617             String name = expression.getProperty();
2618             FieldNode field = classNode.getField(name);
2619             if (field != null) {
2620                 visitFieldExpression(new FieldExpression(field));
2621                 return;
2622             }
2623         }
2624 
2625         // we need to clear the LHS flag to avoid "this." evaluating as ASTORE
2626         // rather than ALOAD
2627         boolean left = leftHandExpression;
2628         leftHandExpression = false;
2629         objectExpression.visit(this);
2630         leftHandExpression = left;
2631 
2632         cv.visitLdcInsn(expression.getProperty());
2633 
2634         if (expression.isSafe()) {
2635             if (left) {
2636                 setAttributeSafeMethod2.call(cv);
2637             }
2638             else {
2639                 getAttributeSafeMethod.call(cv);
2640             }
2641         }
2642         else {
2643             if (left) {
2644                 setAttributeMethod2.call(cv);
2645             }
2646             else {
2647                 getAttributeMethod.call(cv);
2648             }
2649         }
2650     }
2651 
2652     protected boolean isGroovyObject(Expression objectExpression) {
2653         return isThisExpression(objectExpression);
2654     }
2655 
2656     /***
2657      * Checks if the given property expression represents a fully qualified class name
2658      * @return the class name or null if the property is not a valid class name
2659      */
2660     protected String checkForQualifiedClass(PropertyExpression expression) {
2661         String text = expression.getText();
2662         if (text != null && text.endsWith(".class")) {
2663             text = text.substring(0, text.length() - 6);
2664         }
2665         try {
2666             return resolveClassName(text);
2667         }
2668         catch (Exception e) {
2669             return null;
2670         }
2671     }
2672 
2673     public void visitFieldExpression(FieldExpression expression) {
2674         FieldNode field = expression.getField();
2675 
2676 
2677 	    if (field.isStatic()) {
2678         	if (leftHandExpression) {
2679         		storeStaticField(expression);
2680         	}
2681         	else {
2682         		loadStaticField(expression);
2683         	}
2684         } else {
2685         	if (leftHandExpression) {
2686         		storeThisInstanceField(expression);
2687         	}
2688         	else {
2689         		loadInstanceField(expression);
2690         	}
2691 		}
2692     }
2693 
2694     /***
2695      *
2696      * @param fldExp
2697      */
2698     public void loadStaticField(FieldExpression fldExp) {
2699         FieldNode field = fldExp.getField();
2700         boolean holder = field.isHolder() && !isInClosureConstructor();
2701         String type = field.getType();
2702 
2703         String ownerName = (field.getOwner().equals(classNode.getName()))
2704                 ? internalClassName
2705                 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2706         if (holder) {
2707             cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
2708             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
2709         }
2710         else {
2711             cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
2712             if (BytecodeHelper.isPrimitiveType(type)) {
2713                 helper.box(type);
2714 			} else {
2715 			}
2716         }
2717     }
2718 
2719 	/***
2720 	 * RHS instance field. should move most of the code in the BytecodeHelper
2721 	 * @param fldExp
2722 	 */
2723     public void loadInstanceField(FieldExpression fldExp) {
2724     	FieldNode field = fldExp.getField();
2725         boolean holder = field.isHolder() && !isInClosureConstructor();
2726         String type = field.getType();
2727         String ownerName = (field.getOwner().equals(classNode.getName()))
2728 				? internalClassName
2729 				: org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2730 
2731         cv.visitVarInsn(ALOAD, 0);
2732 		cv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
2733 
2734 		if (holder) {
2735 			cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
2736 		} else {
2737 			if (BytecodeHelper.isPrimitiveType(type)) {
2738 				helper.box(type);
2739 			} else {
2740 			}
2741 		}
2742     }
2743 
2744     public void storeThisInstanceField(FieldExpression expression) {
2745         FieldNode field = expression.getField();
2746 
2747         boolean holder = field.isHolder() && !isInClosureConstructor();
2748         String type = field.getType();
2749 
2750         String ownerName =  (field.getOwner().equals(classNode.getName())) ?
2751         		internalClassName : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2752         if (holder) {
2753             Variable tv = visitASTOREInTemp(field.getName());
2754             int tempIndex = tv.getIndex();
2755             cv.visitVarInsn(ALOAD, 0);
2756             cv.visitFieldInsn(GETFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2757             cv.visitVarInsn(ALOAD, tempIndex);
2758             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
2759             removeVar(tv);
2760         }
2761         else {
2762             if (isInClosureConstructor()) {
2763                 helper.doCast(type);
2764             }
2765             else {
2766                 if (ENABLE_EARLY_BINDING) {
2767                     helper.doCast(type);
2768                 }
2769                 else {
2770                     // this may be superfluous
2771                     doConvertAndCast(type);
2772                 }
2773             }
2774             //Variable tmpVar = defineVariable(createVariableName(field.getName()), "java.lang.Object", false);
2775             Variable tmpVar = defineVariable(createVariableName(field.getName()), field.getType(), false);
2776             //int tempIndex = tmpVar.getIndex();
2777             //helper.store(field.getType(), tempIndex);
2778             helper.store(tmpVar, MARK_START);
2779             helper.loadThis(); //cv.visitVarInsn(ALOAD, 0);
2780             helper.load(tmpVar);
2781             helper.putField(field, ownerName);
2782             //cv.visitFieldInsn(PUTFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2783             // let's remove the temp var
2784             removeVar(tmpVar);
2785         }
2786     }
2787 
2788 
2789     public void storeStaticField(FieldExpression expression) {
2790     	FieldNode field = expression.getField();
2791 
2792         boolean holder = field.isHolder() && !isInClosureConstructor();
2793 
2794         String type = field.getType();
2795 
2796         String ownerName = (field.getOwner().equals(classNode.getName()))
2797                 ? internalClassName
2798                 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2799         if (holder) {
2800             Variable tv = visitASTOREInTemp(field.getName());
2801             int tempIndex = tv.getIndex();
2802             cv.visitFieldInsn(GETSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2803             cv.visitVarInsn(ALOAD, tempIndex);
2804             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
2805             removeVar(tv);
2806         }
2807         else {
2808             if (isInClosureConstructor()) {
2809                 helper.doCast(type);
2810             }
2811             else {
2812                 if (ENABLE_EARLY_BINDING) {
2813                     helper.doCast(type);
2814                 }
2815                 else {
2816                     // this may be superfluous
2817                     //doConvertAndCast(type);
2818                     // use weaker cast
2819                     helper.doCast(type);
2820                 }
2821             }
2822             cv.visitFieldInsn(PUTSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2823         }
2824     }
2825 
2826     protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first ) {
2827         FieldNode field = expression.getField();
2828         boolean isStatic = field.isStatic();
2829 
2830         Variable fieldTemp = defineVariable(createVariableName(field.getName()), "java.lang.Object", false);
2831         int valueIdx = fieldTemp.getIndex();
2832 
2833         if (leftHandExpression && first) {
2834             cv.visitVarInsn(ASTORE, valueIdx);
2835             visitVariableStartLabel(fieldTemp);
2836         }
2837 
2838         if (steps > 1 || !isStatic) {
2839             cv.visitVarInsn(ALOAD, 0);
2840             cv.visitFieldInsn(
2841                 GETFIELD,
2842                 internalClassName,
2843                 "owner",
2844                 BytecodeHelper.getTypeDescription(outerClassNode.getName()));
2845         }
2846 
2847         if( steps == 1 ) {
2848             int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
2849             String ownerName = BytecodeHelper.getClassInternalName(outerClassNode.getName());
2850 
2851             if (leftHandExpression) {
2852                 cv.visitVarInsn(ALOAD, valueIdx);
2853                 boolean holder = field.isHolder() && !isInClosureConstructor();
2854                 if ( !holder) {
2855                     doConvertAndCast(field.getType());
2856                 }
2857             }
2858             cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType()));
2859             if (!leftHandExpression) {
2860                 if (BytecodeHelper.isPrimitiveType(field.getType())) {
2861                     helper.box(field.getType());
2862                 }
2863             }
2864         }
2865 
2866         else {
2867             visitOuterFieldExpression( expression, outerClassNode.getOuterClass(), steps - 1, false );
2868         }
2869     }
2870 
2871 
2872 
2873     /***
2874      *  Visits a bare (unqualified) variable expression.
2875      */
2876 
2877     public void visitVariableExpression(VariableExpression expression) {
2878 
2879         String variableName = expression.getVariable();
2880 
2881       //-----------------------------------------------------------------------
2882       // SPECIAL CASES
2883 
2884         //
2885         // "this" for static methods is the Class instance
2886 
2887         if (isStaticMethod() && variableName.equals("this")) {
2888             visitClassExpression(new ClassExpression(classNode.getName()));
2889             return;                                               // <<< FLOW CONTROL <<<<<<<<<
2890         }
2891 
2892         //
2893         // "super" also requires special handling
2894 
2895         if (variableName.equals("super")) {
2896             visitClassExpression(new ClassExpression(classNode.getSuperClass()));
2897             return;                                               // <<< FLOW CONTROL <<<<<<<<<
2898         }
2899 
2900 
2901         //
2902         // class names return a Class instance, too
2903 
2904 //        if (!variableName.equals("this")) {
2905 //            String className = resolveClassName(variableName);
2906 //            if (className != null) {
2907 //                if (leftHandExpression) {
2908 //                    throw new RuntimeParserException(
2909 //                        "Cannot use a class expression on the left hand side of an assignment",
2910 //                        expression);
2911 //                }
2912 //                visitClassExpression(new ClassExpression(className));
2913 //                return;                                               // <<< FLOW CONTROL <<<<<<<<<
2914 //            }
2915 //        }
2916 
2917 
2918       //-----------------------------------------------------------------------
2919       // GENERAL VARIABLE LOOKUP
2920 
2921 
2922         //
2923         // We are handling only unqualified variables here.  Therefore,
2924         // we do not care about accessors, because local access doesn't
2925         // go through them.  Therefore, precedence is as follows:
2926         //   1) local variables, nearest block first
2927         //   2) class fields
2928         //   3) repeat search from 2) in next outer class
2929 
2930         boolean  handled  = false;
2931         Variable variable = (Variable)variableStack.get( variableName );
2932 
2933         if( variable != null ) {
2934 
2935             if( variable.isProperty() ) {
2936                 processPropertyVariable(variable );
2937             }
2938             else {
2939                 if (ENABLE_EARLY_BINDING && expression.isTypeResolved() && leftHandExpression) {
2940                     // let's pass the type back to the variable
2941                     String typeName = expression.getType();
2942                     Type varOldType = variable.getType();
2943                     if (varOldType.isDynamic()) {
2944                         variable.setType(new Type(typeName, true));
2945                     }
2946                     else if (!varOldType.getName().equals(typeName)){
2947                         new GroovyRuntimeException("VariableExpression data type conflicts with the existing variable. "
2948                                 + "[" + expression.getLineNumber() + ":" + expression.getColumnNumber() + "]");
2949                     }
2950                 }
2951                 processStackVariable(variable );
2952             }
2953 
2954             handled = true;
2955         } else {
2956             //
2957             // Loop through outer classes for fields
2958 
2959             int       steps   = 0;
2960             ClassNode currentClassNode = classNode;
2961             FieldNode field   = null;
2962 
2963             do {
2964                 if( (field = currentClassNode.getField(variableName)) != null ) {
2965                     if (methodNode == null || !methodNode.isStatic() || field.isStatic() )
2966                         break; //this is a match. break out. todo to be tested
2967                 }
2968                 steps++;
2969 
2970             } while( (currentClassNode = currentClassNode.getOuterClass()) != null );
2971 
2972             if( field != null ) {
2973                 processFieldAccess( variableName, field, steps );
2974                 handled = true;
2975             }
2976         }
2977 
2978         //
2979         // class names return a Class instance, too
2980         if (!handled  && !variableName.equals("this")) {
2981             String className = resolveClassName(variableName);
2982             if (className != null) {
2983                 if (leftHandExpression) {
2984                     throwException("The variable name '"+variableName+"' conflicts with the class name '"+className+"'. Please use another variable name");
2985                 }
2986                 visitClassExpression(new ClassExpression(className));
2987                 return;                                               // <<< FLOW CONTROL <<<<<<<<<
2988             }
2989         }
2990 
2991         //
2992         // Finally, if unhandled, create a variable for it.
2993         // Except there a stack variable should be created,
2994         // we define the variable as a property accessor and
2995         // let other parts of the classgen report the error
2996         // if the property doesn't exist.
2997 
2998         if( !handled ) {
2999             String variableType = expression.getType();
3000             variable = defineVariable( variableName, variableType );
3001 
3002             if (leftHandExpression && expression.isDynamic()) {
3003                 variable.setDynamic(true); // false  by default
3004             }
3005             else {
3006                 variable.setDynamic(false);
3007             }
3008 
3009             if( isInScriptBody() || !leftHandExpression ) { // todo problematic: if on right hand not defined, should I report undefined var error?
3010                 variable.setProperty( true );
3011                 processPropertyVariable(variable );
3012             }
3013             else {
3014                 processStackVariable(variable );
3015             }
3016         }
3017     }
3018 
3019 
3020     protected void processStackVariable(Variable variable ) {
3021         boolean holder = variable.isHolder() && !passingClosureParams;
3022 
3023         if( leftHandExpression ) {
3024             helper.storeVar(variable, holder);
3025         }
3026         else {
3027         	helper.loadVar(variable, holder);
3028         }
3029         if (ASM_DEBUG) {
3030             helper.mark("var: " + variable.getName());
3031         }
3032     }
3033 
3034     private void visitVariableStartLabel(Variable variable) {
3035         if (CREATE_DEBUG_INFO) {
3036             Label l = variable.getStartLabel();
3037             if (l != null) {
3038                 cv.visitLabel(l);
3039             } else {
3040                 System.out.println("start label == null! what to do about this?");
3041             }
3042         }
3043     }
3044 
3045     protected void processPropertyVariable(Variable variable ) {
3046     	String name = variable.getName();
3047         if (variable.isHolder() && passingClosureParams && isInScriptBody() ) {
3048             // lets create a ScriptReference to pass into the closure
3049             cv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference");
3050             cv.visitInsn(DUP);
3051 
3052             loadThisOrOwner();
3053             cv.visitLdcInsn(name);
3054 
3055             cv.visitMethodInsn(
3056                 INVOKESPECIAL,
3057                 "org/codehaus/groovy/runtime/ScriptReference",
3058                 "<init>",
3059                 "(Lgroovy/lang/Script;Ljava/lang/String;)V");
3060         }
3061         else {
3062             visitPropertyExpression(new PropertyExpression(VariableExpression.THIS_EXPRESSION, name));
3063         }
3064     }
3065 
3066 
3067     protected void processFieldAccess( String name, FieldNode field, int steps ) {
3068         FieldExpression expression = new FieldExpression(field);
3069 
3070         if( steps == 0 ) {
3071             visitFieldExpression( expression );
3072         }
3073         else {
3074             visitOuterFieldExpression( expression, classNode.getOuterClass(), steps, true );
3075         }
3076     }
3077 
3078 
3079 
3080     /***
3081      * @return true if we are in a script body, where all variables declared are no longer
3082      * local variables but are properties
3083      */
3084     protected boolean isInScriptBody() {
3085         if (classNode.isScriptBody()) {
3086             return true;
3087         }
3088         else {
3089             return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
3090         }
3091     }
3092 
3093     /***
3094      * @return true if this expression will have left a value on the stack
3095      * that must be popped
3096      */
3097     protected boolean isPopRequired(Expression expression) {
3098         if (expression instanceof MethodCallExpression) {
3099             if (expression.getType() != null && expression.getType().equals("void")) { // nothing on the stack
3100                 return false;
3101             } else {
3102                 return !MethodCallExpression.isSuperMethodCall((MethodCallExpression) expression);
3103             }
3104         }
3105         if (expression instanceof BinaryExpression) {
3106             BinaryExpression binExp = (BinaryExpression) expression;
3107             switch (binExp.getOperation().getType()) {   // br todo should leave a copy of the value on the stack for all the assignemnt.
3108 //                case Types.EQUAL :   // br a copy of the right value is left on the stack (see evaluateEqual()) so a pop is required for a standalone assignment
3109 //                case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAsignment()
3110 //                case Types.MINUS_EQUAL :
3111 //                case Types.MULTIPLY_EQUAL :
3112 //                case Types.DIVIDE_EQUAL :
3113 //                case Types.INTDIV_EQUAL :
3114 //                case Types.MOD_EQUAL :
3115 //                    return false;
3116             }
3117         }
3118         return true;
3119     }
3120 
3121     protected boolean firstStatementIsSuperInit(Statement code) {
3122         ExpressionStatement expStmt = null;
3123         if (code instanceof ExpressionStatement) {
3124             expStmt = (ExpressionStatement) code;
3125         }
3126         else if (code instanceof BlockStatement) {
3127             BlockStatement block = (BlockStatement) code;
3128             if (!block.getStatements().isEmpty()) {
3129                 Object expr = block.getStatements().get(0);
3130                 if (expr instanceof ExpressionStatement) {
3131                     expStmt = (ExpressionStatement) expr;
3132                 }
3133             }
3134         }
3135         if (expStmt != null) {
3136             Expression expr = expStmt.getExpression();
3137             if (expr instanceof MethodCallExpression) {
3138             	MethodCallExpression call = (MethodCallExpression) expr;
3139                 if (MethodCallExpression.isSuperMethodCall(call)) {
3140                     // not sure which one is constantly used as the super class ctor call. To cover both for now
3141                 	return call.getMethod().equals("<init>") || call.getMethod().equals("super");
3142                 }
3143             }
3144         }
3145         return false;
3146     }
3147 
3148     protected void createSyntheticStaticFields() {
3149         for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
3150             String staticFieldName = (String) iter.next();
3151             // generate a field node
3152             cw.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null);
3153         }
3154 
3155         if (!syntheticStaticFields.isEmpty()) {
3156             cv =
3157                 cw.visitMethod(
3158                     ACC_STATIC + ACC_SYNTHETIC,
3159                     "class$",
3160                     "(Ljava/lang/String;)Ljava/lang/Class;",
3161                     null,
3162                     null);
3163             helper = new BytecodeHelper(cv);
3164 
3165             Label l0 = new Label();
3166             cv.visitLabel(l0);
3167             cv.visitVarInsn(ALOAD, 0);
3168             cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
3169             Label l1 = new Label();
3170             cv.visitLabel(l1);
3171             cv.visitInsn(ARETURN);
3172             Label l2 = new Label();
3173             cv.visitLabel(l2);
3174             cv.visitVarInsn(ASTORE, 1);
3175             cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
3176             cv.visitInsn(DUP);
3177             cv.visitVarInsn(ALOAD, 1);
3178             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;");
3179             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V");
3180             cv.visitInsn(ATHROW);
3181             cv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
3182             cv.visitMaxs(3, 2);
3183 
3184             cw.visitEnd();
3185         }
3186     }
3187     /*** load class object on stack */
3188     public void visitClassExpression(ClassExpression expression) {
3189         String type = expression.getText();
3190         //type = checkValidType(type, expression, "Must be a valid type name for a constructor call");
3191 
3192 
3193         if (BytecodeHelper.isPrimitiveType(type)) {
3194             String objectType = BytecodeHelper.getObjectTypeForPrimitive(type);
3195             cv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;");
3196         }
3197         else {
3198             final String staticFieldName =
3199                 (type.equals(classNode.getName())) ? "class$0" : "class$" + type.replace('.', '$').replace('[', '_').replace(';', '_');
3200 
3201             syntheticStaticFields.add(staticFieldName);
3202 
3203             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
3204             Label l0 = new Label();
3205             cv.visitJumpInsn(IFNONNULL, l0);
3206             cv.visitLdcInsn(type);
3207             cv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
3208             cv.visitInsn(DUP);
3209             cv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
3210             Label l1 = new Label();
3211             cv.visitJumpInsn(GOTO, l1);
3212             cv.visitLabel(l0);
3213             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
3214             cv.visitLabel(l1);
3215         }
3216     }
3217 
3218     public void visitRangeExpression(RangeExpression expression) {
3219         leftHandExpression = false;
3220         expression.getFrom().visit(this);
3221 
3222         leftHandExpression = false;
3223         expression.getTo().visit(this);
3224 
3225         helper.pushConstant(expression.isInclusive());
3226 
3227         createRangeMethod.call(cv);
3228     }
3229 
3230     public void visitMapEntryExpression(MapEntryExpression expression) {
3231     }
3232 
3233     public void visitMapExpression(MapExpression expression) {
3234         List entries = expression.getMapEntryExpressions();
3235         int size = entries.size();
3236         helper.pushConstant(size * 2);
3237 
3238         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3239 
3240         int i = 0;
3241         for (Iterator iter = entries.iterator(); iter.hasNext();) {
3242             Object object = iter.next();
3243             MapEntryExpression entry = (MapEntryExpression) object;
3244 
3245             cv.visitInsn(DUP);
3246             helper.pushConstant(i++);
3247             visitAndAutoboxBoolean(entry.getKeyExpression());
3248             cv.visitInsn(AASTORE);
3249 
3250             cv.visitInsn(DUP);
3251             helper.pushConstant(i++);
3252             visitAndAutoboxBoolean(entry.getValueExpression());
3253             cv.visitInsn(AASTORE);
3254         }
3255         createMapMethod.call(cv);
3256     }
3257 
3258     public void visitTupleExpression(TupleExpression expression) {
3259         int size = expression.getExpressions().size();
3260 
3261         helper.pushConstant(size);
3262 
3263         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3264 
3265         for (int i = 0; i < size; i++) {
3266             cv.visitInsn(DUP);
3267             helper.pushConstant(i);
3268             visitAndAutoboxBoolean(expression.getExpression(i));
3269             cv.visitInsn(AASTORE);
3270         }
3271         //createTupleMethod.call(cv);
3272     }
3273 
3274     public void visitArrayExpression(ArrayExpression expression) {
3275         String type = expression.getElementType();
3276         if (type!=null && type.endsWith("[]")) type = type.substring(0,type.length()-2);
3277         String typeName = BytecodeHelper.getClassInternalName(type);        
3278         Expression sizeExpression = expression.getSizeExpression();
3279 
3280         int size=0;
3281         if (sizeExpression != null) {
3282             // lets convert to an int
3283             visitAndAutoboxBoolean(sizeExpression);
3284             asIntMethod.call(cv);
3285         } else {
3286             size = expression.getExpressions().size();
3287             helper.pushConstant(size);
3288         }
3289 
3290         int storeIns=AASTORE;
3291         if (BytecodeHelper.isPrimitiveType(type)) {
3292             int primType=0;
3293             if (type.equals("boolean")) {
3294                 primType = T_BOOLEAN;
3295                 storeIns = BASTORE;
3296             } else if (type.equals("char")) {
3297                 primType = T_CHAR;
3298                 storeIns = CASTORE;
3299             } else if (type.equals("float")) {
3300                 primType = T_FLOAT;
3301                 storeIns = FASTORE;
3302             } else if (type.equals("double")) {
3303                 primType = T_DOUBLE;
3304                 storeIns = DASTORE;
3305             } else if (type.equals("byte")) {
3306                 primType = T_BYTE;
3307                 storeIns = BASTORE;
3308             } else if (type.equals("short")) {
3309                 primType = T_SHORT;
3310                 storeIns = SASTORE;
3311             } else if (type.equals("int")) {
3312                 primType = T_INT;
3313                 storeIns=IASTORE;
3314             } else if (type.equals("long")) {
3315                 primType = T_LONG;
3316                 storeIns = LASTORE;
3317             } 
3318             cv.visitIntInsn(NEWARRAY, primType);
3319         } else {
3320             cv.visitTypeInsn(ANEWARRAY, typeName);
3321         }
3322 
3323         for (int i = 0; i < size; i++) {
3324             cv.visitInsn(DUP);
3325             helper.pushConstant(i);
3326             Expression elementExpression = expression.getExpression(i);
3327             if (elementExpression == null) {
3328                 ConstantExpression.NULL.visit(this);
3329             } else {
3330                 if (!type.equals(elementExpression.getClass().getName())) {
3331                     visitCastExpression(new CastExpression(type, elementExpression, true));
3332                 } else {
3333                     visitAndAutoboxBoolean(elementExpression);
3334                 }
3335             }
3336             cv.visitInsn(storeIns);            
3337         }
3338         
3339         if (BytecodeHelper.isPrimitiveType(type)) {
3340             int par = defineVariable("par","java.lang.Object").getIndex();
3341             cv.visitVarInsn(ASTORE, par);
3342             cv.visitVarInsn(ALOAD, par);
3343         }
3344     }
3345 
3346     public void visitListExpression(ListExpression expression) {
3347         int size = expression.getExpressions().size();
3348         helper.pushConstant(size);
3349 
3350         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3351 
3352         for (int i = 0; i < size; i++) {
3353             cv.visitInsn(DUP);
3354             helper.pushConstant(i);
3355             visitAndAutoboxBoolean(expression.getExpression(i));
3356             cv.visitInsn(AASTORE);
3357         }
3358         createListMethod.call(cv);
3359     }
3360 
3361     public void visitGStringExpression(GStringExpression expression) {
3362         int size = expression.getValues().size();
3363         helper.pushConstant(size);
3364 
3365         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3366 
3367         for (int i = 0; i < size; i++) {
3368             cv.visitInsn(DUP);
3369             helper.pushConstant(i);
3370             visitAndAutoboxBoolean(expression.getValue(i));
3371             cv.visitInsn(AASTORE);
3372         }
3373 
3374         Variable tv = visitASTOREInTemp("iterator");
3375         int paramIdx = tv.getIndex();
3376 
3377         ClassNode innerClass = createGStringClass(expression);
3378         addInnerClass(innerClass);
3379         String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName());
3380 
3381         cv.visitTypeInsn(NEW, innerClassinternalName);
3382         cv.visitInsn(DUP);
3383         cv.visitVarInsn(ALOAD, paramIdx);
3384 
3385         cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", "([Ljava/lang/Object;)V");
3386         removeVar(tv);
3387     }
3388 
3389     private Variable visitASTOREInTemp(String s) {
3390         return storeInTemp(s, "java.lang.Object");
3391     }
3392 
3393     // Implementation methods
3394     //-------------------------------------------------------------------------
3395     protected boolean addInnerClass(ClassNode innerClass) {
3396         innerClass.setModule(classNode.getModule());
3397         return innerClasses.add(innerClass);
3398     }
3399 
3400     protected ClassNode createClosureClass(ClosureExpression expression) {
3401         ClassNode owner = getOutermostClass();
3402         boolean parentIsInnerClass = owner instanceof InnerClassNode;
3403         String outerClassName = owner.getName();
3404         String name = outerClassName + "$"
3405                 + context.getNextClosureInnerName(owner, classNode, methodNode); // br added a more infomative name
3406         boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass();
3407         if (staticMethodOrInStaticClass) {
3408             outerClassName = "java.lang.Class";
3409         }
3410         Parameter[] parameters = expression.getParameters();
3411         if (parameters == null || parameters.length == 0) {
3412             // lets create a default 'it' parameter
3413             parameters = new Parameter[] { new Parameter("it")};
3414         }
3415 
3416         Parameter[] localVariableParams = getClosureSharedVariables(expression);
3417 
3418         InnerClassNode answer = new InnerClassNode(owner, name, 0, "groovy.lang.Closure"); // clsures are local inners and not public
3419         answer.setEnclosingMethod(this.methodNode);
3420         if (staticMethodOrInStaticClass) {
3421             answer.setStaticClass(true);
3422         }
3423         if (isInScriptBody()) {
3424             answer.setScriptBody(true);
3425         }
3426         MethodNode method =
3427             answer.addMethod("doCall", ACC_PUBLIC, "java.lang.Object", parameters, expression.getCode());
3428 
3429         method.setLineNumber(expression.getLineNumber());
3430         method.setColumnNumber(expression.getColumnNumber());
3431 
3432         VariableScope varScope = expression.getVariableScope();
3433         if (varScope == null) {
3434             throw new RuntimeException(
3435                 "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
3436         }
3437         else {
3438             method.setVariableScope(varScope);
3439         }
3440         if (parameters.length > 1
3441             || (parameters.length == 1
3442                 && parameters[0].getType() != null
3443                 && !parameters[0].getType().equals("java.lang.Object"))) {
3444 
3445             // lets add a typesafe call method
3446             answer.addMethod(
3447                 "call",
3448                 ACC_PUBLIC,
3449                 "java.lang.Object",
3450                 parameters,
3451                 new ReturnStatement(
3452                     new MethodCallExpression(
3453                         VariableExpression.THIS_EXPRESSION,
3454                         "doCall",
3455                         new ArgumentListExpression(parameters))));
3456         }
3457 
3458         FieldNode ownerField = answer.addField("owner", ACC_PRIVATE, outerClassName, null);
3459 
3460         // lets make the constructor
3461         BlockStatement block = new BlockStatement();
3462         block.addStatement(
3463             new ExpressionStatement(
3464                 new MethodCallExpression(
3465                     new VariableExpression("super"),
3466                     "<init>",
3467                     new VariableExpression("_outerInstance"))));
3468         block.addStatement(
3469             new ExpressionStatement(
3470                 new BinaryExpression(
3471                     new FieldExpression(ownerField),
3472                     Token.newSymbol(Types.EQUAL, -1, -1),
3473                     new VariableExpression("_outerInstance"))));
3474 
3475         // lets assign all the parameter fields from the outer context
3476         for (int i = 0; i < localVariableParams.length; i++) {
3477             Parameter param = localVariableParams[i];
3478             String paramName = param.getName();
3479             boolean holder = mutableVars.contains(paramName);
3480             Expression initialValue = null;
3481             String type = param.getType();
3482             FieldNode paramField = null;
3483             if (holder) {
3484             	initialValue = new VariableExpression(paramName);
3485                 type = Reference.class.getName();
3486                 param.makeReference();
3487                 paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue);
3488                 paramField.setHolder(true);
3489                 String realType = param.getRealType();
3490                 String methodName = Verifier.capitalize(paramName);
3491 
3492                 // lets add a getter & setter
3493                 Expression fieldExp = new FieldExpression(paramField);
3494                 answer.addMethod(
3495                     "get" + methodName,
3496                     ACC_PUBLIC,
3497                     realType,
3498                     Parameter.EMPTY_ARRAY,
3499                     new ReturnStatement(fieldExp));
3500 
3501                 /*
3502                 answer.addMethod(
3503                     "set" + methodName,
3504                     ACC_PUBLIC,
3505                     "void",
3506                     new Parameter[] { new Parameter(realType, "__value") },
3507                     new ExpressionStatement(
3508                         new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
3509                         */
3510             }
3511             else {
3512             	PropertyNode propertyNode = answer.addProperty(paramName, ACC_PUBLIC, type, initialValue, null, null);
3513                 paramField = propertyNode.getField();
3514                 block.addStatement(
3515                     new ExpressionStatement(
3516                         new BinaryExpression(
3517                             new FieldExpression(paramField),
3518                             Token.newSymbol(Types.EQUAL, -1, -1),
3519                             new VariableExpression(paramName))));
3520             }
3521         }
3522 
3523         Parameter[] params = new Parameter[2 + localVariableParams.length];
3524         params[0] = new Parameter(outerClassName, "_outerInstance");
3525         params[1] = new Parameter("java.lang.Object", "_delegate");
3526         System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
3527 
3528         answer.addConstructor(ACC_PUBLIC, params, block);
3529         return answer;
3530     }
3531 
3532     protected ClassNode getOutermostClass() {
3533         if (outermostClass == null) {
3534             outermostClass = classNode;
3535             while (outermostClass instanceof InnerClassNode) {
3536                 outermostClass = outermostClass.getOuterClass();
3537             }
3538         }
3539         return outermostClass;
3540     }
3541 
3542     protected ClassNode createGStringClass(GStringExpression expression) {
3543         ClassNode owner = classNode;
3544         if (owner instanceof InnerClassNode) {
3545             owner = owner.getOuterClass();
3546         }
3547         String outerClassName = owner.getName();
3548         String name = outerClassName + "$" + context.getNextInnerClassIdx();
3549         InnerClassNode answer = new InnerClassNode(owner, name, 0, GString.class.getName());
3550         answer.setEnclosingMethod(this.methodNode);
3551         FieldNode stringsField =
3552             answer.addField(
3553                 "strings",
3554                 ACC_PRIVATE /*| ACC_STATIC*/,
3555                 "java.lang.String[]",
3556                 new ArrayExpression("java.lang.String", expression.getStrings()));
3557         answer.addMethod(
3558             "getStrings",
3559             ACC_PUBLIC,
3560             "java.lang.String[]",
3561             Parameter.EMPTY_ARRAY,
3562             new ReturnStatement(new FieldExpression(stringsField)));
3563         // lets make the constructor
3564         BlockStatement block = new BlockStatement();
3565         block.addStatement(
3566             new ExpressionStatement(
3567                 new MethodCallExpression(new VariableExpression("super"), "<init>", new VariableExpression("values"))));
3568         Parameter[] contructorParams = new Parameter[] { new Parameter("java.lang.Object[]", "values")};
3569         answer.addConstructor(ACC_PUBLIC, contructorParams, block);
3570         return answer;
3571     }
3572 
3573     protected void doConvertAndCast(String type) {
3574         if (!type.equals("java.lang.Object")) {
3575             /*** todo should probably support array coercions */
3576             if (!type.endsWith("[]") && isValidTypeForCast(type)) {
3577                 visitClassExpression(new ClassExpression(type));
3578                 asTypeMethod.call(cv);
3579             }
3580 
3581             helper.doCast(type);
3582         }
3583     }
3584 
3585     protected void evaluateLogicalOrExpression(BinaryExpression expression) {
3586         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
3587         Label l0 = new Label();
3588         Label l2 = new Label();
3589         cv.visitJumpInsn(IFEQ, l0);
3590 
3591         cv.visitLabel(l2);
3592 
3593         visitConstantExpression(ConstantExpression.TRUE);
3594 
3595         Label l1 = new Label();
3596         cv.visitJumpInsn(GOTO, l1);
3597         cv.visitLabel(l0);
3598 
3599         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
3600 
3601         cv.visitJumpInsn(IFNE, l2);
3602 
3603         visitConstantExpression(ConstantExpression.FALSE);
3604         cv.visitLabel(l1);
3605     }
3606 
3607     // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for
3608     // consistancy.
3609     protected void evaluateLogicalAndExpression(BinaryExpression expression) {
3610         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
3611         Label l0 = new Label();
3612         cv.visitJumpInsn(IFEQ, l0);
3613 
3614         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
3615 
3616         cv.visitJumpInsn(IFEQ, l0);
3617 
3618         visitConstantExpression(ConstantExpression.TRUE);
3619 
3620         Label l1 = new Label();
3621         cv.visitJumpInsn(GOTO, l1);
3622         cv.visitLabel(l0);
3623 
3624         visitConstantExpression(ConstantExpression.FALSE);
3625 
3626         cv.visitLabel(l1);
3627     }
3628 
3629     protected void evaluateBinaryExpression(String method, BinaryExpression expression) {
3630         Expression leftExpression = expression.getLeftExpression();
3631         leftHandExpression = false;
3632         leftExpression.visit(this);
3633         cv.visitLdcInsn(method);
3634         leftHandExpression = false;
3635         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}).visit(this);
3636         // expression.getRightExpression().visit(this);
3637         invokeMethodMethod.call(cv);
3638     }
3639 
3640     protected void evaluateCompareTo(BinaryExpression expression) {
3641         Expression leftExpression = expression.getLeftExpression();
3642         leftHandExpression = false;
3643         leftExpression.visit(this);
3644         if (isComparisonExpression(leftExpression)) {
3645             helper.boxBoolean();
3646         }
3647 
3648         // if the right hand side is a boolean expression, we need to autobox
3649         Expression rightExpression = expression.getRightExpression();
3650         rightExpression.visit(this);
3651         if (isComparisonExpression(rightExpression)) {
3652             helper.boxBoolean();
3653         }
3654         compareToMethod.call(cv);
3655     }
3656 
3657     protected void evaluateBinaryExpressionWithAsignment(String method, BinaryExpression expression) {
3658         Expression leftExpression = expression.getLeftExpression();
3659         if (leftExpression instanceof BinaryExpression) {
3660             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
3661             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
3662                 // lets replace this assignment to a subscript operator with a
3663                 // method call
3664                 // e.g. x[5] += 10
3665                 // -> (x, [], 5), =, x[5] + 10
3666                 // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
3667 
3668                 MethodCallExpression methodCall =
3669                     new MethodCallExpression(
3670                         expression.getLeftExpression(),
3671                         method,
3672                         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}));
3673 
3674                 Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression());
3675 
3676                 visitMethodCallExpression(
3677                     new MethodCallExpression(
3678                         leftBinExpr.getLeftExpression(),
3679                         "putAt",
3680                         new ArgumentListExpression(new Expression[] { safeIndexExpr, methodCall })));
3681                 //cv.visitInsn(POP);
3682                 return;
3683             }
3684         }
3685 
3686         evaluateBinaryExpression(method, expression);
3687 
3688         // br to leave a copy of rvalue on the stack. see also isPopRequired()
3689         cv.visitInsn(DUP);
3690 
3691         leftHandExpression = true;
3692         evaluateExpression(leftExpression);
3693         leftHandExpression = false;
3694     }
3695 
3696     private void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression bin) {
3697         if (ENABLE_EARLY_BINDING && true) {
3698             evalBinaryExp_EarlyBinding(compareMethod, bin);
3699         }
3700         else {
3701             evalBinaryExp_LateBinding(compareMethod, bin);
3702         }
3703     }
3704 
3705     protected void evalBinaryExp_LateBinding(MethodCaller compareMethod, BinaryExpression expression) {
3706         Expression leftExp = expression.getLeftExpression();
3707         Expression rightExp = expression.getRightExpression();
3708         load(leftExp);
3709         load(rightExp);
3710         compareMethod.call(cv);
3711     }
3712 
3713     /***
3714      * note: leave the primitive boolean on staock for comparison expressions. All the result types need to match the
3715      * utility methods in the ScriptBytecodeAdapter.
3716      * @param compareMethod
3717      * @param expression
3718      */
3719     protected void evalBinaryExp_EarlyBinding(MethodCaller compareMethod, BinaryExpression expression) {
3720         Expression leftExp = expression.getLeftExpression();
3721         Expression rightExp = expression.getRightExpression();
3722 
3723         expression.resolve(this);
3724         if (expression.isResolveFailed() || expression.getTypeClass() == null){
3725             evalBinaryExp_LateBinding(compareMethod, expression);
3726             return;
3727         }
3728         else {
3729             Class lclass = leftExp.getTypeClass();
3730             Class rclass = rightExp.getTypeClass();
3731             if (lclass == null || rclass == null) {
3732                 if ((lclass == null && rclass != null) || (lclass != null && rclass == null)) {
3733                     // lets treat special cases: obj == null / obj != null . leave primitive boolean on the stack, which will be boxed by visitAndAutoBox()
3734                     if (leftExp == ConstantExpression.NULL && !rclass.isPrimitive() ||
3735                             rightExp == ConstantExpression.NULL && !lclass.isPrimitive()) {
3736                         Expression exp = leftExp == ConstantExpression.NULL? rightExp : leftExp;
3737                         int type = expression.getOperation().getType();
3738                         switch (type) {
3739                             case Types.COMPARE_EQUAL :
3740                                 load(exp);
3741                                 cv.visitInsn(ICONST_1);
3742                                 cv.visitInsn(SWAP);
3743                                 Label l1 = new Label();
3744                                 cv.visitJumpInsn(IFNULL, l1);
3745                                 cv.visitInsn(POP);
3746                                 cv.visitInsn(ICONST_0);
3747                                 cv.visitLabel(l1);
3748                                 return;
3749                             case Types.COMPARE_NOT_EQUAL :
3750                                 load(exp);
3751                                 cv.visitInsn(ICONST_1);
3752                                 cv.visitInsn(SWAP);
3753                                 Label l2 = new Label();
3754                                 cv.visitJumpInsn(IFNONNULL, l2);
3755                                 cv.visitInsn(POP);
3756                                 cv.visitInsn(ICONST_0);
3757                                 cv.visitLabel(l2);
3758                                 return;
3759                             default:
3760                                 evalBinaryExp_LateBinding(compareMethod, expression);
3761                                 return;
3762                         }
3763                     }
3764                     else {
3765                         evalBinaryExp_LateBinding(compareMethod, expression);
3766                         return;
3767                     }
3768                 }
3769                 else {
3770                     evalBinaryExp_LateBinding(compareMethod, expression);
3771                     return;
3772                 }
3773             }
3774             else if (lclass == String.class && rclass == String.class) {
3775                 int type = expression.getOperation().getType();
3776                 switch (type) {
3777                     case Types.COMPARE_EQUAL : // ==
3778                         load(leftExp); cast(String.class);
3779                         load(rightExp); cast(String.class);
3780                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z");
3781                         //helper.quickBoxIfNecessary(boolean.class);
3782                         return;
3783                     case Types.COMPARE_NOT_EQUAL :
3784                         load(leftExp);cast(String.class);
3785                         load(rightExp); cast(String.class);
3786                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z");
3787                         cv.visitInsn(ICONST_1);
3788                         cv.visitInsn(IXOR);
3789                         //helper.quickBoxIfNecessary(boolean.class);
3790                         return;
3791                     case Types.COMPARE_TO :
3792                         load(leftExp);cast(String.class);
3793                         load(rightExp); cast(String.class);
3794                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I");
3795                         helper.quickBoxIfNecessary(int.class); // object type
3796                         return;
3797                     case Types.COMPARE_GREATER_THAN :
3798                     case Types.COMPARE_GREATER_THAN_EQUAL :
3799                     case Types.COMPARE_LESS_THAN :
3800                     case Types.COMPARE_LESS_THAN_EQUAL :
3801                         {
3802                             int op;
3803                             switch (type) {
3804                                 case Types.COMPARE_GREATER_THAN :
3805                                     op = IFLE;
3806                                     break;
3807                                 case Types.COMPARE_GREATER_THAN_EQUAL :
3808                                     op = IFLT;
3809                                     break;
3810                                 case Types.COMPARE_LESS_THAN :
3811                                     op = IFGE;
3812                                     break;
3813                                 case Types.COMPARE_LESS_THAN_EQUAL :
3814                                     op = IFGT;
3815                                     break;
3816                                 default:
3817                                     System.err.println("flow control error: should not be here. type: " + type);
3818                                     return;
3819                             }
3820                             load(leftExp);cast(String.class);
3821                             load(rightExp); cast(String.class);
3822                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I");
3823 
3824                             // set true/false on stack
3825                             Label l4 = new Label();
3826                             cv.visitJumpInsn(op, l4);
3827                             // need to use primitive boolean //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
3828                             cv.visitInsn(ICONST_1);  // true
3829                             Label l5 = new Label();
3830                             cv.visitJumpInsn(GOTO, l5);
3831                             cv.visitLabel(l4);
3832                             cv.visitInsn(ICONST_0); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
3833                             cv.visitLabel(l5);
3834                         }
3835                         return;
3836 
3837                     default:
3838                         evalBinaryExp_LateBinding(compareMethod, expression);
3839                         return;
3840                 }
3841             }
3842             else if (Integer.class == lclass && Integer.class == rclass) {
3843                 int type = expression.getOperation().getType();
3844                 switch (type) {
3845                     case Types.COMPARE_EQUAL : // ==
3846                         load(leftExp); cast(Integer.class);
3847                         load(rightExp);
3848                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z");
3849                         //helper.quickBoxIfNecessary(boolean.class);
3850                         return;
3851                     case Types.COMPARE_NOT_EQUAL :
3852                         load(leftExp); cast(Integer.class);
3853                         load(rightExp);
3854                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z");
3855                         cv.visitInsn(ICONST_1);
3856                         cv.visitInsn(IXOR);
3857                         //helper.quickBoxIfNecessary(boolean.class);
3858                         return;
3859                     case Types.COMPARE_TO :
3860                         load(leftExp); cast(Integer.class);
3861                         load(rightExp);
3862                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I");
3863                         helper.quickBoxIfNecessary(int.class);
3864                         return;
3865                     case Types.COMPARE_GREATER_THAN :
3866                     case Types.COMPARE_GREATER_THAN_EQUAL :
3867                     case Types.COMPARE_LESS_THAN :
3868                     case Types.COMPARE_LESS_THAN_EQUAL :
3869                         {
3870                             int op;
3871                             switch (type) {
3872                                 case Types.COMPARE_GREATER_THAN :
3873                                     op = IFLE;
3874                                     break;
3875                                 case Types.COMPARE_GREATER_THAN_EQUAL :
3876                                     op = IFLT;
3877                                     break;
3878                                 case Types.COMPARE_LESS_THAN :
3879                                     op = IFGE;
3880                                     break;
3881                                 case Types.COMPARE_LESS_THAN_EQUAL :
3882                                     op = IFGT;
3883                                     break;
3884                                 default:
3885                                     System.err.println("flow control error: should not be here. type: " + type);
3886                                     return;
3887                             }
3888                             load(leftExp); cast(Integer.class);
3889                             load(rightExp);
3890                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I");
3891 
3892                             Label l4 = new Label();
3893                             cv.visitJumpInsn(op, l4);
3894                             cv.visitInsn(ICONST_1); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
3895                             Label l5 = new Label();
3896                             cv.visitJumpInsn(GOTO, l5);
3897                             cv.visitLabel(l4);
3898                             cv.visitInsn(ICONST_0);//cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
3899                             cv.visitLabel(l5);
3900                         }
3901                         return;
3902 
3903                     default:
3904                         evalBinaryExp_LateBinding(compareMethod, expression);
3905                         return;
3906                 }
3907             }
3908             else {
3909                 evalBinaryExp_LateBinding(compareMethod, expression);
3910                 return;
3911             }
3912         }
3913     }
3914 
3915     private void cast(Class aClass) {
3916         if (!aClass.isPrimitive() && aClass != Object.class) {
3917             cv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(aClass.getName()));
3918         }
3919     }
3920 
3921     protected void evaluateEqual(BinaryExpression expression) {
3922         if (ENABLE_EARLY_BINDING) {
3923             expression.resolve(this);
3924             if (expression.isTypeResolved()) {
3925                 if (expression.getRightExpression().getTypeClass() == Void.TYPE) {
3926                     throwException("void value appeared on right hand side of assignment. ");
3927                 }
3928             }
3929         }
3930 
3931         Expression leftExpression = expression.getLeftExpression();
3932         if (leftExpression instanceof BinaryExpression) {
3933             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
3934             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
3935                 // lets replace this assignment to a subscript operator with a
3936                 // method call
3937                 // e.g. x[5] = 10
3938                 // -> (x, [], 5), =, 10
3939                 // -> methodCall(x, "putAt", [5, 10])
3940                 do {
3941                     if (true && ENABLE_EARLY_BINDING){
3942                         Class typeclass = leftBinExpr.getLeftExpression().getTypeClass();
3943                         if (typeclass == null) {
3944                             break;
3945                         }
3946 
3947                         if (typeclass == Map.class) {// call aMap.put()
3948                             load(expression.getRightExpression());
3949                             // let's leave a copy of the value on the stack.
3950                             cv.visitInsn(DUP);
3951                             final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType());
3952                             // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType());
3953                             final Class rclass = expression.getRightExpression().getTypeClass();
3954                             BytecodeExpression loadTempByteCode = new BytecodeExpression() {
3955                                 public void visit(GroovyCodeVisitor visitor) {
3956                                     cv.visitVarInsn(ALOAD, rightTemp.getIndex());
3957                                 }
3958                                 protected void resolveType(AsmClassGenerator resolver) {
3959                                     setTypeClass(rclass);
3960                                 }
3961                             };
3962 
3963                             visitMethodCallExpression(
3964                                     new MethodCallExpression(
3965                                             leftBinExpr.getLeftExpression(),
3966                                             "put",
3967                                             new ArgumentListExpression(
3968                                                     new Expression[] {
3969                                                         leftBinExpr.getRightExpression(),
3970                                                         loadTempByteCode})));
3971                             cv.visitInsn(POP); // pop the put method return
3972                             removeVar(rightTemp);
3973                             return;
3974                         }
3975                         else if (typeclass == List.class){
3976                             // call DefaultGroovyMethods.putAt()V
3977                             // DefaultGroovyMethods.putAt(x, 5, "c"); this is faster thangoing thru metaclass
3978                             // this method does not return any value. so indicate this fact in the expression
3979 
3980                             load(expression.getRightExpression());
3981                             // let's leave a copy of the value on the stack. this is really lazy.
3982                             cv.visitInsn(DUP);
3983                             final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType());
3984                             // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType());
3985                             final Class rclass = expression.getRightExpression().getTypeClass();
3986                             BytecodeExpression loadTempBytes = new BytecodeExpression() {
3987                                 public void visit(GroovyCodeVisitor visitor) {
3988                                     cv.visitVarInsn(ALOAD, rightTemp.getIndex());
3989                                 }
3990                                 protected void resolveType(AsmClassGenerator resolver) {
3991                                     setTypeClass(rclass);
3992                                 }
3993                             };
3994 
3995                             visitMethodCallExpression(
3996                                 new MethodCallExpression(
3997                                     new ClassExpression(DefaultGroovyMethods.class),
3998                                     "putAt",
3999                                     new ArgumentListExpression(
4000                                         new Expression[] {
4001                                             leftBinExpr.getLeftExpression(),
4002                                             leftBinExpr.getRightExpression(),
4003                                             loadTempBytes })));
4004                             removeVar(rightTemp);
4005                             return;
4006 
4007                         }
4008                         else {
4009                             break;
4010                         }
4011                     }
4012                 } while (false);
4013 
4014                 visitMethodCallExpression(
4015                     new MethodCallExpression(
4016                         leftBinExpr.getLeftExpression(),
4017                         "putAt",
4018                         new ArgumentListExpression(
4019                             new Expression[] { leftBinExpr.getRightExpression(), expression.getRightExpression()})));
4020                  // cv.visitInsn(POP); //this is realted to isPopRequired()
4021                 return;
4022             }
4023         }
4024 
4025         // lets evaluate the RHS then hopefully the LHS will be a field
4026         leftHandExpression = false;
4027         Expression rightExpression = expression.getRightExpression();
4028 
4029         String type = getLHSType(leftExpression);
4030         if (type != null) {
4031             //System.out.println("### expression: " + leftExpression);
4032             //System.out.println("### type: " + type);
4033 
4034             // lets not cast for primitive types as we handle these in field setting etc
4035             if (BytecodeHelper.isPrimitiveType(type)) {
4036                 rightExpression.visit(this);
4037             }
4038             else {
4039                 if (ENABLE_EARLY_BINDING) {
4040                     if (leftExpression.isDynamic()) { // br the previous if() probably should check this too!
4041                         visitAndAutoboxBoolean(rightExpression);
4042                     }
4043                     else {
4044                         if (type.equals(rightExpression.getType())) {
4045                             visitAndAutoboxBoolean(rightExpression);
4046                         }
4047                         else {
4048                             if (rightExpression instanceof ConstantExpression &&
4049                                     ((ConstantExpression)rightExpression).getValue() == null) {
4050                                 cv.visitInsn(ACONST_NULL);
4051                             }
4052                             else {
4053                                 visitCastExpression(new CastExpression(type, rightExpression));
4054                             }
4055                         }
4056                     }
4057                 }
4058                 else if (!type.equals("java.lang.Object")){
4059                     visitCastExpression(new CastExpression(type, rightExpression));
4060                 }
4061                 else {
4062                     visitAndAutoboxBoolean(rightExpression);
4063                 }
4064             }
4065         }
4066         else {
4067             visitAndAutoboxBoolean(rightExpression);
4068         }
4069 
4070 
4071         // br: attempt to pass type info from right to left for assignment
4072         if (ENABLE_EARLY_BINDING) {
4073             Class rc = rightExpression.getTypeClass();
4074             if (rc != null && rc.isArray()) {
4075                 Class elemType = rc.getComponentType();
4076                 if (elemType.isPrimitive()) {
4077                     visitClassExpression(new ClassExpression(elemType));
4078                     convertPrimitiveArray.call(cv);
4079                     cast(loadClass(BytecodeHelper.formatNameForClassLoading(elemType.getName() + "[]")));
4080                 }
4081             }
4082 
4083 
4084             if (leftExpression.isDynamic() ) {
4085                 // propagate the type from right to left if the left is dynamic
4086                 if (!(leftExpression instanceof FieldExpression ) && !(leftExpression instanceof PropertyExpression))
4087                     copyTypeClass(leftExpression, rightExpression);
4088             }
4089             else {
4090                 Class lc = leftExpression.getTypeClass();
4091 //                Class rc = rightExpression.getTypeClass();
4092                 if (lc != null && rc != null && !lc.isAssignableFrom(rc) && !lc.isPrimitive()) {
4093                     // let's use extended conversion logic in the invoker class.
4094                     if (!lc.isArray()) {
4095                         visitClassExpression(new ClassExpression(lc));
4096                         asTypeMethod.call(cv);
4097                         helper.doCast(lc);
4098                     }
4099                     else {
4100                         // may not need this, since variable type converts primitive array to object array automatically
4101                         Class elemType = lc.getComponentType();
4102                         if (elemType.isPrimitive()) {
4103                             // let's allow type copy for primitive array, meaning [i can be changed to [Integer
4104                             copyTypeClass(leftExpression, rightExpression);
4105                         }
4106                     }
4107                 }
4108             }
4109         }
4110         cv.visitInsn(DUP);  // to leave a copy of the rightexpression value on the stack after the assignment.
4111         leftHandExpression = true;
4112         leftExpression.visit(this);
4113         leftHandExpression = false;
4114     }
4115 
4116     private void copyTypeClass(Expression leftExpression, Expression rightExpression) {
4117         // copy type class from the right to the left, boxing numbers & treat ClassExpression specially
4118         Class rclass = rightExpression.getTypeClass();
4119         if (rightExpression instanceof ClassExpression) {
4120             leftExpression.setTypeClass(Class.class);
4121         }
4122         else {
4123             rclass = BytecodeHelper.boxOnPrimitive(rclass);
4124             leftExpression.setTypeClass(rclass);
4125         }
4126     }
4127 
4128     private boolean canBeAssignedFrom(String ltype, String rtype) {
4129         if (rtype == null) {
4130             return false;
4131         }
4132         else if (ltype == null || ltype.equals("java.lang.Object")) {
4133             return true;
4134         } else {
4135             return false;
4136         }
4137     }
4138 
4139     private boolean canBeAssignedFrom(Expression l, Expression r) {
4140             if (r.getTypeClass() == null) {
4141                 return false;
4142             }
4143             else if (l.isDynamic()){
4144                 return true;
4145             } else {
4146                 return false;
4147             }
4148         }
4149     private boolean canBeAssignedFrom(Class l, Class r) {
4150             if (r == null) {
4151                 return false;
4152             }
4153             else if (l == null || l == Object.class){
4154                 return true;
4155             } else {
4156                 return false;
4157             }
4158         }
4159 
4160     /***
4161      * Deduces the type name required for some casting
4162      *
4163      * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
4164      */
4165     protected String getLHSType(Expression leftExpression) {
4166         do {
4167 // commented out. not quiteworking yet. would complain something like:
4168 //java.lang.ClassFormatError: Foo$1 (Illegal Field name "class$[Ljava$lang$String;")
4169 //
4170 //            if (ENABLE_EARLY_BINDING) {
4171 //                String type = leftExpression.getType();
4172 //                if (type == null)
4173 //                    break;
4174 //                return isValidTypeForCast(type) ? type : null;
4175 //            }
4176         } while (false);
4177 
4178         if (leftExpression instanceof VariableExpression) {
4179             VariableExpression varExp = (VariableExpression) leftExpression;
4180             String type = varExp.getType();
4181             if (isValidTypeForCast(type)) {
4182                 return type;
4183             }
4184             String variableName = varExp.getVariable();
4185             Variable variable = (Variable) variableStack.get(variableName);
4186             if (variable != null) {
4187                 if (variable.isHolder() || variable.isProperty()) {
4188                     return null;
4189                 }
4190                 type = variable.getTypeName();
4191                 if (isValidTypeForCast(type)) {
4192                     return type;
4193                 }
4194             }
4195             else {
4196                 FieldNode field = classNode.getField(variableName);
4197                 if (field == null) {
4198                     field = classNode.getOuterField(variableName);
4199                 }
4200                 if (field != null) {
4201                     type = field.getType();
4202                     if (!field.isHolder() && isValidTypeForCast(type)) {
4203                         return type;
4204                     }
4205                 }
4206             }
4207         }
4208         return null;
4209     }
4210 
4211     protected boolean isValidTypeForCast(String type) {
4212         return type != null && !type.equals("java.lang.Object") && !type.equals("groovy.lang.Reference") && !BytecodeHelper.isPrimitiveType(type);
4213     }
4214 
4215     protected void visitAndAutoboxBoolean(Expression expression) {
4216         expression.visit(this);
4217 
4218         if (isComparisonExpression(expression)) {
4219             helper.boxBoolean(); // convert boolean to Boolean
4220         }
4221     }
4222 
4223     protected void evaluatePrefixMethod(String method, Expression expression) {
4224         if (isNonStaticField(expression) && ! isHolderVariable(expression) && !isStaticMethod()) {
4225             cv.visitVarInsn(ALOAD, 0);
4226         }
4227         expression.visit(this);
4228         cv.visitLdcInsn(method);
4229         invokeNoArgumentsMethod.call(cv);
4230 
4231         leftHandExpression = true;
4232         expression.visit(this);
4233         leftHandExpression = false;
4234         expression.visit(this);
4235     }
4236 
4237     protected void evaluatePostfixMethod(String method, Expression expression) {
4238         leftHandExpression = false;
4239         expression.visit(this);
4240 
4241         Variable tv = visitASTOREInTemp("postfix_" + method);
4242         int tempIdx  = tv.getIndex();
4243         cv.visitVarInsn(ALOAD, tempIdx);
4244 
4245         cv.visitLdcInsn(method);
4246         invokeNoArgumentsMethod.call(cv);
4247 
4248         store(expression);
4249 
4250         cv.visitVarInsn(ALOAD, tempIdx);
4251         removeVar(tv);
4252     }
4253 
4254     protected boolean isHolderVariable(Expression expression) {
4255         if (expression instanceof FieldExpression) {
4256             FieldExpression fieldExp = (FieldExpression) expression;
4257             return fieldExp.getField().isHolder();
4258         }
4259         if (expression instanceof VariableExpression) {
4260             VariableExpression varExp = (VariableExpression) expression;
4261             Variable variable = (Variable) variableStack.get(varExp.getVariable());
4262             if (variable != null) {
4263                 return variable.isHolder();
4264             }
4265             FieldNode field = classNode.getField(varExp.getVariable());
4266             if (field != null) {
4267                 return field.isHolder();
4268             }
4269         }
4270         return false;
4271     }
4272 
4273     protected void evaluateInstanceof(BinaryExpression expression) {
4274         expression.getLeftExpression().visit(this);
4275         Expression rightExp = expression.getRightExpression();
4276         String className = null;
4277         if (rightExp instanceof ClassExpression) {
4278             ClassExpression classExp = (ClassExpression) rightExp;
4279             className = classExp.getType();
4280         }
4281         else {
4282             throw new RuntimeException(
4283                 "Right hand side of the instanceof keyworld must be a class name, not: " + rightExp);
4284         }
4285         className = checkValidType(className, expression, "Must be a valid type name for an instanceof statement");
4286         String classInternalName = BytecodeHelper.getClassInternalName(className);
4287         cv.visitTypeInsn(INSTANCEOF, classInternalName);
4288     }
4289 
4290     /***
4291      * @return true if the given argument expression requires the stack, in
4292      *         which case the arguments are evaluated first, stored in the
4293      *         variable stack and then reloaded to make a method call
4294      */
4295     protected boolean argumentsUseStack(Expression arguments) {
4296         return arguments instanceof TupleExpression || arguments instanceof ClosureExpression;
4297     }
4298 
4299     /***
4300      * @return true if the given expression represents a non-static field
4301      */
4302     protected boolean isNonStaticField(Expression expression) {
4303         FieldNode field = null;
4304         if (expression instanceof VariableExpression) {
4305             VariableExpression varExp = (VariableExpression) expression;
4306             field = classNode.getField(varExp.getVariable());
4307         }
4308         else if (expression instanceof FieldExpression) {
4309             FieldExpression fieldExp = (FieldExpression) expression;
4310             field = classNode.getField(fieldExp.getFieldName());
4311         }
4312         else if (expression instanceof PropertyExpression) {
4313             PropertyExpression fieldExp = (PropertyExpression) expression;
4314             field = classNode.getField(fieldExp.getProperty());
4315         }
4316         if (field != null) {
4317             return !field.isStatic();
4318         }
4319         return false;
4320     }
4321 
4322     protected boolean isThisExpression(Expression expression) {
4323         if (expression instanceof VariableExpression) {
4324             VariableExpression varExp = (VariableExpression) expression;
4325             return varExp.getVariable().equals("this");
4326         }
4327         return false;
4328     }
4329 
4330     /***
4331      * For assignment expressions, return a safe expression for the LHS we can use
4332      * to return the value
4333      */
4334     protected Expression createReturnLHSExpression(Expression expression) {
4335         if (expression instanceof BinaryExpression) {
4336             BinaryExpression binExpr = (BinaryExpression) expression;
4337             if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
4338                 return createReusableExpression(binExpr.getLeftExpression());
4339             }
4340         }
4341         return null;
4342     }
4343 
4344     protected Expression createReusableExpression(Expression expression) {
4345         ExpressionTransformer transformer = new ExpressionTransformer() {
4346             public Expression transform(Expression expression) {
4347                 if (expression instanceof PostfixExpression) {
4348                     PostfixExpression postfixExp = (PostfixExpression) expression;
4349                     return postfixExp.getExpression();
4350                 }
4351                 else if (expression instanceof PrefixExpression) {
4352                     PrefixExpression prefixExp = (PrefixExpression) expression;
4353                     return prefixExp.getExpression();
4354                 }
4355                 return expression;
4356             }
4357         };
4358 
4359         // could just be a postfix / prefix expression or nested inside some other expression
4360         return transformer.transform(expression.transformExpression(transformer));
4361     }
4362 
4363     protected boolean isComparisonExpression(Expression expression) {
4364         if (expression instanceof BinaryExpression) {
4365             BinaryExpression binExpr = (BinaryExpression) expression;
4366             switch (binExpr.getOperation().getType()) {
4367                 case Types.COMPARE_EQUAL :
4368                 case Types.MATCH_REGEX :
4369                 case Types.COMPARE_GREATER_THAN :
4370                 case Types.COMPARE_GREATER_THAN_EQUAL :
4371                 case Types.COMPARE_LESS_THAN :
4372                 case Types.COMPARE_LESS_THAN_EQUAL :
4373                 case Types.COMPARE_IDENTICAL :
4374                 case Types.COMPARE_NOT_EQUAL :
4375                 case Types.KEYWORD_INSTANCEOF :
4376                     return true;
4377             }
4378         }
4379         else if (expression instanceof BooleanExpression) {
4380             return true;
4381         }
4382         return false;
4383     }
4384 
4385     protected void onLineNumber(ASTNode statement, String message) {
4386         int line = statement.getLineNumber();
4387         int col = statement.getColumnNumber();
4388         this.currentASTNode = statement;
4389 
4390         if (line >=0) {
4391             lineNumber = line;
4392             columnNumber = col;
4393         }
4394         if (CREATE_LINE_NUMBER_INFO && line >= 0 && cv != null) {
4395             Label l = new Label();
4396             cv.visitLabel(l);
4397             cv.visitLineNumber(line, l);
4398             if (ASM_DEBUG) {
4399                 helper.mark(message + "[" + statement.getLineNumber() + ":" + statement.getColumnNumber() + "]");
4400             }
4401         }
4402     }
4403 
4404     protected VariableScope getVariableScope() {
4405         if (variableScope == null) {
4406             if (methodNode != null) {
4407                 // if we're a closure method we'll have our variable scope already created
4408                 variableScope = methodNode.getVariableScope();
4409                 if (variableScope == null) {
4410                     variableScope = new VariableScope();
4411                     methodNode.setVariableScope(variableScope);
4412                     VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
4413                     visitor.setParameters(methodNode.getParameters());
4414                     Statement code = methodNode.getCode();
4415                     if (code != null) {
4416                         code.visit(visitor);
4417                     }
4418                 }
4419                 addFieldsToVisitor(variableScope);
4420             }
4421             else if (constructorNode != null) {
4422                 variableScope = new VariableScope();
4423                 constructorNode.setVariableScope(variableScope);
4424                 VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
4425                 visitor.setParameters(constructorNode.getParameters());
4426                 Statement code = constructorNode.getCode();
4427                 if (code != null) {
4428                     code.visit(visitor);
4429                 }
4430                 addFieldsToVisitor(variableScope);
4431             }
4432             else {
4433                 throw new RuntimeException("Can't create a variable scope outside of a method or constructor");
4434             }
4435         }
4436         return variableScope;
4437     }
4438 
4439     /***
4440      * @return a list of parameters for each local variable which needs to be
4441      *         passed into a closure
4442      */
4443     protected Parameter[] getClosureSharedVariables(ClosureExpression expression) {
4444         List vars = new ArrayList();
4445 
4446         //
4447         // First up, get the scopes for outside and inside the closure.
4448         // The inner scope must cover all nested closures, as well, as
4449         // everything that will be needed must be imported.
4450 
4451         VariableScope outerScope = getVariableScope().createRecursiveParentScope();
4452         VariableScope innerScope = expression.getVariableScope();
4453         if (innerScope == null) {
4454             System.out.println(
4455                 "No variable scope for: " + expression + " method: " + methodNode + " constructor: " + constructorNode);
4456             innerScope = new VariableScope(getVariableScope());
4457         }
4458         else {
4459             innerScope = innerScope.createRecursiveChildScope();
4460         }
4461 
4462 
4463         //
4464         // DeclaredVariables include any name that was assigned to within
4465         // the scope.  ReferencedVariables include any name that was read
4466         // from within the scope.  We get the sets from each and must piece
4467         // together the stack variable import list for the closure.  Note
4468         // that we don't worry about field variables here, as we don't have
4469         // to do anything special with them.  Stack variables, on the other
4470         // hand, have to be wrapped up in References for use.
4471 
4472         Set outerDecls = outerScope.getDeclaredVariables();
4473         Set outerRefs  = outerScope.getReferencedVariables();
4474         Set innerDecls = innerScope.getDeclaredVariables();
4475         Set innerRefs  = innerScope.getReferencedVariables();
4476 
4477 
4478         //
4479         // So, we care about any name referenced in the closure UNLESS:
4480         //   1) it's not declared in the outer context;
4481         //   2) it's a parameter;
4482         //   3) it's a field in the context class that isn't overridden
4483         //      by a stack variable in the outer context.
4484         //
4485         // BUG: We don't actually have the necessary information to do
4486         //      this right!  The outer declarations don't distinguish
4487         //      between assignments and variable declarations.  Therefore
4488         //      we can't tell when field variables have been overridden
4489         //      by stack variables in the outer context.  This must
4490         //      be fixed!
4491 
4492         Set varSet = new HashSet();
4493         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
4494             String var = (String) iter.next();
4495             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
4496             if (outerDecls.contains(var) && (isNotFieldOfOutermostClass(var))) {
4497                 String type = getVariableType(var);
4498                 vars.add(new Parameter(type, var));
4499                 varSet.add(var);
4500             }
4501         }
4502         for (Iterator iter = outerRefs.iterator(); iter.hasNext();) {
4503             String var = (String) iter.next();
4504             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
4505             if (innerDecls.contains(var) && (isNotFieldOfOutermostClass(var)) && !varSet.contains(var)) {
4506                 String type = getVariableType(var);
4507                 vars.add(new Parameter(type, var));
4508             }
4509         }
4510 
4511 
4512         Parameter[] answer = new Parameter[vars.size()];
4513         vars.toArray(answer);
4514         return answer;
4515     }
4516 
4517     protected boolean isNotFieldOfOutermostClass(String var) {
4518         //return classNode.getField(var) == null || isInnerClass();
4519         return getOutermostClass().getField(var) == null;
4520     }
4521 
4522     protected void findMutableVariables() {
4523         /*
4524         VariableScopeCodeVisitor outerVisitor = new VariableScopeCodeVisitor(true);
4525         node.getCode().visit(outerVisitor);
4526 
4527         addFieldsToVisitor(outerVisitor);
4528 
4529         VariableScopeCodeVisitor innerVisitor = outerVisitor.getClosureVisitor();
4530         */
4531         VariableScope outerScope = getVariableScope();
4532 
4533         // lets create a scope concatenating all the closure expressions
4534         VariableScope innerScope = outerScope.createCompositeChildScope();
4535 
4536         Set outerDecls = outerScope.getDeclaredVariables();
4537         Set outerRefs = outerScope.getReferencedVariables();
4538         Set innerDecls = innerScope.getDeclaredVariables();
4539         Set innerRefs = innerScope.getReferencedVariables();
4540 
4541         mutableVars.clear();
4542 
4543         for (Iterator iter = innerDecls.iterator(); iter.hasNext();) {
4544             String var = (String) iter.next();
4545             if ((outerDecls.contains(var) || outerRefs.contains(var)) && classNode.getField(var) == null) {
4546                 mutableVars.add(var);
4547             }
4548         }
4549 
4550         // we may call the closure twice and modify the variable in the outer scope
4551         // so for now lets assume that all variables are mutable
4552         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
4553             String var = (String) iter.next();
4554             if (outerDecls.contains(var) && classNode.getField(var) == null) {
4555                 mutableVars.add(var);
4556             }
4557         }
4558 
4559         //                System.out.println();
4560         //                System.out.println("method: " + methodNode + " classNode: " + classNode);
4561         //                System.out.println("child scopes: " + outerScope.getChildren());
4562         //                System.out.println("outerDecls: " + outerDecls);
4563         //                System.out.println("outerRefs: " + outerRefs);
4564         //                System.out.println("innerDecls: " + innerDecls);
4565         //                System.out.println("innerRefs: " + innerRefs);
4566     }
4567 
4568     protected void addFieldsToVisitor(VariableScope scope) {
4569         for (Iterator iter = classNode.getFields().iterator(); iter.hasNext();) {
4570             FieldNode field = (FieldNode) iter.next();
4571             String name = field.getName();
4572 
4573             scope.getDeclaredVariables().add(name);
4574             scope.getReferencedVariables().add(name);
4575         }
4576     }
4577 
4578     private boolean isInnerClass() {
4579         return classNode instanceof InnerClassNode;
4580     }
4581 
4582     protected String getVariableType(String name) {
4583         Variable variable = (Variable) variableStack.get(name);
4584         if (variable != null) {
4585             return variable.getTypeName();
4586         }
4587         return null;
4588     }
4589 
4590     protected void resetVariableStack(Parameter[] parameters) {
4591         lastVariableIndex = -1;
4592         variableStack.clear();
4593 
4594         scope = new BlockScope(null);
4595         //pushBlockScope();
4596 
4597         // lets push this onto the stack
4598         definingParameters = true;
4599         if (!isStaticMethod()) {
4600             defineVariable("this", classNode.getName()).getIndex();
4601         } // now lets create indices for the parameteres
4602         for (int i = 0; i < parameters.length; i++) {
4603             Parameter parameter = parameters[i];
4604             String type = parameter.getType();
4605             Variable v = defineVariable(parameter.getName(), type);
4606             int idx = v.getIndex();
4607             if (BytecodeHelper.isPrimitiveType(type)) {
4608                 helper.load(type, idx);
4609                 helper.box(type);
4610                 cv.visitVarInsn(ASTORE, idx);
4611             }
4612         }
4613         definingParameters = false;
4614     }
4615 
4616     protected void popScope() {
4617         int lastID = scope.getFirstVariableIndex();
4618 
4619         List removeKeys = new ArrayList();
4620         for (Iterator iter = variableStack.entrySet().iterator(); iter.hasNext();) {
4621             Map.Entry entry = (Map.Entry) iter.next();
4622             String name = (String) entry.getKey();
4623             Variable value = (Variable) entry.getValue();
4624             if (value.getIndex() >= lastID) {
4625                 removeKeys.add(name);
4626             }
4627         }
4628         for (Iterator iter = removeKeys.iterator(); iter.hasNext();) {
4629             Variable v  = (Variable) variableStack.remove(iter.next());
4630             if (CREATE_DEBUG_INFO) { // set localvartable
4631                 if (v != null) {
4632                     visitVariableEndLabel(v);
4633                     cv.visitLocalVariable(
4634                             v.getName(),
4635                             BytecodeHelper.getTypeDescription(v.getTypeName()),
4636                             v.getStartLabel(),
4637                             v.getEndLabel(),
4638                             v.getIndex()
4639                     );
4640                 }
4641             }
4642         }
4643         scope = scope.getParent();
4644     }
4645 
4646     void removeVar(Variable v ) {
4647     	variableStack.remove(v.getName());
4648         if (CREATE_DEBUG_INFO) { // set localvartable
4649         	Label endl = new Label();
4650         	cv.visitLabel(endl);
4651         	cv.visitLocalVariable(
4652         			v.getName(),
4653 					BytecodeHelper.getTypeDescription(v.getTypeName()),
4654 					v.getStartLabel(),
4655 					endl,
4656 					v.getIndex()
4657         	);
4658         }
4659     }
4660     private void visitVariableEndLabel(Variable v) {
4661         if (CREATE_DEBUG_INFO) {
4662             if(v.getEndLabel() == null) {
4663                 Label end = new Label();
4664                 v.setEndLabel(end);
4665             }
4666             cv.visitLabel(v.getEndLabel());
4667         }
4668     }
4669 
4670     protected void pushBlockScope() {
4671         pushBlockScope(true, true);
4672     }
4673 
4674     /***
4675      * create a new scope. Set break/continue label if the canXXX parameter is true. Otherwise
4676      * inherit parent's label.
4677      * @param canContinue   true if the start of the scope can take continue label
4678      * @param canBreak  true if the end of the scope can take break label
4679      */
4680     protected void pushBlockScope(boolean canContinue, boolean canBreak) {
4681         BlockScope parentScope = scope;
4682         scope = new BlockScope(parentScope);
4683         scope.setContinueLabel(canContinue ? new Label() : (parentScope == null ? null : parentScope.getContinueLabel()));
4684         scope.setBreakLabel(canBreak? new Label() : (parentScope == null ? null : parentScope.getBreakLabel()));
4685         scope.setFirstVariableIndex(getNextVariableID());
4686     }
4687 
4688     /***
4689      * Defines the given variable in scope and assigns it to the stack
4690      */
4691     protected Variable defineVariable(String name, String type) {
4692         return defineVariable(name, type, true);
4693     }
4694 
4695     protected Variable defineVariable(String name, String type, boolean define) {
4696         return defineVariable(name, new Type(type), define);
4697     }
4698 
4699     private Variable defineVariable(String name, Type type, boolean define) {
4700         Variable answer = (Variable) variableStack.get(name);
4701         if (answer == null) {
4702             lastVariableIndex = getNextVariableID();
4703             answer = new Variable(lastVariableIndex, type, name);
4704             if (mutableVars.contains(name)) {
4705                 answer.setHolder(true);
4706             }
4707             variableStack.put(name, answer);
4708 
4709             Label startLabel  = new Label();
4710             answer.setStartLabel(startLabel);
4711             if (define) {
4712                 if (definingParameters) {
4713                     if (answer.isHolder()) {
4714                         cv.visitTypeInsn(NEW, "groovy/lang/Reference"); // br todo to associate a label with the variable
4715                         cv.visitInsn(DUP);
4716                         cv.visitVarInsn(ALOAD, lastVariableIndex);
4717                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V");
4718                         cv.visitVarInsn(ASTORE, lastVariableIndex);
4719                         cv.visitLabel(startLabel);
4720                     }
4721                 }
4722                 else {
4723                     // using new variable inside a comparison expression
4724                     // so lets initialize it too
4725                     if (answer.isHolder() && !isInScriptBody()) {
4726                         //cv.visitVarInsn(ASTORE, lastVariableIndex + 1); // I might need this to set the reference value
4727 
4728                         cv.visitTypeInsn(NEW, "groovy/lang/Reference");
4729                         cv.visitInsn(DUP);
4730                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "()V");
4731 
4732                         cv.visitVarInsn(ASTORE, lastVariableIndex);
4733                         cv.visitLabel(startLabel);
4734                         //cv.visitVarInsn(ALOAD, idx + 1);
4735                     }
4736                     else {
4737                         if (!leftHandExpression) { // new var on the RHS: init with null
4738                             cv.visitInsn(ACONST_NULL);
4739                             cv.visitVarInsn(ASTORE, lastVariableIndex);
4740                             cv.visitLabel(startLabel);
4741                         }
4742                     }
4743                 }
4744             }
4745         }
4746         return answer;
4747     }
4748 
4749     private boolean isDoubleSizeVariable(Type type) {
4750         return "long".equals(type.getName()) || "double".equals(type.getName());
4751     }
4752 
4753     private int getNextVariableID() {
4754     	int index = 0;
4755     	for (Iterator iter = variableStack.values().iterator(); iter.hasNext();) {
4756     		Variable var = (Variable) iter.next();
4757     		if (isDoubleSizeVariable(var.getType())) {
4758     			index += 2;
4759     		} else {
4760     			index++;
4761     		}
4762     	}
4763     	return index;
4764     }
4765 
4766     /*** @return true if the given name is a local variable or a field */
4767     protected boolean isFieldOrVariable(String name) {
4768         return variableStack.containsKey(name) || classNode.getField(name) != null;
4769     }
4770 
4771     protected Type checkValidType(Type type, ASTNode node, String message) {
4772         if (type.isDynamic()) {
4773             return type;
4774         }
4775         String name = checkValidType(type.getName(), node, message);
4776         if (type.getName().equals(name)) {
4777             return type;
4778         }
4779         return new Type(name);
4780     }
4781 
4782     protected String checkValidType(String type, ASTNode node, String message) {
4783         if (type!= null && type.length() == 0)
4784             return "java.lang.Object";
4785         if (type.endsWith("[]")) {
4786             String postfix = "[]";
4787             String prefix = type.substring(0, type.length() - 2);
4788             return checkValidType(prefix, node, message) + postfix;
4789         }
4790         int idx = type.indexOf('$');
4791         if (idx > 0) {
4792             String postfix = type.substring(idx);
4793             String prefix = type.substring(0, idx);
4794             return checkValidType(prefix, node, message) + postfix;
4795         }
4796         if (BytecodeHelper.isPrimitiveType(type) || "void".equals(type)) {
4797             return type;
4798         }
4799         String original = type;
4800         type = resolveClassName(type);
4801         if (type != null) {
4802             return type;
4803         }
4804 
4805         throw new MissingClassException(original, node, message + " for class: " + classNode.getName());
4806     }
4807 
4808     protected String resolveClassName(String type) {
4809         return classNode.resolveClassName(type);
4810     }
4811 
4812     protected String createVariableName(String type) {
4813         return "__" + type + (++tempVariableNameCounter);
4814     }
4815 
4816     /***
4817      * @return if the type of the expression can be determined at compile time
4818      *         then this method returns the type - otherwise null
4819      */
4820     protected String getExpressionType(Expression expression) {
4821         if (isComparisonExpression(expression)) {
4822             return "boolean";
4823         }
4824         if (expression instanceof VariableExpression) {
4825             VariableExpression varExpr = (VariableExpression) expression;
4826             Variable variable = (Variable) variableStack.get(varExpr.getVariable());
4827             if (variable != null && !variable.isHolder()) {
4828                 Type type = variable.getType();
4829                 if (! type.isDynamic()) {
4830                     return type.getName();
4831                 }
4832             }
4833         }
4834         return null;
4835     }
4836 
4837     /***
4838      * @return true if the value is an Integer, a Float, a Long, a Double or a
4839      *         String .
4840      */
4841     protected static boolean isPrimitiveFieldType(String type) {
4842         return type.equals("java.lang.String")
4843             || type.equals("java.lang.Integer")
4844             || type.equals("java.lang.Double")
4845             || type.equals("java.lang.Long")
4846             || type.equals("java.lang.Float");
4847     }
4848 
4849     protected boolean isInClosureConstructor() {
4850         return constructorNode != null
4851             && classNode.getOuterClass() != null
4852             && classNode.getSuperClass().equals(Closure.class.getName());
4853     }
4854 
4855     protected boolean isStaticMethod() {
4856         if (methodNode == null) { // we're in a constructor
4857             return false;
4858         }
4859         return methodNode.isStatic();
4860     }
4861 
4862     Map classCache = new HashMap();
4863     {
4864         classCache.put("int", Integer.TYPE);
4865         classCache.put("byte", Byte.TYPE);
4866         classCache.put("short", Short.TYPE);
4867         classCache.put("char", Character.TYPE);
4868         classCache.put("boolean", Boolean.TYPE);
4869         classCache.put("long", Long.TYPE);
4870         classCache.put("double", Double.TYPE);
4871         classCache.put("float", Float.TYPE);
4872         classCache.put("void", Void.TYPE);
4873     }
4874     /***
4875      * @return loads the given type name
4876      */
4877     protected Class loadClass(String name) {
4878 
4879         if (name.equals(this.classNode.getName())) {
4880             return Object.class;
4881         }
4882 
4883         if (name == null) {
4884             return null;
4885         }
4886         else if (name.length() == 0) {
4887             return Object.class;
4888         }
4889 
4890         name = BytecodeHelper.formatNameForClassLoading(name);
4891 
4892     	try {
4893     		Class cls = (Class)classCache.get(name);
4894     		if (cls != null)
4895     			return cls;
4896 
4897     		CompileUnit compileUnit = getCompileUnit();
4898             if (compileUnit != null) {
4899             	cls = compileUnit.loadClass(name);
4900                 classCache.put(name, cls);
4901             	return cls;
4902             }
4903             else {
4904                 throw new ClassGeneratorException("Could not load class: " + name);
4905             }
4906         }
4907         catch (ClassNotFoundException e) {
4908             throw new ClassGeneratorException("Error when compiling class: " + classNode.getName() + ". Reason: could not load class: " + name + " reason: " + e, e);
4909         }
4910     }
4911 
4912     protected CompileUnit getCompileUnit() {
4913         CompileUnit answer = classNode.getCompileUnit();
4914         if (answer == null) {
4915             answer = context.getCompileUnit();
4916         }
4917         return answer;
4918     }
4919 
4920     /***
4921      * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding.
4922      * @param call
4923      */
4924     public void resolve(MethodCallExpression call) {
4925         if (call.isResolveFailed()) {
4926             return;
4927         }
4928         else if (call.isTypeResolved()) {
4929             return;
4930         }
4931 
4932         Expression obj = call.getObjectExpression();
4933         String meth = call.getMethod();
4934         Class ownerClass = null;
4935         boolean isStaticCall = false;
4936         boolean isSuperCall = false;
4937 
4938         List arglist = new ArrayList();
4939         Expression args = call.getArguments();
4940         if (args instanceof TupleExpression) {
4941             TupleExpression tupleExpression = (TupleExpression) args;
4942             List argexps = tupleExpression.getExpressions();
4943             for (int i = 0; i < argexps.size(); i++) {
4944                 Expression expression = (Expression) argexps.get(i);
4945                 Class cls = expression.getTypeClass();
4946                 if (cls == null) {
4947                     call.setResolveFailed(true);
4948                     return ;
4949                 }
4950                 else {
4951                     arglist.add(cls);
4952                 }
4953             }
4954         } else if (args instanceof ClosureExpression) {
4955             call.setResolveFailed(true);
4956             return ;// todo
4957         } else {
4958             call.setResolveFailed(true);
4959             return ;
4960         }
4961 
4962 
4963         Class[] argsArray = new Class[arglist.size()];
4964         arglist.toArray(argsArray);
4965 
4966 
4967         if (obj instanceof ClassExpression) {
4968             // static call
4969             //ClassExpression cls = (ClassExpression)obj;
4970             //String type = cls.getType();
4971             ownerClass = obj.getTypeClass(); //loadClass(type);
4972             isStaticCall = true;
4973         } else if (obj instanceof VariableExpression) {
4974             VariableExpression var = (VariableExpression) obj;
4975             if (var.getVariable().equals("this") && (methodNode == null? true : !methodNode.isStatic()) ) {
4976                 isStaticCall = false;
4977                 if (methodNode != null) {
4978                     isStaticCall = Modifier.isStatic(methodNode.getModifiers());
4979                 }
4980                 MetaMethod mmeth = getMethodOfThisAndSuper(meth, argsArray, isStaticCall);
4981                 if (mmeth != null) {
4982                     call.setMethod(mmeth);
4983                     return ;
4984                 }
4985                 else {
4986                     call.setResolveFailed(true);
4987                     return ;
4988                 }
4989             }
4990             else if (var.getVariable().equals("super") ) {
4991                 isSuperCall = true;
4992                 ownerClass = var.getTypeClass();
4993             }
4994             else {
4995                 ownerClass = var.getTypeClass();
4996             }
4997         }
4998         else /*if (obj instanceof PropertyExpression)*/ {
4999             ownerClass = obj.getTypeClass();
5000             if (ownerClass == null) {
5001                 call.setResolveFailed(true);
5002                 call.setFailure("target class is null");
5003                 return ;  // take care other cases later version
5004             }
5005         }
5006 
5007         if (ownerClass == Object.class) {
5008             call.setResolveFailed(true);
5009             return ; // use late binding for dynamic types
5010         }
5011         else if (ownerClass == null)  {
5012             call.setResolveFailed(true);
5013             return ; // use dynamic dispatching for GroovyObject
5014         }
5015         else
5016             if (!isSuperCall && !isStaticCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // not optimize GroovyObject meth call for now
5017             call.setResolveFailed(true);
5018             return ;
5019         }
5020         else
5021             if (ownerClass.isPrimitive()) {
5022             call.setResolveFailed(true);
5023             return ; // todo handle primitives
5024         }
5025 
5026 
5027         //MetaMethod mmethod = ScriptBytecodeAdapter.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
5028         // todo is this thread safe?
5029         MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
5030         if (mmethod!= null) {
5031             call.setMethod(mmethod);
5032         }
5033         else {
5034             call.setResolveFailed(true);
5035         }
5036         return ;
5037     }
5038     /***
5039      * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding.
5040      * @param call
5041      */
5042     public void resolve(ConstructorCallExpression call) {
5043         if (call.isResolveFailed()) {
5044             return ;
5045         }
5046         else if (call.isTypeResolved()) {
5047             return ;
5048         }
5049 
5050         String declaredType = call.getTypeToSet();
5051         if (declaredType.equals(classNode.getName())) {
5052             call.setResolveFailed(true);
5053             call.setFailure("cannot resolve on the current class itself. ");
5054             return;
5055         }
5056         else {
5057             call.setType(declaredType);
5058             if (call.getTypeClass() == null) {
5059                 call.setResolveFailed(true);
5060                 call.setFailure("type name cannot be resolved. ");
5061                 return;
5062             }
5063         }
5064 
5065         boolean isSuperCall = false;
5066 
5067         List arglist = new ArrayList();
5068         Expression args = call.getArguments();
5069         if (args instanceof TupleExpression) {
5070             TupleExpression tupleExpression = (TupleExpression) args;
5071             List argexps = tupleExpression.getExpressions();
5072             for (int i = 0; i < argexps.size(); i++) {
5073                 Expression expression = (Expression) argexps.get(i);
5074                 Class cls = expression.getTypeClass();
5075                 if (cls == null) {
5076                     call.setResolveFailed(true);
5077                     return ;
5078                 }
5079                 else {
5080                     arglist.add(cls);
5081                 }
5082             }
5083         } else if (args instanceof ClosureExpression) {
5084             call.setResolveFailed(true);
5085             call.setFailure("don't know how to handle closure arg. ");
5086             return ;// todo
5087         } else {
5088             call.setResolveFailed(true);
5089             call.setFailure("unknown arg type: " + args.getClass().getName());
5090             return ;
5091         }
5092 
5093 
5094         Class[] argsArray = new Class[arglist.size()];
5095         arglist.toArray(argsArray);
5096 
5097         Class ownerClass = call.getTypeClass();
5098         if (ownerClass == null) {
5099             String typeName = call.getType();
5100             if (typeName.equals(this.classNode.getName())) {
5101                 // this is a ctor call to this class
5102                 call.setResolveFailed(true);
5103                 call.setFailure("invoke constructor for this. no optimization for now");
5104                 return ;
5105             }
5106             else {
5107                 try {
5108                     ownerClass = loadClass(typeName);
5109                     if (ownerClass == null) {
5110                         call.setResolveFailed(true);
5111                         call.setFailure("owner class type is null. ");
5112                         return ;
5113                     }
5114                 }
5115                 catch (Throwable th) {
5116                     call.setResolveFailed(true);
5117                     call.setFailure("Exception: " + th);
5118                     return ;
5119                 }
5120             }
5121         }
5122 
5123         if (ownerClass == Object.class) {
5124             call.setResolveFailed(true);
5125             call.setFailure("owner class type java.lang.Object.  use late binding for dynamic types");
5126             return ; //
5127         }
5128         else if (ownerClass == null)  {
5129             call.setResolveFailed(true);
5130             call.setFailure("owner class type is null. use dynamic dispatching for GroovyObject");
5131             return; // use dynamic dispatching for GroovyObject
5132         }
5133 // safe to call groovyobject ctor
5134 //        else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call
5135 //            call.setResolveFailed(true);
5136 //            return null;
5137 //        }
5138         else if (ownerClass.isPrimitive()) {
5139             call.setResolveFailed(true);
5140             throwException("The owner of the constructor is primitive.");
5141             return ;
5142         }
5143 
5144         Constructor ctor = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedConstructor(ownerClass, argsArray);
5145         if (ctor!= null) {
5146             call.setConstructor(ctor);
5147         }
5148         else {
5149             call.setResolveFailed(true);
5150         }
5151         return ;
5152     }
5153 
5154     public void resolve(PropertyExpression propertyExpression)  {
5155         if (propertyExpression.getTypeClass() != null)
5156             return ;
5157         if (propertyExpression.isResolveFailed())
5158             return ;
5159 
5160         Expression ownerExp = propertyExpression.getObjectExpression();
5161         Class ownerClass = ownerExp.getTypeClass();
5162         String propName = propertyExpression.getProperty();
5163         if (propName.equals("class")) {
5164             propertyExpression.setTypeClass(Class.class);
5165             return ;
5166         }
5167 
5168         // handle arraylength
5169         if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) {
5170             propertyExpression.setTypeClass(int.class);
5171             return;
5172         }
5173 
5174         if (isThisExpression(ownerExp)) {
5175             // lets use the field expression if its available
5176             if (classNode == null) {
5177                 propertyExpression.setResolveFailed(true);
5178                 return ;
5179             }
5180             FieldNode field   = null;
5181             ownerExp.setType(classNode.getName());
5182             try {
5183                 if( (field = classNode.getField(propName)) != null ) {
5184 //                    Class cls =loadClass(field.getType());
5185 //                    propertyExpression.setAccess(PropertyExpression.LOCAL_FIELD_ACCESS);
5186 //                    propertyExpression.setTypeClass(cls);
5187 // local property access. to be determined in the future
5188                     propertyExpression.setResolveFailed(true);
5189                     propertyExpression.setFailure("local property access. to be determined in the future.");
5190                     return ;
5191                 } else {
5192                     // search for super classes/interface
5193                     // interface first first
5194                     String[] interfaces = classNode.getInterfaces();
5195                     String[] supers = new String[interfaces.length + 1];
5196 
5197                     int i = 0;
5198                     for (; i < interfaces.length; i++) {
5199                         supers[i] = interfaces[i];
5200                     }
5201                     supers[i] = classNode.getSuperClass();
5202                     for (int j = 0; j < supers.length; j++) {
5203                         String aSuper = supers[j];
5204                         Class superClass = loadClass(aSuper);
5205                         Field fld  = superClass.getDeclaredField(propName);
5206                         if (fld != null && !Modifier.isPrivate(fld.getModifiers())) {
5207                             propertyExpression.setField(fld);
5208                             return ;
5209                         }
5210                     }
5211                 }
5212             } catch (Exception e) {
5213                 propertyExpression.setResolveFailed(true);
5214                 propertyExpression.setFailure(e.getMessage());
5215                 return ;
5216             }
5217         }
5218         else if (ownerExp instanceof ClassExpression) {
5219             if (ownerClass != null) {
5220                 Field fld  = null;
5221                 try {
5222                     fld = ownerClass.getDeclaredField(propName);
5223                     if (!Modifier.isPrivate(fld.getModifiers())) {
5224                         propertyExpression.setField(fld);
5225                         return ;
5226                     }
5227                 } catch (NoSuchFieldException e) {
5228                     propertyExpression.setResolveFailed(true);
5229                     return ;
5230                 }
5231             }
5232         }
5233         else { // search public field and then setter/getter
5234             if (ownerClass != null) {
5235                 propertyExpression.setResolveFailed(true); // will get reset if property/getter/setter were found
5236                 Field fld  = null;
5237                 try {
5238                     fld = ownerClass.getDeclaredField(propName);
5239                 } catch (NoSuchFieldException e) {}
5240 
5241                 if (fld != null && Modifier.isPublic(fld.getModifiers())) {
5242                     propertyExpression.setField(fld);
5243                 }
5244 
5245                 // let's get getter and setter
5246                 String getterName = "get" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
5247                 String setterName = "set" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
5248 
5249                 Method[] meths = ownerClass.getMethods();
5250                 for (int i = 0; i < meths.length; i++) {
5251                     Method method = meths[i];
5252                     String methName =method.getName();
5253                     Class[] paramClasses = method.getParameterTypes();
5254                     if (methName.equals(getterName) && paramClasses.length == 0) {
5255                         propertyExpression.setGetter(method);
5256                     } else if (methName.equals(setterName) && paramClasses.length == 1) {
5257                         propertyExpression.setSetter(method);
5258                     }
5259                 }
5260                 return ;
5261             }
5262         }
5263         propertyExpression.setResolveFailed(true);
5264         return ;
5265     }
5266 
5267     public void resolve(AttributeExpression attributeExpression)  {
5268         if (attributeExpression.getTypeClass() != null)
5269             return ;
5270         if (attributeExpression.isResolveFailed())
5271             return ;
5272 
5273         Expression ownerExp = attributeExpression.getObjectExpression();
5274         Class ownerClass = ownerExp.getTypeClass();
5275         String propName = attributeExpression.getProperty();
5276         if (propName.equals("class")) {
5277             attributeExpression.setTypeClass(Class.class);
5278             return ;
5279         }
5280 
5281         // handle arraylength
5282         if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) {
5283             attributeExpression.setTypeClass(int.class);
5284             return;
5285         }
5286 
5287         if (isThisExpression(ownerExp)) {
5288             // lets use the field expression if its available
5289             if (classNode == null) {
5290                 attributeExpression.setResolveFailed(true);
5291                 return ;
5292             }
5293             FieldNode field   = null;
5294             ownerExp.setType(classNode.getName());
5295             try {
5296                 if( (field = classNode.getField(propName)) != null ) {
5297 //                    Class cls =loadClass(field.getType());
5298 //                    attributeExpression.setAccess(PropertyExpression.LOCAL_FIELD_ACCESS);
5299 //                    attributeExpression.setTypeClass(cls);
5300 // local property access. to be determined in the future
5301                     attributeExpression.setResolveFailed(true);
5302                     attributeExpression.setFailure("local property access. to be determined in the future.");
5303                     return ;
5304                 } else {
5305                     // search for super classes/interface
5306                     // interface first first
5307                     String[] interfaces = classNode.getInterfaces();
5308                     String[] supers = new String[interfaces.length + 1];
5309 
5310                     int i = 0;
5311                     for (; i < interfaces.length; i++) {
5312                         supers[i] = interfaces[i];
5313                     }
5314                     supers[i] = classNode.getSuperClass();
5315                     for (int j = 0; j < supers.length; j++) {
5316                         String aSuper = supers[j];
5317                         Class superClass = loadClass(aSuper);
5318                         Field fld  = superClass.getDeclaredField(propName);
5319                         if (fld != null && !Modifier.isPrivate(fld.getModifiers())) {
5320                             attributeExpression.setField(fld);
5321                             return ;
5322                         }
5323                     }
5324                 }
5325             } catch (Exception e) {
5326                 attributeExpression.setResolveFailed(true);
5327                 attributeExpression.setFailure(e.getMessage());
5328                 return ;
5329             }
5330         }
5331         else if (ownerExp instanceof ClassExpression) {
5332             if (ownerClass != null) {
5333                 Field fld  = null;
5334                 try {
5335                     fld = ownerClass.getDeclaredField(propName);
5336                     if (!Modifier.isPrivate(fld.getModifiers())) {
5337                         attributeExpression.setField(fld);
5338                         return ;
5339                     }
5340                 } catch (NoSuchFieldException e) {
5341                     attributeExpression.setResolveFailed(true);
5342                     return ;
5343                 }
5344             }
5345         }
5346         else { // search public field and then setter/getter
5347             if (ownerClass != null) {
5348                 attributeExpression.setResolveFailed(true); // will get reset if property/getter/setter were found
5349                 Field fld  = null;
5350                 try {
5351                     fld = ownerClass.getDeclaredField(propName);
5352                 } catch (NoSuchFieldException e) {}
5353 
5354                 if (fld != null && Modifier.isPublic(fld.getModifiers())) {
5355                     attributeExpression.setField(fld);
5356                 }
5357 
5358                 // let's get getter and setter
5359                 String getterName = "get" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
5360                 String setterName = "set" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
5361 
5362                 Method[] meths = ownerClass.getMethods();
5363                 for (int i = 0; i < meths.length; i++) {
5364                     Method method = meths[i];
5365                     String methName =method.getName();
5366                     Class[] paramClasses = method.getParameterTypes();
5367                     if (methName.equals(getterName) && paramClasses.length == 0) {
5368                         attributeExpression.setGetter(method);
5369                     } else if (methName.equals(setterName) && paramClasses.length == 1) {
5370                         attributeExpression.setSetter(method);
5371                     }
5372                 }
5373                 return ;
5374             }
5375         }
5376         attributeExpression.setResolveFailed(true);
5377         return ;
5378     }
5379     /*** search in the current classNode and super class for matching method */
5380     private MetaMethod getMethodOfThisAndSuper(String methName, Class[] argsArray, boolean isStaticCall) {
5381         MethodNode candidate = null;
5382         List meths = classNode.getMethods();
5383         Class[] candidateParamClasses = null;
5384         for (int i = 0; i < meths.size(); i++) {
5385             MethodNode meth = (MethodNode) meths.get(i);
5386             if (meth.getName().equals(methName)) {
5387                 Parameter[] params = meth.getParameters();
5388                 if  (params.length == argsArray.length) {
5389                     Class[] paramClasses = new Class[params.length];
5390                     for (int j = 0; j < params.length; j++) {
5391                         Parameter param = params[j];
5392                         String type = param.getType();
5393                         Class paramClass = null;
5394                         try {
5395                             paramClass = loadClass(type);
5396                         } catch (Exception e) {
5397                             log.warning(e.getMessage());
5398                             return null;
5399                         }
5400                         paramClasses[j] = paramClass;
5401                     }
5402                     if (MetaClass.isValidMethod(paramClasses, argsArray, false)) {
5403                         candidateParamClasses = paramClasses;
5404                         candidate = meth;
5405                         break;
5406                     }
5407                     else {
5408                         if (MetaClass.isValidMethod(paramClasses, argsArray, true)){
5409                             candidateParamClasses = paramClasses;
5410                             candidate = meth;
5411                             break;
5412                         }
5413                     }
5414                 }
5415             }
5416         }
5417 
5418         if (candidate != null && candidateParamClasses != null) {
5419             // let's synth a MetaMethod from the MethodNode
5420             try {
5421                 return new MetaMethod(methName, null, candidateParamClasses, loadClass(candidate.getReturnType()), candidate.getModifiers());
5422             } catch (Exception e) {
5423                 log.warning(e.getMessage());
5424                 return null;
5425             }
5426         }
5427         else {
5428             // try super class
5429             Class superClass = null;
5430             try {
5431                 superClass = loadClass(classNode.getSuperClass());
5432             }
5433             catch(Exception e) {
5434                 // the super may be a groovy class that's not compiled yet
5435                 log.warning(e.getMessage());
5436             }
5437             // should I filter out GroovyObject super class here?
5438             if (superClass != null ) {
5439                 MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(superClass, methName, argsArray, isStaticCall);
5440                 if (mmethod == null)
5441                     return null;
5442                 int modies = mmethod.getModifiers();
5443                 if (Modifier.isPrivate(modies)) {
5444                     return null;
5445                 }
5446                 else if(modies == 0) {
5447                     // match package
5448                     int pThis = classNode.getName().lastIndexOf(".");
5449                     String packageNameThis = pThis > 0? classNode.getName().substring(0, pThis) : "";
5450 
5451                     int pSuper = classNode.getSuperClass().lastIndexOf(".");
5452                     String packageNameSuper = pSuper > 0? classNode.getSuperClass().substring(0, pSuper) : "";
5453                     if (packageNameThis.equals(packageNameSuper)) {
5454                         return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers());
5455                     }
5456                     else {
5457                         return null;
5458                     }
5459                 }
5460                 else {
5461                     // let changes the declaring class back to null (meaning "this"), so that proper class inheritance permission control is obeyed
5462                     return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers());
5463                 }
5464             }
5465             return null;
5466         }
5467     }
5468 
5469 
5470     /***
5471      *  to find out the real type of a Variable Object
5472      */
5473     public void resolve(VariableExpression expression) {
5474 
5475         String variableName = expression.getVariable();
5476 // todo process arrays!
5477       //-----------------------------------------------------------------------
5478       // SPECIAL CASES
5479 
5480         //
5481         // "this" for static methods is the Class instance
5482 
5483         if (isStaticMethod() && variableName.equals("this")) {
5484             expression.setTypeClass(Class.class);
5485             return;
5486         } else if (variableName.equals("super")) {
5487             if (isStaticMethod() ) {
5488                 expression.setTypeClass(Class.class);
5489                 return;
5490             }
5491             else {
5492                 try {
5493                     Class cls = loadClass(classNode.getSuperClass());
5494                     expression.setTypeClass(cls);
5495                     return ;
5496                 }
5497                 catch (Exception e) {
5498                     expression.setResolveFailed(true);
5499                     expression.setFailure(e.getMessage());
5500                     return ;
5501                 }
5502             }
5503         } else if (variableName.equals("this")){
5504             return ;
5505         } else {
5506 //            String className = resolveClassName(variableName);
5507 //            if (className != null) {
5508 //                return loadClass(className);
5509 //            }
5510         }
5511 
5512 
5513       //-----------------------------------------------------------------------
5514       // GENERAL VARIABLE LOOKUP
5515 
5516         //
5517         // We are handling only unqualified variables here.  Therefore,
5518         // we do not care about accessors, because local access doesn't
5519         // go through them.  Therefore, precedence is as follows:
5520         //   1) local variables, nearest block first
5521         //   2) class fields
5522         //   3) repeat search from 2) in next outer class
5523 
5524         boolean  handled  = false;
5525         Variable variable = (Variable)variableStack.get( variableName );
5526 
5527         try {
5528             if( variable != null ) {
5529                 Type t = variable.getType();
5530                 if (t.getRealName().length() == 0) {
5531                     String tname = t.getName();
5532                     if (tname.endsWith("[]")) {
5533                         expression.setResolveFailed(true);
5534                         expression.setFailure("array type to be supported later");
5535                         return ;  // todo hanlde array
5536                     }
5537                     else if (tname.equals(classNode.getName())){
5538                         expression.setResolveFailed(true);
5539                         return ;
5540                     }
5541                     else if (classNode.getOuterClass() != null && tname.equals(classNode.getOuterClass().getName())){
5542                         expression.setResolveFailed(true);
5543                         return ;
5544                     }
5545                     Class cls = loadClass(tname);
5546                     expression.setTypeClass(cls);
5547                     expression.setDynamic(t.isDynamic());
5548                     return ;
5549                 } else {
5550                     String tname = t.getRealName();
5551                     if (tname.endsWith("[]")) {
5552                         expression.setResolveFailed(true);
5553                         expression.setFailure("array type to be supported later");
5554                         return ;  // todo hanlde array
5555                     }
5556                     Class cls = loadClass(tname);
5557                     expression.setTypeClass(cls);
5558                     expression.setDynamic(t.isDynamic());
5559                     return ;
5560                 }
5561 
5562 //            if( variable.isProperty() ) {
5563 //                processPropertyVariable(variable );
5564 //            }
5565 //            else {
5566 //                processStackVariable(variable );
5567 //            }
5568 //
5569             } else {
5570                 int       steps   = 0;
5571                 ClassNode currentClassNode = classNode;
5572                 FieldNode field   = null;
5573                 do {
5574                     if( (field = currentClassNode.getField(variableName)) != null ) {
5575                         if (methodNode == null || !methodNode.isStatic() || field.isStatic() ) {
5576                             if (/*field.isDynamicType() || */field.isHolder()) {
5577                                 expression.setResolveFailed(true);
5578                                 expression.setFailure("reference type to be supported later");
5579                                 return ;
5580                             } else {
5581                                 String type = field.getType();
5582                                 Class cls = loadClass(type);
5583                                 expression.setTypeClass(cls);
5584                                 expression.setDynamic(field.isDynamicType());
5585                                 return ;
5586                             }
5587                         }
5588                     }
5589                     steps++;
5590                 } while( (currentClassNode = currentClassNode.getOuterClass()) != null );
5591             }
5592 
5593             //
5594             // Finally, a new variable
5595 
5596             String variableType = expression.getType();
5597             if (variableType.length() > 0 && !variableType.equals("java.lang.Object")) {
5598                 Class cls = loadClass(variableType);
5599                 expression.setTypeClass(cls);
5600                 return ;
5601             }
5602         } catch (Exception e) {
5603             log.warning(e.getMessage());
5604             expression.setResolveFailed(true);
5605             expression.setFailure(e.getMessage());
5606         }
5607         return ;
5608     }
5609 
5610     public MetaMethod resolve(StaticMethodCallExpression staticCall) {
5611        if (staticCall.isResolveFailed()) {
5612             return null;
5613         }
5614         else if (staticCall.isTypeResolved()) {
5615             return staticCall.getMetaMethod();
5616         }
5617 
5618         String ownerTypeName = staticCall.getOwnerType();
5619         String meth = staticCall.getMethod();
5620         Class ownerClass = null;
5621         try {
5622             ownerClass = loadClass(ownerTypeName);
5623         } catch (Exception e) {
5624             staticCall.setResolveFailed(true);
5625             staticCall.setFailure("Owner type could not be resolved: " + e);
5626             return null;
5627         }
5628 
5629         boolean isStaticCall = true;
5630         boolean isSuperCall = false;
5631 
5632         List arglist = new ArrayList();
5633         Expression args = staticCall.getArguments();
5634         if (args instanceof TupleExpression) {
5635             TupleExpression tupleExpression = (TupleExpression) args;
5636             List argexps = tupleExpression.getExpressions();
5637             for (int i = 0; i < argexps.size(); i++) {
5638                 Expression expression = (Expression) argexps.get(i);
5639                 Class cls = expression.getTypeClass();
5640                 if (cls == null) {
5641                     staticCall.setResolveFailed(true);
5642                     staticCall.setFailure("Argument type could not be resolved.");
5643                     return null;
5644                 }
5645                 else {
5646                     arglist.add(cls);
5647                 }
5648             }
5649         } else if (args instanceof ClosureExpression) {
5650             staticCall.setResolveFailed(true);
5651             staticCall.setFailure("Resolving on Closure call not implemented yet. ");
5652             return null;// todo
5653         } else {
5654             staticCall.setResolveFailed(true);
5655             staticCall.setFailure("Unknown argument expression type.");
5656             return null;
5657         }
5658 
5659 
5660         Class[] argsArray = new Class[arglist.size()];
5661         arglist.toArray(argsArray);
5662 
5663         if (ownerClass == Object.class) {
5664             staticCall.setResolveFailed(true);
5665             staticCall.setFailure("Resolving on java.lang.Object static call not supported. ");
5666             return null; // use late binding for dynamic types
5667         }
5668         else if (ownerClass == null)  {
5669             staticCall.setResolveFailed(true);
5670             staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. ");
5671             return null; // use dynamic dispatching for GroovyObject
5672         }
5673         else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call
5674             staticCall.setResolveFailed(true);
5675             staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. ");
5676             return null;
5677         }
5678         else if (ownerClass.isPrimitive()) {
5679             staticCall.setResolveFailed(true);
5680             staticCall.setFailure("Could not use primitive as method owner");
5681             return null; // todo handle primitives
5682         }
5683 
5684 
5685         //MetaMethod mmethod = ScriptBytecodeAdapter.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
5686         // todo is this thread safe?
5687         MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
5688         if (mmethod!= null) {
5689             staticCall.setMetaMethod(mmethod);
5690         }
5691         else {
5692             staticCall.setResolveFailed(true);
5693             staticCall.setFailure("Could not find MetaMethod in the MetaClass.");
5694         }
5695         return mmethod;
5696     }
5697 
5698     // the fowllowing asXXX() methods are copied from the Invoker class, to avoid initilization of an invoker instance,
5699     // which has lots of baggage with it, notably the meta class stuff.
5700     private static Object asType(Object object, Class type) {
5701         if (object == null) {
5702             return null;
5703         }
5704         if (type.isInstance(object)) {
5705             return object;
5706         }
5707         if (type.equals(String.class)) {
5708             return object.toString();
5709         }
5710         if (type.equals(Character.class)) {
5711             if (object instanceof Number) {
5712                 return asCharacter((Number) object);
5713             }
5714             else {
5715                 String text = object.toString();
5716                 if (text.length() == 1) {
5717                     return new Character(text.charAt(0));
5718                 }
5719                 else {
5720                     throw new ClassCastException("Cannot cast: " + text + " to a Character");
5721                 }
5722             }
5723         }
5724         if (Number.class.isAssignableFrom(type)) {
5725             if (object instanceof Character) {
5726                 return new Integer(((Character) object).charValue());
5727             }
5728             else if (object instanceof String) {
5729                 String c = (String) object;
5730                 if (c.length() == 1) {
5731                     return new Integer(c.charAt(0));
5732                 }
5733                 else {
5734                     throw new ClassCastException("Cannot cast: '" + c + "' to an Integer");
5735                 }
5736             }
5737         }
5738         if (object instanceof Number) {
5739             Number n = (Number) object;
5740             if (type.isPrimitive()) {
5741                 if (type == byte.class) {
5742                     return new Byte(n.byteValue());
5743                 }
5744                 if (type == char.class) {
5745                     return new Character((char) n.intValue());
5746                 }
5747                 if (type == short.class) {
5748                     return new Short(n.shortValue());
5749                 }
5750                 if (type == int.class) {
5751                     return new Integer(n.intValue());
5752                 }
5753                 if (type == long.class) {
5754                     return new Long(n.longValue());
5755                 }
5756                 if (type == float.class) {
5757                     return new Float(n.floatValue());
5758                 }
5759                 if (type == double.class) {
5760                     Double answer = new Double(n.doubleValue());
5761                     //throw a runtime exception if conversion would be out-of-range for the type.
5762                     if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
5763                             || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
5764                         throw new  GroovyRuntimeException("Automatic coercion of "+n.getClass().getName()
5765                                 +" value "+n+" to double failed.  Value is out of range.");
5766                     }
5767                     return answer;
5768                 }
5769             }
5770             else {
5771                 if (Number.class.isAssignableFrom(type)) {
5772                     if (type == Byte.class) {
5773                         return new Byte(n.byteValue());
5774                     }
5775                     if (type == Character.class) {
5776                         return new Character((char) n.intValue());
5777                     }
5778                     if (type == Short.class) {
5779                         return new Short(n.shortValue());
5780                     }
5781                     if (type == Integer.class) {
5782                         return new Integer(n.intValue());
5783                     }
5784                     if (type == Long.class) {
5785                         return new Long(n.longValue());
5786                     }
5787                     if (type == Float.class) {
5788                         return new Float(n.floatValue());
5789                     }
5790                     if (type == Double.class) {
5791                         Double answer = new Double(n.doubleValue());
5792                         //throw a runtime exception if conversion would be out-of-range for the type.
5793                         if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
5794                                 || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
5795                             throw new  GroovyRuntimeException("Automatic coercion of "+n.getClass().getName()
5796                                     +" value "+n+" to double failed.  Value is out of range.");
5797                         }
5798                         return answer;
5799                     }
5800 
5801                 }
5802             }
5803         }
5804         if (type == Boolean.class) {
5805             return asBool(object) ? Boolean.TRUE : Boolean.FALSE;
5806         }
5807         return object;
5808     }
5809 
5810     private static boolean asBool(Object object) {
5811        if (object instanceof Boolean) {
5812             Boolean booleanValue = (Boolean) object;
5813             return booleanValue.booleanValue();
5814         }
5815         else if (object instanceof Matcher) {
5816             Matcher matcher = (Matcher) object;
5817             RegexSupport.setLastMatcher(matcher);
5818             return matcher.find();
5819         }
5820         else if (object instanceof Collection) {
5821             Collection collection = (Collection) object;
5822             return !collection.isEmpty();
5823         }
5824         else if (object instanceof Number) {
5825             Number n = (Number) object;
5826             return n.intValue() != 0;
5827         }
5828         else {
5829             return object != null;
5830         }
5831     }
5832     private static Character asCharacter(Number value) {
5833         return new Character((char) value.intValue());
5834     }
5835 
5836     private static Character asCharacter(String text) {
5837         return new Character(text.charAt(0));
5838     }
5839 }