View Javadoc

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