View Javadoc

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