View Javadoc

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