View Javadoc

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