View Javadoc

1   /*
2    * $Id: AsmClassGenerator2.java,v 1.7 2005/01/11 16:57:13 glaforge 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.7 $
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 = false;
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         if (text != null && text.endsWith(".class")) {
2581             text = text.substring(0, text.length() - 6);
2582         }
2583         try {
2584             return resolveClassName(text);
2585         }
2586         catch (Exception e) {
2587             return null;
2588         }
2589     }
2590 
2591     public void visitFieldExpression(FieldExpression expression) {
2592         FieldNode field = expression.getField();
2593 
2594 
2595 	    if (field.isStatic()) {
2596         	if (leftHandExpression) {
2597         		storeStaticField(expression);
2598         	}
2599         	else {
2600         		loadStaticField(expression);
2601         	}
2602         } else {
2603         	if (leftHandExpression) {
2604         		storeThisInstanceField(expression);
2605         	}
2606         	else {
2607         		loadInstanceField(expression);
2608         	}
2609 		}
2610     }
2611 
2612     /***
2613      *
2614      * @param fldExp
2615      */
2616     public void loadStaticField(FieldExpression fldExp) {
2617         FieldNode field = fldExp.getField();
2618         boolean holder = field.isHolder() && !isInClosureConstructor();
2619         String type = field.getType();
2620 
2621         String ownerName = (field.getOwner().equals(classNode.getName()))
2622                 ? internalClassName
2623                 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2624         if (holder) {
2625             cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
2626             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
2627         }
2628         else {
2629             cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
2630             if (BytecodeHelper.isPrimitiveType(type)) {
2631                 helper.box(type);
2632 			} else {
2633 			}
2634         }
2635     }
2636 
2637 	/***
2638 	 * RHS instance field. should move most of the code in the BytecodeHelper
2639 	 * @param fldExp
2640 	 */
2641     public void loadInstanceField(FieldExpression fldExp) {
2642     	FieldNode field = fldExp.getField();
2643         boolean holder = field.isHolder() && !isInClosureConstructor();
2644         String type = field.getType();
2645         String ownerName = (field.getOwner().equals(classNode.getName()))
2646 				? internalClassName
2647 				: org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2648 
2649         cv.visitVarInsn(ALOAD, 0);
2650 		cv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
2651 
2652 		if (holder) {
2653 			cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
2654 		} else {
2655 			if (BytecodeHelper.isPrimitiveType(type)) {
2656 				helper.box(type);
2657 			} else {
2658 			}
2659 		}
2660     }
2661 
2662     public void storeThisInstanceField(FieldExpression expression) {
2663         FieldNode field = expression.getField();
2664 
2665         boolean holder = field.isHolder() && !isInClosureConstructor();
2666         String type = field.getType();
2667 
2668         String ownerName =  (field.getOwner().equals(classNode.getName())) ?
2669         		internalClassName : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2670         if (holder) {
2671             Variable tv = visitASTOREInTemp(field.getName());
2672             int tempIndex = tv.getIndex();
2673             cv.visitVarInsn(ALOAD, 0);
2674             cv.visitFieldInsn(GETFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2675             cv.visitVarInsn(ALOAD, tempIndex);
2676             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
2677             removeVar(tv);
2678         }
2679         else {
2680             if (isInClosureConstructor()) {
2681                 helper.doCast(type);
2682             }
2683             else {
2684                 if (ENABLE_EARLY_BINDING) {
2685                     helper.doCast(type);
2686                 }
2687                 else {
2688                     // this may be superfluous
2689                     doConvertAndCast(type);
2690                 }
2691             }
2692             //Variable tmpVar = defineVariable(createVariableName(field.getName()), "java.lang.Object", false);
2693             Variable tmpVar = defineVariable(createVariableName(field.getName()), field.getType(), false);
2694             //int tempIndex = tmpVar.getIndex();
2695             //helper.store(field.getType(), tempIndex);
2696             helper.store(tmpVar, MARK_START);
2697             helper.loadThis(); //cv.visitVarInsn(ALOAD, 0);
2698             helper.load(tmpVar);
2699             helper.putField(field, ownerName);
2700             //cv.visitFieldInsn(PUTFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2701             // let's remove the temp var
2702             removeVar(tmpVar);
2703         }
2704     }
2705 
2706 
2707     public void storeStaticField(FieldExpression expression) {
2708     	FieldNode field = expression.getField();
2709 
2710         boolean holder = field.isHolder() && !isInClosureConstructor();
2711 
2712         String type = field.getType();
2713 
2714         String ownerName = (field.getOwner().equals(classNode.getName()))
2715                 ? internalClassName
2716                 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner()));
2717         if (holder) {
2718             Variable tv = visitASTOREInTemp(field.getName());
2719             int tempIndex = tv.getIndex();
2720             cv.visitFieldInsn(GETSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2721             cv.visitVarInsn(ALOAD, tempIndex);
2722             cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
2723             removeVar(tv);
2724         }
2725         else {
2726             if (isInClosureConstructor()) {
2727                 helper.doCast(type);
2728             }
2729             else {
2730                 if (ENABLE_EARLY_BINDING) {
2731                     helper.doCast(type);
2732                 }
2733                 else {
2734                     // this may be superfluous
2735                     //doConvertAndCast(type);
2736                     // use weaker cast
2737                     helper.doCast(type);
2738                 }
2739             }
2740             cv.visitFieldInsn(PUTSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2741         }
2742     }
2743 
2744     protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first ) {
2745         FieldNode field = expression.getField();
2746         boolean isStatic = field.isStatic();
2747 
2748         Variable fieldTemp = defineVariable(createVariableName(field.getName()), "java.lang.Object", false);
2749         int valueIdx = fieldTemp.getIndex();
2750 
2751         if (leftHandExpression && first) {
2752             cv.visitVarInsn(ASTORE, valueIdx);
2753             visitVariableStartLabel(fieldTemp);
2754         }
2755 
2756         if (steps > 1 || !isStatic) {
2757             cv.visitVarInsn(ALOAD, 0);
2758             cv.visitFieldInsn(
2759                 GETFIELD,
2760                 internalClassName,
2761                 "owner",
2762                 BytecodeHelper.getTypeDescription(outerClassNode.getName()));
2763         }
2764 
2765         if( steps == 1 ) {
2766             int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
2767             String ownerName = BytecodeHelper.getClassInternalName(outerClassNode.getName());
2768 
2769             if (leftHandExpression) {
2770                 cv.visitVarInsn(ALOAD, valueIdx);
2771                 boolean holder = field.isHolder() && !isInClosureConstructor();
2772                 if ( !holder) {
2773                     doConvertAndCast(field.getType());
2774                 }
2775             }
2776             cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType()));
2777             if (!leftHandExpression) {
2778                 if (BytecodeHelper.isPrimitiveType(field.getType())) {
2779                     helper.box(field.getType());
2780                 }
2781             }
2782         }
2783 
2784         else {
2785             visitOuterFieldExpression( expression, outerClassNode.getOuterClass(), steps - 1, false );
2786         }
2787     }
2788 
2789 
2790 
2791     /***
2792      *  Visits a bare (unqualified) variable expression.
2793      */
2794 
2795     public void visitVariableExpression(VariableExpression expression) {
2796 
2797         String variableName = expression.getVariable();
2798 
2799       //-----------------------------------------------------------------------
2800       // SPECIAL CASES
2801 
2802         //
2803         // "this" for static methods is the Class instance
2804 
2805         if (isStaticMethod() && variableName.equals("this")) {
2806             visitClassExpression(new ClassExpression(classNode.getName()));
2807             return;                                               // <<< FLOW CONTROL <<<<<<<<<
2808         }
2809 
2810         //
2811         // "super" also requires special handling
2812 
2813         if (variableName.equals("super")) {
2814             visitClassExpression(new ClassExpression(classNode.getSuperClass()));
2815             return;                                               // <<< FLOW CONTROL <<<<<<<<<
2816         }
2817 
2818 
2819         //
2820         // class names return a Class instance, too
2821 
2822 //        if (!variableName.equals("this")) {
2823 //            String className = resolveClassName(variableName);
2824 //            if (className != null) {
2825 //                if (leftHandExpression) {
2826 //                    throw new RuntimeParserException(
2827 //                        "Cannot use a class expression on the left hand side of an assignment",
2828 //                        expression);
2829 //                }
2830 //                visitClassExpression(new ClassExpression(className));
2831 //                return;                                               // <<< FLOW CONTROL <<<<<<<<<
2832 //            }
2833 //        }
2834 
2835 
2836       //-----------------------------------------------------------------------
2837       // GENERAL VARIABLE LOOKUP
2838 
2839 
2840         //
2841         // We are handling only unqualified variables here.  Therefore,
2842         // we do not care about accessors, because local access doesn't
2843         // go through them.  Therefore, precedence is as follows:
2844         //   1) local variables, nearest block first
2845         //   2) class fields
2846         //   3) repeat search from 2) in next outer class
2847 
2848         boolean  handled  = false;
2849         Variable variable = (Variable)variableStack.get( variableName );
2850 
2851         if( variable != null ) {
2852 
2853             if( variable.isProperty() ) {
2854                 processPropertyVariable(variable );
2855             }
2856             else {
2857                 if (ENABLE_EARLY_BINDING && expression.isTypeResolved() && leftHandExpression) {
2858                     // let's pass the type back to the variable
2859                     String typeName = expression.getType();
2860                     Type varOldType = variable.getType();
2861                     if (varOldType.isDynamic()) {
2862                         variable.setType(new Type(typeName, true));
2863                     }
2864                     else if (!varOldType.getName().equals(typeName)){
2865                         new GroovyRuntimeException("VariableExpression data type conflicts with the existing variable. "
2866                                 + "[" + expression.getLineNumber() + ":" + expression.getColumnNumber() + "]");
2867                     }
2868                 }
2869                 processStackVariable(variable );
2870             }
2871 
2872             handled = true;
2873         } else {
2874             //
2875             // Loop through outer classes for fields
2876 
2877             int       steps   = 0;
2878             ClassNode currentClassNode = classNode;
2879             FieldNode field   = null;
2880 
2881             do {
2882                 if( (field = currentClassNode.getField(variableName)) != null ) {
2883                     if (methodNode == null || !methodNode.isStatic() || field.isStatic() )
2884                         break; //this is a match. break out. todo to be tested
2885                 }
2886                 steps++;
2887 
2888             } while( (currentClassNode = currentClassNode.getOuterClass()) != null );
2889 
2890             if( field != null ) {
2891                 processFieldAccess( variableName, field, steps );
2892                 handled = true;
2893             }
2894         }
2895 
2896         //
2897         // class names return a Class instance, too
2898         if (!handled  && !variableName.equals("this")) {
2899             String className = resolveClassName(variableName);
2900             if (className != null) {
2901                 if (leftHandExpression) {
2902                     throwException("Cannot use a class expression on the left hand side of an assignment");
2903                 }
2904                 visitClassExpression(new ClassExpression(className));
2905                 return;                                               // <<< FLOW CONTROL <<<<<<<<<
2906             }
2907         }
2908 
2909         //
2910         // Finally, if unhandled, create a variable for it.
2911         // Except there a stack variable should be created,
2912         // we define the variable as a property accessor and
2913         // let other parts of the classgen report the error
2914         // if the property doesn't exist.
2915 
2916         if( !handled ) {
2917             String variableType = expression.getType();
2918             variable = defineVariable( variableName, variableType );
2919 
2920             if (leftHandExpression && expression.isDynamic()) {
2921                 variable.setDynamic(true); // false  by default
2922             }
2923             else {
2924                 variable.setDynamic(false);
2925             }
2926 
2927             if( isInScriptBody() || !leftHandExpression ) { // todo problematic: if on right hand not defined, should I report undefined var error?
2928                 variable.setProperty( true );
2929                 processPropertyVariable(variable );
2930             }
2931             else {
2932                 processStackVariable(variable );
2933             }
2934         }
2935     }
2936 
2937 
2938     protected void processStackVariable(Variable variable ) {
2939         boolean holder = variable.isHolder() && !passingClosureParams;
2940 
2941         if( leftHandExpression ) {
2942             helper.storeVar(variable, holder);
2943         }
2944         else {
2945         	helper.loadVar(variable, holder);
2946         }
2947         if (ASM_DEBUG) {
2948             helper.mark("var: " + variable.getName());
2949         }
2950     }
2951 
2952     private void visitVariableStartLabel(Variable variable) {
2953         if (CREATE_DEBUG_INFO) {
2954             Label l = variable.getStartLabel();
2955             if (l != null) {
2956                 cv.visitLabel(l);
2957             } else {
2958                 System.out.println("start label == null! what to do about this?");
2959             }
2960         }
2961     }
2962 
2963     protected void processPropertyVariable(Variable variable ) {
2964     	String name = variable.getName();
2965         if (variable.isHolder() && passingClosureParams && isInScriptBody() ) {
2966             // lets create a ScriptReference to pass into the closure
2967             cv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference");
2968             cv.visitInsn(DUP);
2969 
2970             loadThisOrOwner();
2971             cv.visitLdcInsn(name);
2972 
2973             cv.visitMethodInsn(
2974                 INVOKESPECIAL,
2975                 "org/codehaus/groovy/runtime/ScriptReference",
2976                 "<init>",
2977                 "(Lgroovy/lang/Script;Ljava/lang/String;)V");
2978         }
2979         else {
2980             visitPropertyExpression(new PropertyExpression(VariableExpression.THIS_EXPRESSION, name));
2981         }
2982     }
2983 
2984 
2985     protected void processFieldAccess( String name, FieldNode field, int steps ) {
2986         FieldExpression expression = new FieldExpression(field);
2987 
2988         if( steps == 0 ) {
2989             visitFieldExpression( expression );
2990         }
2991         else {
2992             visitOuterFieldExpression( expression, classNode.getOuterClass(), steps, true );
2993         }
2994     }
2995 
2996 
2997 
2998     /***
2999      * @return true if we are in a script body, where all variables declared are no longer
3000      * local variables but are properties
3001      */
3002     protected boolean isInScriptBody() {
3003         if (classNode.isScriptBody()) {
3004             return true;
3005         }
3006         else {
3007             return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
3008         }
3009     }
3010 
3011     /***
3012      * @return true if this expression will have left a value on the stack
3013      * that must be popped
3014      */
3015     protected boolean isPopRequired(Expression expression) {
3016         if (expression instanceof MethodCallExpression) {
3017             if (expression.getType() != null && expression.getType().equals("void")) { // nothing on the stack
3018                 return false;
3019             } else {
3020                 return !MethodCallExpression.isSuperMethodCall((MethodCallExpression) expression);
3021             }
3022         }
3023         if (expression instanceof BinaryExpression) {
3024             BinaryExpression binExp = (BinaryExpression) expression;
3025             switch (binExp.getOperation().getType()) {   // br todo should leave a copy of the value on the stack for all the assignemnt.
3026 //                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
3027 //                case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAsignment()
3028 //                case Types.MINUS_EQUAL :
3029 //                case Types.MULTIPLY_EQUAL :
3030 //                case Types.DIVIDE_EQUAL :
3031 //                case Types.INTDIV_EQUAL :
3032 //                case Types.MOD_EQUAL :
3033 //                    return false;
3034             }
3035         }
3036         return true;
3037     }
3038 
3039     protected boolean firstStatementIsSuperInit(Statement code) {
3040         ExpressionStatement expStmt = null;
3041         if (code instanceof ExpressionStatement) {
3042             expStmt = (ExpressionStatement) code;
3043         }
3044         else if (code instanceof BlockStatement) {
3045             BlockStatement block = (BlockStatement) code;
3046             if (!block.getStatements().isEmpty()) {
3047                 Object expr = block.getStatements().get(0);
3048                 if (expr instanceof ExpressionStatement) {
3049                     expStmt = (ExpressionStatement) expr;
3050                 }
3051             }
3052         }
3053         if (expStmt != null) {
3054             Expression expr = expStmt.getExpression();
3055             if (expr instanceof MethodCallExpression) {
3056             	MethodCallExpression call = (MethodCallExpression) expr;
3057                 if (MethodCallExpression.isSuperMethodCall(call)) {
3058                     // not sure which one is constantly used as the super class ctor call. To cover both for now
3059                 	return call.getMethod().equals("<init>") || call.getMethod().equals("super");
3060                 }
3061             }
3062         }
3063         return false;
3064     }
3065 
3066     protected void createSyntheticStaticFields() {
3067         for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
3068             String staticFieldName = (String) iter.next();
3069             // generate a field node
3070             cw.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null);
3071         }
3072 
3073         if (!syntheticStaticFields.isEmpty()) {
3074             cv =
3075                 cw.visitMethod(
3076                     ACC_STATIC + ACC_SYNTHETIC,
3077                     "class$",
3078                     "(Ljava/lang/String;)Ljava/lang/Class;",
3079                     null,
3080                     null);
3081             helper = new BytecodeHelper(cv);
3082 
3083             Label l0 = new Label();
3084             cv.visitLabel(l0);
3085             cv.visitVarInsn(ALOAD, 0);
3086             cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
3087             Label l1 = new Label();
3088             cv.visitLabel(l1);
3089             cv.visitInsn(ARETURN);
3090             Label l2 = new Label();
3091             cv.visitLabel(l2);
3092             cv.visitVarInsn(ASTORE, 1);
3093             cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
3094             cv.visitInsn(DUP);
3095             cv.visitVarInsn(ALOAD, 1);
3096             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;");
3097             cv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V");
3098             cv.visitInsn(ATHROW);
3099             cv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
3100             cv.visitMaxs(3, 2);
3101 
3102             cw.visitEnd();
3103         }
3104     }
3105     /*** load class object on stack */
3106     public void visitClassExpression(ClassExpression expression) {
3107         String type = expression.getText();
3108         //type = checkValidType(type, expression, "Must be a valid type name for a constructor call");
3109 
3110 
3111         if (BytecodeHelper.isPrimitiveType(type)) {
3112             String objectType = BytecodeHelper.getObjectTypeForPrimitive(type);
3113             cv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;");
3114         }
3115         else {
3116             final String staticFieldName =
3117                 (type.equals(classNode.getName())) ? "class$0" : "class$" + type.replace('.', '$').replace('[', '_').replace(';', '_');
3118 
3119             syntheticStaticFields.add(staticFieldName);
3120 
3121             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
3122             Label l0 = new Label();
3123             cv.visitJumpInsn(IFNONNULL, l0);
3124             cv.visitLdcInsn(type);
3125             cv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
3126             cv.visitInsn(DUP);
3127             cv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
3128             Label l1 = new Label();
3129             cv.visitJumpInsn(GOTO, l1);
3130             cv.visitLabel(l0);
3131             cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
3132             cv.visitLabel(l1);
3133         }
3134     }
3135 
3136     public void visitRangeExpression(RangeExpression expression) {
3137         leftHandExpression = false;
3138         expression.getFrom().visit(this);
3139 
3140         leftHandExpression = false;
3141         expression.getTo().visit(this);
3142 
3143         helper.pushConstant(expression.isInclusive());
3144 
3145         createRangeMethod.call(cv);
3146     }
3147 
3148     public void visitMapEntryExpression(MapEntryExpression expression) {
3149     }
3150 
3151     public void visitMapExpression(MapExpression expression) {
3152         List entries = expression.getMapEntryExpressions();
3153         int size = entries.size();
3154         helper.pushConstant(size * 2);
3155 
3156         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3157 
3158         int i = 0;
3159         for (Iterator iter = entries.iterator(); iter.hasNext();) {
3160             MapEntryExpression entry = (MapEntryExpression) iter.next();
3161 
3162             cv.visitInsn(DUP);
3163             helper.pushConstant(i++);
3164             visitAndAutoboxBoolean(entry.getKeyExpression());
3165             cv.visitInsn(AASTORE);
3166 
3167             cv.visitInsn(DUP);
3168             helper.pushConstant(i++);
3169             visitAndAutoboxBoolean(entry.getValueExpression());
3170             cv.visitInsn(AASTORE);
3171         }
3172         createMapMethod.call(cv);
3173     }
3174 
3175     public void visitTupleExpression(TupleExpression expression) {
3176         int size = expression.getExpressions().size();
3177 
3178         helper.pushConstant(size);
3179 
3180         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3181 
3182         for (int i = 0; i < size; i++) {
3183             cv.visitInsn(DUP);
3184             helper.pushConstant(i);
3185             visitAndAutoboxBoolean(expression.getExpression(i));
3186             cv.visitInsn(AASTORE);
3187         }
3188         //createTupleMethod.call(cv);
3189     }
3190 
3191     public void visitArrayExpression(ArrayExpression expression) {
3192         String type = expression.getElementType();
3193         String typeName = BytecodeHelper.getClassInternalName(type);
3194         Expression sizeExpression = expression.getSizeExpression();
3195         if (sizeExpression != null) {
3196             // lets convert to an int
3197             visitAndAutoboxBoolean(sizeExpression);
3198             asIntMethod.call(cv);
3199 
3200             cv.visitTypeInsn(ANEWARRAY, typeName);
3201         }
3202         else {
3203             int size = expression.getExpressions().size();
3204             helper.pushConstant(size);
3205 
3206             cv.visitTypeInsn(ANEWARRAY, typeName);
3207 
3208             for (int i = 0; i < size; i++) {
3209                 cv.visitInsn(DUP);
3210                 helper.pushConstant(i);
3211                 Expression elementExpression = expression.getExpression(i);
3212                 if (elementExpression == null) {
3213                     ConstantExpression.NULL.visit(this);
3214                 }
3215                 else {
3216 
3217                     if(!type.equals(elementExpression.getClass().getName())) {
3218                         visitCastExpression(new CastExpression(type, elementExpression));
3219                     }
3220                     else {
3221                         visitAndAutoboxBoolean(elementExpression);
3222                     }
3223                 }
3224                 cv.visitInsn(AASTORE);
3225             }
3226         }
3227     }
3228 
3229     public void visitListExpression(ListExpression expression) {
3230         int size = expression.getExpressions().size();
3231         helper.pushConstant(size);
3232 
3233         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3234 
3235         for (int i = 0; i < size; i++) {
3236             cv.visitInsn(DUP);
3237             helper.pushConstant(i);
3238             visitAndAutoboxBoolean(expression.getExpression(i));
3239             cv.visitInsn(AASTORE);
3240         }
3241         createListMethod.call(cv);
3242     }
3243 
3244     public void visitGStringExpression(GStringExpression expression) {
3245         int size = expression.getValues().size();
3246         helper.pushConstant(size);
3247 
3248         cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
3249 
3250         for (int i = 0; i < size; i++) {
3251             cv.visitInsn(DUP);
3252             helper.pushConstant(i);
3253             visitAndAutoboxBoolean(expression.getValue(i));
3254             cv.visitInsn(AASTORE);
3255         }
3256 
3257         Variable tv = visitASTOREInTemp("iterator");
3258         int paramIdx = tv.getIndex();
3259 
3260         ClassNode innerClass = createGStringClass(expression);
3261         addInnerClass(innerClass);
3262         String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName());
3263 
3264         cv.visitTypeInsn(NEW, innerClassinternalName);
3265         cv.visitInsn(DUP);
3266         cv.visitVarInsn(ALOAD, paramIdx);
3267 
3268         cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", "([Ljava/lang/Object;)V");
3269         removeVar(tv);
3270     }
3271 
3272     private Variable visitASTOREInTemp(String s) {
3273         return storeInTemp(s, "java.lang.Object");
3274     }
3275 
3276     // Implementation methods
3277     //-------------------------------------------------------------------------
3278     protected boolean addInnerClass(ClassNode innerClass) {
3279         innerClass.setModule(classNode.getModule());
3280         return innerClasses.add(innerClass);
3281     }
3282 
3283     protected ClassNode createClosureClass(ClosureExpression expression) {
3284         ClassNode owner = getOutermostClass();
3285         boolean parentIsInnerClass = owner instanceof InnerClassNode;
3286         String outerClassName = owner.getName();
3287         String name = outerClassName + "$"
3288                 + context.getNextClosureInnerName(owner, classNode, methodNode); // br added a more infomative name
3289         boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass();
3290         if (staticMethodOrInStaticClass) {
3291             outerClassName = "java.lang.Class";
3292         }
3293         Parameter[] parameters = expression.getParameters();
3294         if (parameters == null || parameters.length == 0) {
3295             // lets create a default 'it' parameter
3296             parameters = new Parameter[] { new Parameter("it")};
3297         }
3298 
3299         Parameter[] localVariableParams = getClosureSharedVariables(expression);
3300 
3301         InnerClassNode answer = new InnerClassNode(owner, name, ACC_SUPER, "groovy.lang.Closure"); // clsures are local inners and not public
3302         answer.setEnclosingMethod(this.methodNode);
3303         if (staticMethodOrInStaticClass) {
3304             answer.setStaticClass(true);
3305         }
3306         if (isInScriptBody()) {
3307             answer.setScriptBody(true);
3308         }
3309         MethodNode method =
3310             answer.addMethod("doCall", ACC_PUBLIC, "java.lang.Object", parameters, expression.getCode());
3311 
3312         method.setLineNumber(expression.getLineNumber());
3313         method.setColumnNumber(expression.getColumnNumber());
3314 
3315         VariableScope varScope = expression.getVariableScope();
3316         if (varScope == null) {
3317             throw new RuntimeException(
3318                 "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
3319         }
3320         else {
3321             method.setVariableScope(varScope);
3322         }
3323         if (parameters.length > 1
3324             || (parameters.length == 1
3325                 && parameters[0].getType() != null
3326                 && !parameters[0].getType().equals("java.lang.Object"))) {
3327 
3328             // lets add a typesafe call method
3329             answer.addMethod(
3330                 "call",
3331                 ACC_PUBLIC,
3332                 "java.lang.Object",
3333                 parameters,
3334                 new ReturnStatement(
3335                     new MethodCallExpression(
3336                         VariableExpression.THIS_EXPRESSION,
3337                         "doCall",
3338                         new ArgumentListExpression(parameters))));
3339         }
3340 
3341         FieldNode ownerField = answer.addField("owner", ACC_PRIVATE, outerClassName, null);
3342 
3343         // lets make the constructor
3344         BlockStatement block = new BlockStatement();
3345         block.addStatement(
3346             new ExpressionStatement(
3347                 new MethodCallExpression(
3348                     new VariableExpression("super"),
3349                     "<init>",
3350                     new VariableExpression("_outerInstance"))));
3351         block.addStatement(
3352             new ExpressionStatement(
3353                 new BinaryExpression(
3354                     new FieldExpression(ownerField),
3355                     Token.newSymbol(Types.EQUAL, -1, -1),
3356                     new VariableExpression("_outerInstance"))));
3357 
3358         // lets assign all the parameter fields from the outer context
3359         for (int i = 0; i < localVariableParams.length; i++) {
3360             Parameter param = localVariableParams[i];
3361             String paramName = param.getName();
3362             boolean holder = mutableVars.contains(paramName);
3363             Expression initialValue = null;
3364             String type = param.getType();
3365             FieldNode paramField = null;
3366             if (holder) {
3367             	initialValue = new VariableExpression(paramName);
3368                 type = Reference.class.getName();
3369                 param.makeReference();
3370                 paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue);
3371                 paramField.setHolder(true);
3372                 String realType = param.getRealType();
3373                 String methodName = Verifier.capitalize(paramName);
3374 
3375                 // lets add a getter & setter
3376                 Expression fieldExp = new FieldExpression(paramField);
3377                 answer.addMethod(
3378                     "get" + methodName,
3379                     ACC_PUBLIC,
3380                     realType,
3381                     Parameter.EMPTY_ARRAY,
3382                     new ReturnStatement(fieldExp));
3383 
3384                 /*
3385                 answer.addMethod(
3386                     "set" + methodName,
3387                     ACC_PUBLIC,
3388                     "void",
3389                     new Parameter[] { new Parameter(realType, "__value") },
3390                     new ExpressionStatement(
3391                         new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
3392                         */
3393             }
3394             else {
3395             	PropertyNode propertyNode = answer.addProperty(paramName, ACC_PUBLIC, type, initialValue, null, null);
3396                 paramField = propertyNode.getField();
3397                 block.addStatement(
3398                     new ExpressionStatement(
3399                         new BinaryExpression(
3400                             new FieldExpression(paramField),
3401                             Token.newSymbol(Types.EQUAL, -1, -1),
3402                             new VariableExpression(paramName))));
3403             }
3404         }
3405 
3406         Parameter[] params = new Parameter[2 + localVariableParams.length];
3407         params[0] = new Parameter(outerClassName, "_outerInstance");
3408         params[1] = new Parameter("java.lang.Object", "_delegate");
3409         System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
3410 
3411         answer.addConstructor(ACC_PUBLIC, params, block);
3412         return answer;
3413     }
3414 
3415     protected ClassNode getOutermostClass() {
3416         if (outermostClass == null) {
3417             outermostClass = classNode;
3418             while (outermostClass instanceof InnerClassNode) {
3419                 outermostClass = outermostClass.getOuterClass();
3420             }
3421         }
3422         return outermostClass;
3423     }
3424 
3425     protected ClassNode createGStringClass(GStringExpression expression) {
3426         ClassNode owner = classNode;
3427         if (owner instanceof InnerClassNode) {
3428             owner = owner.getOuterClass();
3429         }
3430         String outerClassName = owner.getName();
3431         String name = outerClassName + "$" + context.getNextInnerClassIdx();
3432         InnerClassNode answer = new InnerClassNode(owner, name, ACC_SUPER, GString.class.getName());
3433         answer.setEnclosingMethod(this.methodNode);
3434         FieldNode stringsField =
3435             answer.addField(
3436                 "strings",
3437                 ACC_PRIVATE /*| ACC_STATIC*/,
3438                 "java.lang.String[]",
3439                 new ArrayExpression("java.lang.String", expression.getStrings()));
3440         answer.addMethod(
3441             "getStrings",
3442             ACC_PUBLIC,
3443             "java.lang.String[]",
3444             Parameter.EMPTY_ARRAY,
3445             new ReturnStatement(new FieldExpression(stringsField)));
3446         // lets make the constructor
3447         BlockStatement block = new BlockStatement();
3448         block.addStatement(
3449             new ExpressionStatement(
3450                 new MethodCallExpression(new VariableExpression("super"), "<init>", new VariableExpression("values"))));
3451         Parameter[] contructorParams = new Parameter[] { new Parameter("java.lang.Object[]", "values")};
3452         answer.addConstructor(ACC_PUBLIC, contructorParams, block);
3453         return answer;
3454     }
3455 
3456     protected void doConvertAndCast(String type) {
3457         if (!type.equals("java.lang.Object")) {
3458             /*** todo should probably support array coercions */
3459             if (!type.endsWith("[]") && isValidTypeForCast(type)) {
3460                 visitClassExpression(new ClassExpression(type));
3461                 asTypeMethod.call(cv);
3462             }
3463 
3464             helper.doCast(type);
3465         }
3466     }
3467 
3468     protected void evaluateLogicalOrExpression(BinaryExpression expression) {
3469         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
3470         Label l0 = new Label();
3471         Label l2 = new Label();
3472         cv.visitJumpInsn(IFEQ, l0);
3473 
3474         cv.visitLabel(l2);
3475 
3476         visitConstantExpression(ConstantExpression.TRUE);
3477 
3478         Label l1 = new Label();
3479         cv.visitJumpInsn(GOTO, l1);
3480         cv.visitLabel(l0);
3481 
3482         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
3483 
3484         cv.visitJumpInsn(IFNE, l2);
3485 
3486         visitConstantExpression(ConstantExpression.FALSE);
3487         cv.visitLabel(l1);
3488     }
3489 
3490     // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for
3491     // consistancy.
3492     protected void evaluateLogicalAndExpression(BinaryExpression expression) {
3493         visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
3494         Label l0 = new Label();
3495         cv.visitJumpInsn(IFEQ, l0);
3496 
3497         visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
3498 
3499         cv.visitJumpInsn(IFEQ, l0);
3500 
3501         visitConstantExpression(ConstantExpression.TRUE);
3502 
3503         Label l1 = new Label();
3504         cv.visitJumpInsn(GOTO, l1);
3505         cv.visitLabel(l0);
3506 
3507         visitConstantExpression(ConstantExpression.FALSE);
3508 
3509         cv.visitLabel(l1);
3510     }
3511 
3512     protected void evaluateBinaryExpression(String method, BinaryExpression expression) {
3513         Expression leftExpression = expression.getLeftExpression();
3514         leftHandExpression = false;
3515         leftExpression.visit(this);
3516         cv.visitLdcInsn(method);
3517         leftHandExpression = false;
3518         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}).visit(this);
3519         // expression.getRightExpression().visit(this);
3520         invokeMethodMethod.call(cv);
3521     }
3522 
3523     protected void evaluateCompareTo(BinaryExpression expression) {
3524         Expression leftExpression = expression.getLeftExpression();
3525         leftHandExpression = false;
3526         leftExpression.visit(this);
3527         expression.getRightExpression().visit(this);
3528         compareToMethod.call(cv);
3529     }
3530 
3531     protected void evaluateBinaryExpressionWithAsignment(String method, BinaryExpression expression) {
3532         Expression leftExpression = expression.getLeftExpression();
3533         if (leftExpression instanceof BinaryExpression) {
3534             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
3535             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
3536                 // lets replace this assignment to a subscript operator with a
3537                 // method call
3538                 // e.g. x[5] += 10
3539                 // -> (x, [], 5), =, x[5] + 10
3540                 // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
3541 
3542                 MethodCallExpression methodCall =
3543                     new MethodCallExpression(
3544                         expression.getLeftExpression(),
3545                         method,
3546                         new ArgumentListExpression(new Expression[] { expression.getRightExpression()}));
3547 
3548                 Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression());
3549 
3550                 visitMethodCallExpression(
3551                     new MethodCallExpression(
3552                         leftBinExpr.getLeftExpression(),
3553                         "putAt",
3554                         new ArgumentListExpression(new Expression[] { safeIndexExpr, methodCall })));
3555                 //cv.visitInsn(POP);
3556                 return;
3557             }
3558         }
3559 
3560         evaluateBinaryExpression(method, expression);
3561 
3562         // br to leave a copy of rvalue on the stack. see also isPopRequired()
3563         cv.visitInsn(DUP);
3564 
3565         leftHandExpression = true;
3566         evaluateExpression(leftExpression);
3567         leftHandExpression = false;
3568     }
3569 
3570     private void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression bin) {
3571         if (ENABLE_EARLY_BINDING && true) {
3572             evalBinaryExp_EarlyBinding(compareMethod, bin);
3573         }
3574         else {
3575             evalBinaryExp_LateBinding(compareMethod, bin);
3576         }
3577     }
3578 
3579     protected void evalBinaryExp_LateBinding(MethodCaller compareMethod, BinaryExpression expression) {
3580         Expression leftExp = expression.getLeftExpression();
3581         Expression rightExp = expression.getRightExpression();
3582         load(leftExp);
3583         load(rightExp);
3584         compareMethod.call(cv);
3585     }
3586 
3587     /***
3588      * note: leave the primitive boolean on staock for comparison expressions. All the result types need to match the
3589      * utility methods in the InvokerHelper.
3590      * @param compareMethod
3591      * @param expression
3592      */
3593     protected void evalBinaryExp_EarlyBinding(MethodCaller compareMethod, BinaryExpression expression) {
3594         Expression leftExp = expression.getLeftExpression();
3595         Expression rightExp = expression.getRightExpression();
3596 
3597         expression.resolve(this);
3598         if (expression.isResolveFailed() || expression.getTypeClass() == null){
3599             evalBinaryExp_LateBinding(compareMethod, expression);
3600             return;
3601         }
3602         else {
3603             Class lclass = leftExp.getTypeClass();
3604             Class rclass = rightExp.getTypeClass();
3605             if (lclass == null || rclass == null) {
3606                 if ((lclass == null && rclass != null) || (lclass != null && rclass == null)) {
3607                     // lets treat special cases: obj == null / obj != null . leave primitive boolean on the stack, which will be boxed by visitAndAutoBox()
3608                     if (leftExp == ConstantExpression.NULL && !rclass.isPrimitive() ||
3609                             rightExp == ConstantExpression.NULL && !lclass.isPrimitive()) {
3610                         Expression exp = leftExp == ConstantExpression.NULL? rightExp : leftExp;
3611                         int type = expression.getOperation().getType();
3612                         switch (type) {
3613                             case Types.COMPARE_EQUAL :
3614                                 load(exp);
3615                                 cv.visitInsn(ICONST_1);
3616                                 cv.visitInsn(SWAP);
3617                                 Label l1 = new Label();
3618                                 cv.visitJumpInsn(IFNULL, l1);
3619                                 cv.visitInsn(POP);
3620                                 cv.visitInsn(ICONST_0);
3621                                 cv.visitLabel(l1);
3622                                 return;
3623                             case Types.COMPARE_NOT_EQUAL :
3624                                 load(exp);
3625                                 cv.visitInsn(ICONST_1);
3626                                 cv.visitInsn(SWAP);
3627                                 Label l2 = new Label();
3628                                 cv.visitJumpInsn(IFNONNULL, l2);
3629                                 cv.visitInsn(POP);
3630                                 cv.visitInsn(ICONST_0);
3631                                 cv.visitLabel(l2);
3632                                 return;
3633                             default:
3634                                 evalBinaryExp_LateBinding(compareMethod, expression);
3635                                 return;
3636                         }
3637                     }
3638                     else {
3639                         evalBinaryExp_LateBinding(compareMethod, expression);
3640                         return;
3641                     }
3642                 }
3643                 else {
3644                     evalBinaryExp_LateBinding(compareMethod, expression);
3645                     return;
3646                 }
3647             }
3648             else if (lclass == String.class && rclass == String.class) {
3649                 int type = expression.getOperation().getType();
3650                 switch (type) {
3651                     case Types.COMPARE_EQUAL : // ==
3652                         load(leftExp); cast(String.class);
3653                         load(rightExp); cast(String.class);
3654                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z");
3655                         //helper.quickBoxIfNecessary(boolean.class);
3656                         return;
3657                     case Types.COMPARE_NOT_EQUAL :
3658                         load(leftExp);cast(String.class);
3659                         load(rightExp); cast(String.class);
3660                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z");
3661                         cv.visitInsn(ICONST_1);
3662                         cv.visitInsn(IXOR);
3663                         //helper.quickBoxIfNecessary(boolean.class);
3664                         return;
3665                     case Types.COMPARE_TO :
3666                         load(leftExp);cast(String.class);
3667                         load(rightExp); cast(String.class);
3668                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I");
3669                         helper.quickBoxIfNecessary(int.class); // object type
3670                         return;
3671                     case Types.COMPARE_GREATER_THAN :
3672                     case Types.COMPARE_GREATER_THAN_EQUAL :
3673                     case Types.COMPARE_LESS_THAN :
3674                     case Types.COMPARE_LESS_THAN_EQUAL :
3675                         {
3676                             int op;
3677                             switch (type) {
3678                                 case Types.COMPARE_GREATER_THAN :
3679                                     op = IFLE;
3680                                     break;
3681                                 case Types.COMPARE_GREATER_THAN_EQUAL :
3682                                     op = IFLT;
3683                                     break;
3684                                 case Types.COMPARE_LESS_THAN :
3685                                     op = IFGE;
3686                                     break;
3687                                 case Types.COMPARE_LESS_THAN_EQUAL :
3688                                     op = IFGT;
3689                                     break;
3690                                 default:
3691                                     System.err.println("flow control error: should not be here. type: " + type);
3692                                     return;
3693                             }
3694                             load(leftExp);cast(String.class);
3695                             load(rightExp); cast(String.class);
3696                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I");
3697 
3698                             // set true/false on stack
3699                             Label l4 = new Label();
3700                             cv.visitJumpInsn(op, l4);
3701                             // need to use primitive boolean //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
3702                             cv.visitInsn(ICONST_1);  // true
3703                             Label l5 = new Label();
3704                             cv.visitJumpInsn(GOTO, l5);
3705                             cv.visitLabel(l4);
3706                             cv.visitInsn(ICONST_0); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
3707                             cv.visitLabel(l5);
3708                         }
3709                         return;
3710 
3711                     default:
3712                         evalBinaryExp_LateBinding(compareMethod, expression);
3713                         return;
3714                 }
3715             }
3716             else if (Integer.class == lclass && Integer.class == rclass) {
3717                 int type = expression.getOperation().getType();
3718                 switch (type) {
3719                     case Types.COMPARE_EQUAL : // ==
3720                         load(leftExp); cast(Integer.class);
3721                         load(rightExp);
3722                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z");
3723                         //helper.quickBoxIfNecessary(boolean.class);
3724                         return;
3725                     case Types.COMPARE_NOT_EQUAL :
3726                         load(leftExp); cast(Integer.class);
3727                         load(rightExp);
3728                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z");
3729                         cv.visitInsn(ICONST_1);
3730                         cv.visitInsn(IXOR);
3731                         //helper.quickBoxIfNecessary(boolean.class);
3732                         return;
3733                     case Types.COMPARE_TO :
3734                         load(leftExp); cast(Integer.class);
3735                         load(rightExp);
3736                         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I");
3737                         helper.quickBoxIfNecessary(int.class);
3738                         return;
3739                     case Types.COMPARE_GREATER_THAN :
3740                     case Types.COMPARE_GREATER_THAN_EQUAL :
3741                     case Types.COMPARE_LESS_THAN :
3742                     case Types.COMPARE_LESS_THAN_EQUAL :
3743                         {
3744                             int op;
3745                             switch (type) {
3746                                 case Types.COMPARE_GREATER_THAN :
3747                                     op = IFLE;
3748                                     break;
3749                                 case Types.COMPARE_GREATER_THAN_EQUAL :
3750                                     op = IFLT;
3751                                     break;
3752                                 case Types.COMPARE_LESS_THAN :
3753                                     op = IFGE;
3754                                     break;
3755                                 case Types.COMPARE_LESS_THAN_EQUAL :
3756                                     op = IFGT;
3757                                     break;
3758                                 default:
3759                                     System.err.println("flow control error: should not be here. type: " + type);
3760                                     return;
3761                             }
3762                             load(leftExp); cast(Integer.class);
3763                             load(rightExp);
3764                             cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I");
3765 
3766                             Label l4 = new Label();
3767                             cv.visitJumpInsn(op, l4);
3768                             cv.visitInsn(ICONST_1); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
3769                             Label l5 = new Label();
3770                             cv.visitJumpInsn(GOTO, l5);
3771                             cv.visitLabel(l4);
3772                             cv.visitInsn(ICONST_0);//cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
3773                             cv.visitLabel(l5);
3774                         }
3775                         return;
3776 
3777                     default:
3778                         evalBinaryExp_LateBinding(compareMethod, expression);
3779                         return;
3780                 }
3781             }
3782             else {
3783                 evalBinaryExp_LateBinding(compareMethod, expression);
3784                 return;
3785             }
3786         }
3787     }
3788 
3789     private void cast(Class aClass) {
3790         if (!aClass.isPrimitive() && aClass != Object.class) {
3791             cv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(aClass.getName()));
3792         }
3793     }
3794 
3795     protected void evaluateEqual(BinaryExpression expression) {
3796         if (ENABLE_EARLY_BINDING) {
3797             expression.resolve(this);
3798             if (expression.isTypeResolved()) {
3799                 if (expression.getRightExpression().getTypeClass() == Void.TYPE) {
3800                     throwException("void value appeared on right hand side of assignment. ");
3801                 }
3802             }
3803         }
3804 
3805         Expression leftExpression = expression.getLeftExpression();
3806         if (leftExpression instanceof BinaryExpression) {
3807             BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
3808             if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
3809                 // lets replace this assignment to a subscript operator with a
3810                 // method call
3811                 // e.g. x[5] = 10
3812                 // -> (x, [], 5), =, 10
3813                 // -> methodCall(x, "putAt", [5, 10])
3814                 do {
3815                     if (true && ENABLE_EARLY_BINDING){
3816                         Class typeclass = leftBinExpr.getLeftExpression().getTypeClass();
3817                         if (typeclass == null) {
3818                             break;
3819                         }
3820 
3821                         if (typeclass == Map.class) {// call aMap.put()
3822                             load(expression.getRightExpression());
3823                             // let's leave a copy of the value on the stack.
3824                             cv.visitInsn(DUP);
3825                             final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType());
3826                             // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType());
3827                             final Class rclass = expression.getRightExpression().getTypeClass();
3828                             BytecodeExpression loadTempByteCode = new BytecodeExpression() {
3829                                 public void visit(GroovyCodeVisitor visitor) {
3830                                     cv.visitVarInsn(ALOAD, rightTemp.getIndex());
3831                                 }
3832                                 protected void resolveType(AsmClassGenerator2 resolver) {
3833                                     setTypeClass(rclass);
3834                                 }
3835                             };
3836 
3837                             visitMethodCallExpression(
3838                                     new MethodCallExpression(
3839                                             leftBinExpr.getLeftExpression(),
3840                                             "put",
3841                                             new ArgumentListExpression(
3842                                                     new Expression[] {
3843                                                         leftBinExpr.getRightExpression(),
3844                                                         loadTempByteCode})));
3845                             cv.visitInsn(POP); // pop the put method return
3846                             removeVar(rightTemp);
3847                             return;
3848                         }
3849                         else if (typeclass == List.class){
3850                             // call DefaultGroovyMethods.putAt()V
3851                             // DefaultGroovyMethods.putAt(x, 5, "c"); this is faster thangoing thru metaclass
3852                             // this method does not return any value. so indicate this fact in the expression
3853 
3854                             load(expression.getRightExpression());
3855                             // let's leave a copy of the value on the stack. this is really lazy.
3856                             cv.visitInsn(DUP);
3857                             final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType());
3858                             // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType());
3859                             final Class rclass = expression.getRightExpression().getTypeClass();
3860                             BytecodeExpression loadTempBytes = new BytecodeExpression() {
3861                                 public void visit(GroovyCodeVisitor visitor) {
3862                                     cv.visitVarInsn(ALOAD, rightTemp.getIndex());
3863                                 }
3864                                 protected void resolveType(AsmClassGenerator2 resolver) {
3865                                     setTypeClass(rclass);
3866                                 }
3867                             };
3868 
3869                             visitMethodCallExpression(
3870                                 new MethodCallExpression(
3871                                     new ClassExpression(DefaultGroovyMethods.class),
3872                                     "putAt",
3873                                     new ArgumentListExpression(
3874                                         new Expression[] {
3875                                             leftBinExpr.getLeftExpression(),
3876                                             leftBinExpr.getRightExpression(),
3877                                             loadTempBytes })));
3878                             removeVar(rightTemp);
3879                             return;
3880 
3881                         }
3882                         else {
3883                             break;
3884                         }
3885                     }
3886                 } while (false);
3887 
3888                 visitMethodCallExpression(
3889                     new MethodCallExpression(
3890                         leftBinExpr.getLeftExpression(),
3891                         "putAt",
3892                         new ArgumentListExpression(
3893                             new Expression[] { leftBinExpr.getRightExpression(), expression.getRightExpression()})));
3894                  // cv.visitInsn(POP); //this is realted to isPopRequired()
3895                 return;
3896             }
3897         }
3898 
3899         // lets evaluate the RHS then hopefully the LHS will be a field
3900         leftHandExpression = false;
3901         Expression rightExpression = expression.getRightExpression();
3902 
3903         String type = getLHSType(leftExpression);
3904         if (type != null) {
3905             //System.out.println("### expression: " + leftExpression);
3906             //System.out.println("### type: " + type);
3907 
3908             // lets not cast for primitive types as we handle these in field setting etc
3909             if (BytecodeHelper.isPrimitiveType(type)) {
3910                 rightExpression.visit(this);
3911             }
3912             else {
3913                 if (ENABLE_EARLY_BINDING) {
3914                     if (leftExpression.isDynamic()) { // br the previous if() probably should check this too!
3915                         visitAndAutoboxBoolean(rightExpression);
3916                     }
3917                     else {
3918                         if (type.equals(rightExpression.getType())) {
3919                             visitAndAutoboxBoolean(rightExpression);
3920                         }
3921                         else {
3922                             if (rightExpression instanceof ConstantExpression &&
3923                                     ((ConstantExpression)rightExpression).getValue() == null) {
3924                                 cv.visitInsn(ACONST_NULL);
3925                             }
3926                             else {
3927                                 visitCastExpression(new CastExpression(type, rightExpression));
3928                             }
3929                         }
3930                     }
3931                 }
3932                 else if (!type.equals("java.lang.Object")){
3933                     visitCastExpression(new CastExpression(type, rightExpression));
3934                 }
3935                 else {
3936                     visitAndAutoboxBoolean(rightExpression);
3937                 }
3938             }
3939         }
3940         else {
3941             visitAndAutoboxBoolean(rightExpression);
3942         }
3943 
3944 
3945         // br: attempt to pass type info from right to left for assignment
3946         if (ENABLE_EARLY_BINDING) {
3947             Class rc = rightExpression.getTypeClass();
3948             if (rc != null && rc.isArray()) {
3949                 Class elemType = rc.getComponentType();
3950                 if (elemType.isPrimitive()) {
3951                     visitClassExpression(new ClassExpression(elemType));
3952                     convertPrimitiveArray.call(cv);
3953                     cast(loadClass(BytecodeHelper.getObjectArrayTypeForPrimitiveArray(elemType.getName() + "[]")));
3954                 }
3955             }
3956 
3957 
3958             if (leftExpression.isDynamic() ) {
3959                 // propagate the type from right to left if the left is dynamic
3960                 if (!(leftExpression instanceof FieldExpression ) && !(leftExpression instanceof PropertyExpression))
3961                     copyTypeClass(leftExpression, rightExpression);
3962             }
3963             else {
3964                 Class lc = leftExpression.getTypeClass();
3965 //                Class rc = rightExpression.getTypeClass();
3966                 if (lc != null && rc != null && !lc.isAssignableFrom(rc) && !lc.isPrimitive()) {
3967                     // let's use extended conversion logic in the invoker class.
3968                     if (!lc.isArray()) {
3969                         visitClassExpression(new ClassExpression(lc));
3970                         asTypeMethod.call(cv);
3971                         helper.doCast(lc);
3972                     }
3973                     else {
3974                         // may not need this, since variable type converts primitive array to object array automatically
3975                         Class elemType = lc.getComponentType();
3976                         if (elemType.isPrimitive()) {
3977                             // let's allow type copy for primitive array, meaning [i can be changed to [Integer
3978                             copyTypeClass(leftExpression, rightExpression);
3979                         }
3980                     }
3981                 }
3982             }
3983         }
3984         cv.visitInsn(DUP);  // to leave a copy of the rightexpression value on the stack after the assignment.
3985         leftHandExpression = true;
3986         leftExpression.visit(this);
3987         leftHandExpression = false;
3988     }
3989 
3990     private void copyTypeClass(Expression leftExpression, Expression rightExpression) {
3991         // copy type class from the right to the left, boxing numbers & treat ClassExpression specially
3992         Class rclass = rightExpression.getTypeClass();
3993         if (rightExpression instanceof ClassExpression) {
3994             leftExpression.setTypeClass(Class.class);
3995         }
3996         else {
3997             rclass = BytecodeHelper.boxOnPrimitive(rclass);
3998             leftExpression.setTypeClass(rclass);
3999         }
4000     }
4001 
4002     private boolean canBeAssignedFrom(String ltype, String rtype) {
4003         if (rtype == null) {
4004             return false;
4005         }
4006         else if (ltype == null || ltype.equals("java.lang.Object")) {
4007             return true;
4008         } else {
4009             return false;
4010         }
4011     }
4012 
4013     private boolean canBeAssignedFrom(Expression l, Expression r) {
4014             if (r.getTypeClass() == null) {
4015                 return false;
4016             }
4017             else if (l.isDynamic()){
4018                 return true;
4019             } else {
4020                 return false;
4021             }
4022         }
4023     private boolean canBeAssignedFrom(Class l, Class r) {
4024             if (r == null) {
4025                 return false;
4026             }
4027             else if (l == null || l == Object.class){
4028                 return true;
4029             } else {
4030                 return false;
4031             }
4032         }
4033 
4034     /***
4035      * Deduces the type name required for some casting
4036      *
4037      * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
4038      */
4039     protected String getLHSType(Expression leftExpression) {
4040         do {
4041 // commented out. not quiteworking yet. would complain something like:
4042 //java.lang.ClassFormatError: Foo$1 (Illegal Field name "class$[Ljava$lang$String;")
4043 //
4044 //            if (ENABLE_EARLY_BINDING) {
4045 //                String type = leftExpression.getType();
4046 //                if (type == null)
4047 //                    break;
4048 //                return isValidTypeForCast(type) ? type : null;
4049 //            }
4050         } while (false);
4051 
4052         if (leftExpression instanceof VariableExpression) {
4053             VariableExpression varExp = (VariableExpression) leftExpression;
4054             String type = varExp.getType();
4055             if (isValidTypeForCast(type)) {
4056                 return type;
4057             }
4058             String variableName = varExp.getVariable();
4059             Variable variable = (Variable) variableStack.get(variableName);
4060             if (variable != null) {
4061                 if (variable.isHolder() || variable.isProperty()) {
4062                     return null;
4063                 }
4064                 type = variable.getTypeName();
4065                 if (isValidTypeForCast(type)) {
4066                     return type;
4067                 }
4068             }
4069             else {
4070                 FieldNode field = classNode.getField(variableName);
4071                 if (field == null) {
4072                     field = classNode.getOuterField(variableName);
4073                 }
4074                 if (field != null) {
4075                     type = field.getType();
4076                     if (!field.isHolder() && isValidTypeForCast(type)) {
4077                         return type;
4078                     }
4079                 }
4080             }
4081         }
4082         return null;
4083     }
4084 
4085     protected boolean isValidTypeForCast(String type) {
4086         return type != null && !type.equals("java.lang.Object") && !type.equals("groovy.lang.Reference") && !BytecodeHelper.isPrimitiveType(type);
4087     }
4088 
4089     protected void visitAndAutoboxBoolean(Expression expression) {
4090         expression.visit(this);
4091 
4092         if (isComparisonExpression(expression)) {
4093             helper.boxBoolean(); // convert boolean to Boolean
4094         }
4095     }
4096 
4097     protected void evaluatePrefixMethod(String method, Expression expression) {
4098         if (isNonStaticField(expression) && ! isHolderVariable(expression) && !isStaticMethod()) {
4099             cv.visitVarInsn(ALOAD, 0);
4100         }
4101         expression.visit(this);
4102         cv.visitLdcInsn(method);
4103         invokeNoArgumentsMethod.call(cv);
4104 
4105         leftHandExpression = true;
4106         expression.visit(this);
4107         leftHandExpression = false;
4108         expression.visit(this);
4109     }
4110 
4111     protected void evaluatePostfixMethod(String method, Expression expression) {
4112         leftHandExpression = false;
4113         expression.visit(this);
4114 
4115         Variable tv = visitASTOREInTemp("postfix_" + method);
4116         int tempIdx  = tv.getIndex();
4117         cv.visitVarInsn(ALOAD, tempIdx);
4118 
4119         cv.visitLdcInsn(method);
4120         invokeNoArgumentsMethod.call(cv);
4121 
4122         store(expression);
4123 
4124         cv.visitVarInsn(ALOAD, tempIdx);
4125         removeVar(tv);
4126     }
4127 
4128     protected boolean isHolderVariable(Expression expression) {
4129         if (expression instanceof FieldExpression) {
4130             FieldExpression fieldExp = (FieldExpression) expression;
4131             return fieldExp.getField().isHolder();
4132         }
4133         if (expression instanceof VariableExpression) {
4134             VariableExpression varExp = (VariableExpression) expression;
4135             Variable variable = (Variable) variableStack.get(varExp.getVariable());
4136             if (variable != null) {
4137                 return variable.isHolder();
4138             }
4139             FieldNode field = classNode.getField(varExp.getVariable());
4140             if (field != null) {
4141                 return field.isHolder();
4142             }
4143         }
4144         return false;
4145     }
4146 
4147     protected void evaluateInstanceof(BinaryExpression expression) {
4148         expression.getLeftExpression().visit(this);
4149         Expression rightExp = expression.getRightExpression();
4150         String className = null;
4151         if (rightExp instanceof ClassExpression) {
4152             ClassExpression classExp = (ClassExpression) rightExp;
4153             className = classExp.getType();
4154         }
4155         else {
4156             throw new RuntimeException(
4157                 "Right hand side of the instanceof keyworld must be a class name, not: " + rightExp);
4158         }
4159         className = checkValidType(className, expression, "Must be a valid type name for an instanceof statement");
4160         String classInternalName = BytecodeHelper.getClassInternalName(className);
4161         cv.visitTypeInsn(INSTANCEOF, classInternalName);
4162     }
4163 
4164     /***
4165      * @return true if the given argument expression requires the stack, in
4166      *         which case the arguments are evaluated first, stored in the
4167      *         variable stack and then reloaded to make a method call
4168      */
4169     protected boolean argumentsUseStack(Expression arguments) {
4170         return arguments instanceof TupleExpression || arguments instanceof ClosureExpression;
4171     }
4172 
4173     /***
4174      * @return true if the given expression represents a non-static field
4175      */
4176     protected boolean isNonStaticField(Expression expression) {
4177         FieldNode field = null;
4178         if (expression instanceof VariableExpression) {
4179             VariableExpression varExp = (VariableExpression) expression;
4180             field = classNode.getField(varExp.getVariable());
4181         }
4182         else if (expression instanceof FieldExpression) {
4183             FieldExpression fieldExp = (FieldExpression) expression;
4184             field = classNode.getField(fieldExp.getFieldName());
4185         }
4186         else if (expression instanceof PropertyExpression) {
4187             PropertyExpression fieldExp = (PropertyExpression) expression;
4188             field = classNode.getField(fieldExp.getProperty());
4189         }
4190         if (field != null) {
4191             return !field.isStatic();
4192         }
4193         return false;
4194     }
4195 
4196     protected boolean isThisExpression(Expression expression) {
4197         if (expression instanceof VariableExpression) {
4198             VariableExpression varExp = (VariableExpression) expression;
4199             return varExp.getVariable().equals("this");
4200         }
4201         return false;
4202     }
4203 
4204     /***
4205      * For assignment expressions, return a safe expression for the LHS we can use
4206      * to return the value
4207      */
4208     protected Expression createReturnLHSExpression(Expression expression) {
4209         if (expression instanceof BinaryExpression) {
4210             BinaryExpression binExpr = (BinaryExpression) expression;
4211             if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
4212                 return createReusableExpression(binExpr.getLeftExpression());
4213             }
4214         }
4215         return null;
4216     }
4217 
4218     protected Expression createReusableExpression(Expression expression) {
4219         ExpressionTransformer transformer = new ExpressionTransformer() {
4220             public Expression transform(Expression expression) {
4221                 if (expression instanceof PostfixExpression) {
4222                     PostfixExpression postfixExp = (PostfixExpression) expression;
4223                     return postfixExp.getExpression();
4224                 }
4225                 else if (expression instanceof PrefixExpression) {
4226                     PrefixExpression prefixExp = (PrefixExpression) expression;
4227                     return prefixExp.getExpression();
4228                 }
4229                 return expression;
4230             }
4231         };
4232 
4233         // could just be a postfix / prefix expression or nested inside some other expression
4234         return transformer.transform(expression.transformExpression(transformer));
4235     }
4236 
4237     protected boolean isComparisonExpression(Expression expression) {
4238         if (expression instanceof BinaryExpression) {
4239             BinaryExpression binExpr = (BinaryExpression) expression;
4240             switch (binExpr.getOperation().getType()) {
4241                 case Types.COMPARE_EQUAL :
4242                 case Types.MATCH_REGEX :
4243                 case Types.COMPARE_GREATER_THAN :
4244                 case Types.COMPARE_GREATER_THAN_EQUAL :
4245                 case Types.COMPARE_LESS_THAN :
4246                 case Types.COMPARE_LESS_THAN_EQUAL :
4247                 case Types.COMPARE_IDENTICAL :
4248                 case Types.COMPARE_NOT_EQUAL :
4249                 case Types.KEYWORD_INSTANCEOF :
4250                     return true;
4251             }
4252         }
4253         else if (expression instanceof BooleanExpression) {
4254             return true;
4255         }
4256         return false;
4257     }
4258 
4259     protected void onLineNumber(ASTNode statement, String message) {
4260         int line = statement.getLineNumber();
4261         int col = statement.getColumnNumber();
4262         this.currentASTNode = statement;
4263 
4264         if (line >=0) {
4265             lineNumber = line;
4266             columnNumber = col;
4267         }
4268         if (CREATE_DEBUG_INFO && line >= 0 && cv != null) {
4269             Label l = new Label();
4270             cv.visitLabel(l);
4271             cv.visitLineNumber(line, l);
4272             if (ASM_DEBUG) {
4273                 helper.mark(message + "[" + statement.getLineNumber() + ":" + statement.getColumnNumber() + "]");
4274             }
4275         }
4276     }
4277 
4278     protected VariableScope getVariableScope() {
4279         if (variableScope == null) {
4280             if (methodNode != null) {
4281                 // if we're a closure method we'll have our variable scope already created
4282                 variableScope = methodNode.getVariableScope();
4283                 if (variableScope == null) {
4284                     variableScope = new VariableScope();
4285                     methodNode.setVariableScope(variableScope);
4286                     VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
4287                     visitor.setParameters(methodNode.getParameters());
4288                     Statement code = methodNode.getCode();
4289                     if (code != null) {
4290                         code.visit(visitor);
4291                     }
4292                 }
4293                 addFieldsToVisitor(variableScope);
4294             }
4295             else if (constructorNode != null) {
4296                 variableScope = new VariableScope();
4297                 constructorNode.setVariableScope(variableScope);
4298                 VariableScopeCodeVisitor visitor = new VariableScopeCodeVisitor(variableScope);
4299                 visitor.setParameters(constructorNode.getParameters());
4300                 Statement code = constructorNode.getCode();
4301                 if (code != null) {
4302                     code.visit(visitor);
4303                 }
4304                 addFieldsToVisitor(variableScope);
4305             }
4306             else {
4307                 throw new RuntimeException("Can't create a variable scope outside of a method or constructor");
4308             }
4309         }
4310         return variableScope;
4311     }
4312 
4313     /***
4314      * @return a list of parameters for each local variable which needs to be
4315      *         passed into a closure
4316      */
4317     protected Parameter[] getClosureSharedVariables(ClosureExpression expression) {
4318         List vars = new ArrayList();
4319 
4320         //
4321         // First up, get the scopes for outside and inside the closure.
4322         // The inner scope must cover all nested closures, as well, as
4323         // everything that will be needed must be imported.
4324 
4325         VariableScope outerScope = getVariableScope().createRecursiveParentScope();
4326         VariableScope innerScope = expression.getVariableScope();
4327         if (innerScope == null) {
4328             System.out.println(
4329                 "No variable scope for: " + expression + " method: " + methodNode + " constructor: " + constructorNode);
4330             innerScope = new VariableScope(getVariableScope());
4331         }
4332         else {
4333             innerScope = innerScope.createRecursiveChildScope();
4334         }
4335 
4336 
4337         //
4338         // DeclaredVariables include any name that was assigned to within
4339         // the scope.  ReferencedVariables include any name that was read
4340         // from within the scope.  We get the sets from each and must piece
4341         // together the stack variable import list for the closure.  Note
4342         // that we don't worry about field variables here, as we don't have
4343         // to do anything special with them.  Stack variables, on the other
4344         // hand, have to be wrapped up in References for use.
4345 
4346         Set outerDecls = outerScope.getDeclaredVariables();
4347         Set outerRefs  = outerScope.getReferencedVariables();
4348         Set innerDecls = innerScope.getDeclaredVariables();
4349         Set innerRefs  = innerScope.getReferencedVariables();
4350 
4351 
4352         //
4353         // So, we care about any name referenced in the closure UNLESS:
4354         //   1) it's not declared in the outer context;
4355         //   2) it's a parameter;
4356         //   3) it's a field in the context class that isn't overridden
4357         //      by a stack variable in the outer context.
4358         //
4359         // BUG: We don't actually have the necessary information to do
4360         //      this right!  The outer declarations don't distinguish
4361         //      between assignments and variable declarations.  Therefore
4362         //      we can't tell when field variables have been overridden
4363         //      by stack variables in the outer context.  This must
4364         //      be fixed!
4365 
4366         Set varSet = new HashSet();
4367         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
4368             String var = (String) iter.next();
4369             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
4370             if (outerDecls.contains(var) && (isNotFieldOfOutermostClass(var))) {
4371                 String type = getVariableType(var);
4372                 vars.add(new Parameter(type, var));
4373                 varSet.add(var);
4374             }
4375         }
4376         for (Iterator iter = outerRefs.iterator(); iter.hasNext();) {
4377             String var = (String) iter.next();
4378             // lets not pass in fields from the most-outer class, but pass in values from an outer closure
4379             if (innerDecls.contains(var) && (isNotFieldOfOutermostClass(var)) && !varSet.contains(var)) {
4380                 String type = getVariableType(var);
4381                 vars.add(new Parameter(type, var));
4382             }
4383         }
4384 
4385 
4386         Parameter[] answer = new Parameter[vars.size()];
4387         vars.toArray(answer);
4388         return answer;
4389     }
4390 
4391     protected boolean isNotFieldOfOutermostClass(String var) {
4392         //return classNode.getField(var) == null || isInnerClass();
4393         return getOutermostClass().getField(var) == null;
4394     }
4395 
4396     protected void findMutableVariables() {
4397         /*
4398         VariableScopeCodeVisitor outerVisitor = new VariableScopeCodeVisitor(true);
4399         node.getCode().visit(outerVisitor);
4400 
4401         addFieldsToVisitor(outerVisitor);
4402 
4403         VariableScopeCodeVisitor innerVisitor = outerVisitor.getClosureVisitor();
4404         */
4405         VariableScope outerScope = getVariableScope();
4406 
4407         // lets create a scope concatenating all the closure expressions
4408         VariableScope innerScope = outerScope.createCompositeChildScope();
4409 
4410         Set outerDecls = outerScope.getDeclaredVariables();
4411         Set outerRefs = outerScope.getReferencedVariables();
4412         Set innerDecls = innerScope.getDeclaredVariables();
4413         Set innerRefs = innerScope.getReferencedVariables();
4414 
4415         mutableVars.clear();
4416 
4417         for (Iterator iter = innerDecls.iterator(); iter.hasNext();) {
4418             String var = (String) iter.next();
4419             if ((outerDecls.contains(var) || outerRefs.contains(var)) && classNode.getField(var) == null) {
4420                 mutableVars.add(var);
4421             }
4422         }
4423 
4424         // we may call the closure twice and modify the variable in the outer scope
4425         // so for now lets assume that all variables are mutable
4426         for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
4427             String var = (String) iter.next();
4428             if (outerDecls.contains(var) && classNode.getField(var) == null) {
4429                 mutableVars.add(var);
4430             }
4431         }
4432 
4433         //                System.out.println();
4434         //                System.out.println("method: " + methodNode + " classNode: " + classNode);
4435         //                System.out.println("child scopes: " + outerScope.getChildren());
4436         //                System.out.println("outerDecls: " + outerDecls);
4437         //                System.out.println("outerRefs: " + outerRefs);
4438         //                System.out.println("innerDecls: " + innerDecls);
4439         //                System.out.println("innerRefs: " + innerRefs);
4440     }
4441 
4442     protected void addFieldsToVisitor(VariableScope scope) {
4443         for (Iterator iter = classNode.getFields().iterator(); iter.hasNext();) {
4444             FieldNode field = (FieldNode) iter.next();
4445             String name = field.getName();
4446 
4447             scope.getDeclaredVariables().add(name);
4448             scope.getReferencedVariables().add(name);
4449         }
4450     }
4451 
4452     private boolean isInnerClass() {
4453         return classNode instanceof InnerClassNode;
4454     }
4455 
4456     protected String getVariableType(String name) {
4457         Variable variable = (Variable) variableStack.get(name);
4458         if (variable != null) {
4459             return variable.getTypeName();
4460         }
4461         return null;
4462     }
4463 
4464     protected void resetVariableStack(Parameter[] parameters) {
4465         lastVariableIndex = -1;
4466         variableStack.clear();
4467 
4468         scope = new BlockScope(null);
4469         //pushBlockScope();
4470 
4471         // lets push this onto the stack
4472         definingParameters = true;
4473         if (!isStaticMethod()) {
4474             defineVariable("this", classNode.getName()).getIndex();
4475         } // now lets create indices for the parameteres
4476         for (int i = 0; i < parameters.length; i++) {
4477             Parameter parameter = parameters[i];
4478             String type = parameter.getType();
4479             Variable v = defineVariable(parameter.getName(), type);
4480             int idx = v.getIndex();
4481             if (BytecodeHelper.isPrimitiveType(type)) {
4482                 helper.load(type, idx);
4483                 helper.box(type);
4484                 cv.visitVarInsn(ASTORE, idx);
4485             }
4486         }
4487         definingParameters = false;
4488     }
4489 
4490     protected void popScope() {
4491         int lastID = scope.getFirstVariableIndex();
4492 
4493         List removeKeys = new ArrayList();
4494         for (Iterator iter = variableStack.entrySet().iterator(); iter.hasNext();) {
4495             Map.Entry entry = (Map.Entry) iter.next();
4496             String name = (String) entry.getKey();
4497             Variable value = (Variable) entry.getValue();
4498             if (value.getIndex() >= lastID) {
4499                 removeKeys.add(name);
4500             }
4501         }
4502         for (Iterator iter = removeKeys.iterator(); iter.hasNext();) {
4503             Variable v  = (Variable) variableStack.remove(iter.next());
4504             if (CREATE_DEBUG_INFO) { // set localvartable
4505                 if (v != null) {
4506                     visitVariableEndLabel(v);
4507                     cv.visitLocalVariable(
4508                             v.getName(),
4509                             BytecodeHelper.getTypeDescription(v.getTypeName()),
4510                             v.getStartLabel(),
4511                             v.getEndLabel(),
4512                             v.getIndex()
4513                     );
4514                 }
4515             }
4516         }
4517         scope = scope.getParent();
4518     }
4519 
4520     void removeVar(Variable v ) {
4521     	variableStack.remove(v.getName());
4522         if (CREATE_DEBUG_INFO) { // set localvartable
4523         	Label endl = new Label();
4524         	cv.visitLabel(endl);
4525         	cv.visitLocalVariable(
4526         			v.getName(),
4527 					BytecodeHelper.getTypeDescription(v.getTypeName()),
4528 					v.getStartLabel(),
4529 					endl,
4530 					v.getIndex()
4531         	);
4532         }
4533     }
4534     private void visitVariableEndLabel(Variable v) {
4535         if (CREATE_DEBUG_INFO) {
4536             if(v.getEndLabel() == null) {
4537                 Label end = new Label();
4538                 v.setEndLabel(end);
4539             }
4540             cv.visitLabel(v.getEndLabel());
4541         }
4542     }
4543 
4544     protected void pushBlockScope() {
4545         pushBlockScope(true, true);
4546     }
4547 
4548     /***
4549      * create a new scope. Set break/continue label if the canXXX parameter is true. Otherwise
4550      * inherit parent's label.
4551      * @param canContinue   true if the start of the scope can take continue label
4552      * @param canBreak  true if the end of the scope can take break label
4553      */
4554     protected void pushBlockScope(boolean canContinue, boolean canBreak) {
4555         BlockScope parentScope = scope;
4556         scope = new BlockScope(parentScope);
4557         scope.setContinueLabel(canContinue ? new Label() : (parentScope == null ? null : parentScope.getContinueLabel()));
4558         scope.setBreakLabel(canBreak? new Label() : (parentScope == null ? null : parentScope.getBreakLabel()));
4559         scope.setFirstVariableIndex(getNextVariableID());
4560     }
4561 
4562     /***
4563      * Defines the given variable in scope and assigns it to the stack
4564      */
4565     protected Variable defineVariable(String name, String type) {
4566         return defineVariable(name, type, true);
4567     }
4568 
4569     protected Variable defineVariable(String name, String type, boolean define) {
4570         return defineVariable(name, new Type(type), define);
4571     }
4572 
4573     private Variable defineVariable(String name, Type type, boolean define) {
4574         Variable answer = (Variable) variableStack.get(name);
4575         if (answer == null) {
4576             lastVariableIndex = getNextVariableID();
4577             answer = new Variable(lastVariableIndex, type, name);
4578             if (mutableVars.contains(name)) {
4579                 answer.setHolder(true);
4580             }
4581             variableStack.put(name, answer);
4582             Label startLabel  = new Label();
4583             answer.setStartLabel(startLabel);
4584             if (define) {
4585                 if (definingParameters) {
4586                     if (answer.isHolder()) {
4587                         cv.visitTypeInsn(NEW, "groovy/lang/Reference"); // br todo to associate a label with the variable
4588                         cv.visitInsn(DUP);
4589                         cv.visitVarInsn(ALOAD, lastVariableIndex);
4590                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V");
4591                         cv.visitVarInsn(ASTORE, lastVariableIndex);
4592                         cv.visitLabel(startLabel);
4593                     }
4594                 }
4595                 else {
4596                     // using new variable inside a comparison expression
4597                     // so lets initialize it too
4598                     if (answer.isHolder() && !isInScriptBody()) {
4599                         //cv.visitVarInsn(ASTORE, lastVariableIndex + 1); // I might need this to set the reference value
4600 
4601                         cv.visitTypeInsn(NEW, "groovy/lang/Reference");
4602                         cv.visitInsn(DUP);
4603                         cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "()V");
4604 
4605                         cv.visitVarInsn(ASTORE, lastVariableIndex);
4606                         cv.visitLabel(startLabel);
4607                         //cv.visitVarInsn(ALOAD, idx + 1);
4608                     }
4609                     else {
4610                         if (!leftHandExpression) { // new var on the RHS: init with null
4611                             cv.visitInsn(ACONST_NULL);
4612                             cv.visitVarInsn(ASTORE, lastVariableIndex);
4613                             cv.visitLabel(startLabel);
4614                         }
4615                     }
4616                 }
4617             }
4618         }
4619         return answer;
4620     }
4621 
4622     private int getNextVariableID() {
4623         //return Math.max(lastVariableIndex + 1, variableStack.size());
4624         return variableStack.size(); // todo : rework
4625     }
4626 
4627     /*** @return true if the given name is a local variable or a field */
4628     protected boolean isFieldOrVariable(String name) {
4629         return variableStack.containsKey(name) || classNode.getField(name) != null;
4630     }
4631 
4632     protected Type checkValidType(Type type, ASTNode node, String message) {
4633         if (type.isDynamic()) {
4634             return type;
4635         }
4636         String name = checkValidType(type.getName(), node, message);
4637         if (type.getName().equals(name)) {
4638             return type;
4639         }
4640         return new Type(name);
4641     }
4642 
4643     protected String checkValidType(String type, ASTNode node, String message) {
4644         if (type!= null && type.length() == 0)
4645             return "java.lang.Object";
4646         if (type.endsWith("[]")) {
4647             String postfix = "[]";
4648             String prefix = type.substring(0, type.length() - 2);
4649             return checkValidType(prefix, node, message) + postfix;
4650         }
4651         int idx = type.indexOf('$');
4652         if (idx > 0) {
4653             String postfix = type.substring(idx);
4654             String prefix = type.substring(0, idx);
4655             return checkValidType(prefix, node, message) + postfix;
4656         }
4657         if (BytecodeHelper.isPrimitiveType(type) || "void".equals(type)) {
4658             return type;
4659         }
4660         String original = type;
4661         type = resolveClassName(type);
4662         if (type != null) {
4663             return type;
4664         }
4665 
4666         throw new MissingClassException(original, node, message + " for class: " + classNode.getName());
4667     }
4668 
4669     protected String resolveClassName(String type) {
4670         return classNode.resolveClassName(type);
4671     }
4672 
4673     protected String createVariableName(String type) {
4674         return "__" + type + (++tempVariableNameCounter);
4675     }
4676 
4677     /***
4678      * @return if the type of the expression can be determined at compile time
4679      *         then this method returns the type - otherwise null
4680      */
4681     protected String getExpressionType(Expression expression) {
4682         if (isComparisonExpression(expression)) {
4683             return "boolean";
4684         }
4685         if (expression instanceof VariableExpression) {
4686             VariableExpression varExpr = (VariableExpression) expression;
4687             Variable variable = (Variable) variableStack.get(varExpr.getVariable());
4688             if (variable != null && !variable.isHolder()) {
4689                 Type type = variable.getType();
4690                 if (! type.isDynamic()) {
4691                     return type.getName();
4692                 }
4693             }
4694         }
4695         return null;
4696     }
4697 
4698     /***
4699      * @return true if the value is an Integer, a Float, a Long, a Double or a
4700      *         String .
4701      */
4702     protected static boolean isPrimitiveFieldType(String type) {
4703         return type.equals("java.lang.String")
4704             || type.equals("java.lang.Integer")
4705             || type.equals("java.lang.Double")
4706             || type.equals("java.lang.Long")
4707             || type.equals("java.lang.Float");
4708     }
4709 
4710     protected boolean isInClosureConstructor() {
4711         return constructorNode != null
4712             && classNode.getOuterClass() != null
4713             && classNode.getSuperClass().equals(Closure.class.getName());
4714     }
4715 
4716     protected boolean isStaticMethod() {
4717         if (methodNode == null) { // we're in a constructor
4718             return false;
4719         }
4720         return methodNode.isStatic();
4721     }
4722 
4723     Map classCache = new HashMap();
4724     {
4725         classCache.put("int", Integer.TYPE);
4726         classCache.put("byte", Byte.TYPE);
4727         classCache.put("short", Short.TYPE);
4728         classCache.put("char", Character.TYPE);
4729         classCache.put("boolean", Boolean.TYPE);
4730         classCache.put("long", Long.TYPE);
4731         classCache.put("double", Double.TYPE);
4732         classCache.put("float", Float.TYPE);
4733         classCache.put("void", Void.TYPE);
4734     }
4735     /***
4736      * @return loads the given type name
4737      */
4738     protected Class loadClass(String name) {
4739 
4740         if (name.equals(this.classNode.getName())) {
4741             return Object.class;
4742         }
4743 
4744         if (name == null) {
4745             return null;
4746         }
4747         else if (name.length() == 0) {
4748             return Object.class;
4749         }
4750 
4751         name = BytecodeHelper.formatNameForClassLoading(name);
4752 
4753     	try {
4754     		Class cls = (Class)classCache.get(name);
4755     		if (cls != null)
4756     			return cls;
4757 
4758     		CompileUnit compileUnit = getCompileUnit();
4759             if (compileUnit != null) {
4760             	cls = compileUnit.loadClass(name);
4761                 classCache.put(name, cls);
4762             	return cls;
4763             }
4764             else {
4765                 throw new ClassGeneratorException("Could not load class: " + name);
4766             }
4767         }
4768         catch (ClassNotFoundException e) {
4769             throw new ClassGeneratorException("Error when compiling class: " + classNode.getName() + ". Reason: could not load class: " + name + " reason: " + e, e);
4770         }
4771     }
4772 
4773     protected CompileUnit getCompileUnit() {
4774         CompileUnit answer = classNode.getCompileUnit();
4775         if (answer == null) {
4776             answer = context.getCompileUnit();
4777         }
4778         return answer;
4779     }
4780 
4781     /***
4782      * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding.
4783      * @param call
4784      */
4785     public void resolve(MethodCallExpression call) {
4786         if (call.isResolveFailed()) {
4787             return;
4788         }
4789         else if (call.isTypeResolved()) {
4790             return;
4791         }
4792 
4793         Expression obj = call.getObjectExpression();
4794         String meth = call.getMethod();
4795         Class ownerClass = null;
4796         boolean isStaticCall = false;
4797         boolean isSuperCall = false;
4798 
4799         List arglist = new ArrayList();
4800         Expression args = call.getArguments();
4801         if (args instanceof TupleExpression) {
4802             TupleExpression tupleExpression = (TupleExpression) args;
4803             List argexps = tupleExpression.getExpressions();
4804             for (int i = 0; i < argexps.size(); i++) {
4805                 Expression expression = (Expression) argexps.get(i);
4806                 Class cls = expression.getTypeClass();
4807                 if (cls == null) {
4808                     call.setResolveFailed(true);
4809                     return ;
4810                 }
4811                 else {
4812                     arglist.add(cls);
4813                 }
4814             }
4815         } else if (args instanceof ClosureExpression) {
4816             call.setResolveFailed(true);
4817             return ;// todo
4818         } else {
4819             call.setResolveFailed(true);
4820             return ;
4821         }
4822 
4823 
4824         Class[] argsArray = new Class[arglist.size()];
4825         arglist.toArray(argsArray);
4826 
4827 
4828         if (obj instanceof ClassExpression) {
4829             // static call
4830             //ClassExpression cls = (ClassExpression)obj;
4831             //String type = cls.getType();
4832             ownerClass = obj.getTypeClass(); //loadClass(type);
4833             isStaticCall = true;
4834         } else if (obj instanceof VariableExpression) {
4835             VariableExpression var = (VariableExpression) obj;
4836             if (var.getVariable().equals("this") && (methodNode == null? true : !methodNode.isStatic()) ) {
4837                 isStaticCall = false;
4838                 if (methodNode != null) {
4839                     isStaticCall = Modifier.isStatic(methodNode.getModifiers());
4840                 }
4841                 MetaMethod mmeth = getMethodOfThisAndSuper(meth, argsArray, isStaticCall);
4842                 if (mmeth != null) {
4843                     call.setMethod(mmeth);
4844                     return ;
4845                 }
4846                 else {
4847                     call.setResolveFailed(true);
4848                     return ;
4849                 }
4850             }
4851             else if (var.getVariable().equals("super") ) {
4852                 isSuperCall = true;
4853                 ownerClass = var.getTypeClass();
4854             }
4855             else {
4856                 ownerClass = var.getTypeClass();
4857             }
4858         }
4859         else /*if (obj instanceof PropertyExpression)*/ {
4860             ownerClass = obj.getTypeClass();
4861             if (ownerClass == null) {
4862                 call.setResolveFailed(true);
4863                 call.setFailure("target class is null");
4864                 return ;  // take care other cases later version
4865             }
4866         }
4867 
4868         if (ownerClass == Object.class) {
4869             call.setResolveFailed(true);
4870             return ; // use late binding for dynamic types
4871         }
4872         else if (ownerClass == null)  {
4873             call.setResolveFailed(true);
4874             return ; // use dynamic dispatching for GroovyObject
4875         }
4876         else
4877             if (!isSuperCall && !isStaticCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // not optimize GroovyObject meth call for now
4878             call.setResolveFailed(true);
4879             return ;
4880         }
4881         else
4882             if (ownerClass.isPrimitive()) {
4883             call.setResolveFailed(true);
4884             return ; // todo handle primitives
4885         }
4886 
4887 
4888         //MetaMethod mmethod = InvokerHelper.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
4889         // todo is this thread safe?
4890         MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
4891         if (mmethod!= null) {
4892             call.setMethod(mmethod);
4893         }
4894         else {
4895             call.setResolveFailed(true);
4896         }
4897         return ;
4898     }
4899     /***
4900      * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding.
4901      * @param call
4902      */
4903     public void resolve(ConstructorCallExpression call) {
4904         if (call.isResolveFailed()) {
4905             return ;
4906         }
4907         else if (call.isTypeResolved()) {
4908             return ;
4909         }
4910 
4911         String declaredType = call.getTypeToSet();
4912         if (declaredType.equals(classNode.getName())) {
4913             call.setResolveFailed(true);
4914             call.setFailure("cannot resolve on the current class itself. ");
4915             return;
4916         }
4917         else {
4918             call.setType(declaredType);
4919             if (call.getTypeClass() == null) {
4920                 call.setResolveFailed(true);
4921                 call.setFailure("type name cannot be resolved. ");
4922                 return;
4923             }
4924         }
4925 
4926         boolean isSuperCall = false;
4927 
4928         List arglist = new ArrayList();
4929         Expression args = call.getArguments();
4930         if (args instanceof TupleExpression) {
4931             TupleExpression tupleExpression = (TupleExpression) args;
4932             List argexps = tupleExpression.getExpressions();
4933             for (int i = 0; i < argexps.size(); i++) {
4934                 Expression expression = (Expression) argexps.get(i);
4935                 Class cls = expression.getTypeClass();
4936                 if (cls == null) {
4937                     call.setResolveFailed(true);
4938                     return ;
4939                 }
4940                 else {
4941                     arglist.add(cls);
4942                 }
4943             }
4944         } else if (args instanceof ClosureExpression) {
4945             call.setResolveFailed(true);
4946             call.setFailure("don't know how to handle closure arg. ");
4947             return ;// todo
4948         } else {
4949             call.setResolveFailed(true);
4950             call.setFailure("unknown arg type: " + args.getClass().getName());
4951             return ;
4952         }
4953 
4954 
4955         Class[] argsArray = new Class[arglist.size()];
4956         arglist.toArray(argsArray);
4957 
4958         Class ownerClass = call.getTypeClass();
4959         if (ownerClass == null) {
4960             String typeName = call.getType();
4961             if (typeName.equals(this.classNode.getName())) {
4962                 // this is a ctor call to this class
4963                 call.setResolveFailed(true);
4964                 call.setFailure("invoke constructor for this. no optimization for now");
4965                 return ;
4966             }
4967             else {
4968                 try {
4969                     ownerClass = loadClass(typeName);
4970                     if (ownerClass == null) {
4971                         call.setResolveFailed(true);
4972                         call.setFailure("owner class type is null. ");
4973                         return ;
4974                     }
4975                 }
4976                 catch (Throwable th) {
4977                     call.setResolveFailed(true);
4978                     call.setFailure("Exception: " + th);
4979                     return ;
4980                 }
4981             }
4982         }
4983 
4984         if (ownerClass == Object.class) {
4985             call.setResolveFailed(true);
4986             call.setFailure("owner class type java.lang.Object.  use late binding for dynamic types");
4987             return ; //
4988         }
4989         else if (ownerClass == null)  {
4990             call.setResolveFailed(true);
4991             call.setFailure("owner class type is null. use dynamic dispatching for GroovyObject");
4992             return; // use dynamic dispatching for GroovyObject
4993         }
4994 // safe to call groovyobject ctor
4995 //        else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call
4996 //            call.setResolveFailed(true);
4997 //            return null;
4998 //        }
4999         else if (ownerClass.isPrimitive()) {
5000             call.setResolveFailed(true);
5001             throwException("The owner of the constructor is primitive.");
5002             return ;
5003         }
5004 
5005         Constructor ctor = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedConstructor(ownerClass, argsArray);
5006         if (ctor!= null) {
5007             call.setConstructor(ctor);
5008         }
5009         else {
5010             call.setResolveFailed(true);
5011         }
5012         return ;
5013     }
5014 
5015     public void resolve(PropertyExpression propertyExpression)  {
5016         if (propertyExpression.getTypeClass() != null)
5017             return ;
5018         if (propertyExpression.isResolveFailed())
5019             return ;
5020 
5021         Expression ownerExp = propertyExpression.getObjectExpression();
5022         Class ownerClass = ownerExp.getTypeClass();
5023         String propName = propertyExpression.getProperty();
5024         if (propName.equals("class")) {
5025             propertyExpression.setTypeClass(Class.class);
5026             return ;
5027         }
5028 
5029         // handle arraylength
5030         if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) {
5031             propertyExpression.setTypeClass(int.class);
5032             return;
5033         }
5034 
5035         if (isThisExpression(ownerExp)) {
5036             // lets use the field expression if its available
5037             if (classNode == null) {
5038                 propertyExpression.setResolveFailed(true);
5039                 return ;
5040             }
5041             FieldNode field   = null;
5042             ownerExp.setType(classNode.getName());
5043             try {
5044                 if( (field = classNode.getField(propName)) != null ) {
5045 //                    Class cls =loadClass(field.getType());
5046 //                    propertyExpression.setAccess(PropertyExpression.LOCAL_FIELD_ACCESS);
5047 //                    propertyExpression.setTypeClass(cls);
5048 // local property access. to be determined in the future
5049                     propertyExpression.setResolveFailed(true);
5050                     propertyExpression.setFailure("local property access. to be determined in the future.");
5051                     return ;
5052                 } else {
5053                     // search for super classes/interface
5054                     // interface first first
5055                     String[] interfaces = classNode.getInterfaces();
5056                     String[] supers = new String[interfaces.length + 1];
5057 
5058                     int i = 0;
5059                     for (; i < interfaces.length; i++) {
5060                         supers[i] = interfaces[i];
5061                     }
5062                     supers[i] = classNode.getSuperClass();
5063                     for (int j = 0; j < supers.length; j++) {
5064                         String aSuper = supers[j];
5065                         Class superClass = loadClass(aSuper);
5066                         Field fld  = superClass.getDeclaredField(propName);
5067                         if (fld != null && !Modifier.isPrivate(fld.getModifiers())) {
5068                             propertyExpression.setField(fld);
5069                             return ;
5070                         }
5071                     }
5072                 }
5073             } catch (Exception e) {
5074                 propertyExpression.setResolveFailed(true);
5075                 propertyExpression.setFailure(e.getMessage());
5076                 return ;
5077             }
5078         }
5079         else if (ownerExp instanceof ClassExpression) {
5080             if (ownerClass != null) {
5081                 Field fld  = null;
5082                 try {
5083                     fld = ownerClass.getDeclaredField(propName);
5084                     if (!Modifier.isPrivate(fld.getModifiers())) {
5085                         propertyExpression.setField(fld);
5086                         return ;
5087                     }
5088                 } catch (NoSuchFieldException e) {
5089                     propertyExpression.setResolveFailed(true);
5090                     return ;
5091                 }
5092             }
5093         }
5094         else { // search public field and then setter/getter
5095             if (ownerClass != null) {
5096                 propertyExpression.setResolveFailed(true); // will get reset if property/getter/setter were found
5097                 Field fld  = null;
5098                 try {
5099                     fld = ownerClass.getDeclaredField(propName);
5100                 } catch (NoSuchFieldException e) {}
5101 
5102                 if (fld != null && Modifier.isPublic(fld.getModifiers())) {
5103                     propertyExpression.setField(fld);
5104                 }
5105 
5106                 // let's get getter and setter
5107                 String getterName = "get" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
5108                 String setterName = "set" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1);
5109 
5110                 Method[] meths = ownerClass.getMethods();
5111                 for (int i = 0; i < meths.length; i++) {
5112                     Method method = meths[i];
5113                     String methName =method.getName();
5114                     Class[] paramClasses = method.getParameterTypes();
5115                     if (methName.equals(getterName) && paramClasses.length == 0) {
5116                         propertyExpression.setGetter(method);
5117                     } else if (methName.equals(setterName) && paramClasses.length == 1) {
5118                         propertyExpression.setSetter(method);
5119                     }
5120                 }
5121                 return ;
5122             }
5123         }
5124         propertyExpression.setResolveFailed(true);
5125         return ;
5126     }
5127     /*** search in the current classNode and super class for matching method */
5128     private MetaMethod getMethodOfThisAndSuper(String methName, Class[] argsArray, boolean isStaticCall) {
5129         MethodNode candidate = null;
5130         List meths = classNode.getMethods();
5131         Class[] candidateParamClasses = null;
5132         for (int i = 0; i < meths.size(); i++) {
5133             MethodNode meth = (MethodNode) meths.get(i);
5134             if (meth.getName().equals(methName)) {
5135                 Parameter[] params = meth.getParameters();
5136                 if  (params.length == argsArray.length) {
5137                     Class[] paramClasses = new Class[params.length];
5138                     for (int j = 0; j < params.length; j++) {
5139                         Parameter param = params[j];
5140                         String type = param.getType();
5141                         Class paramClass = null;
5142                         try {
5143                             paramClass = loadClass(type);
5144                         } catch (Exception e) {
5145                             log.warning(e.getMessage());
5146                             return null;
5147                         }
5148                         paramClasses[j] = paramClass;
5149                     }
5150                     if (MetaClass.isValidMethod(paramClasses, argsArray, false)) {
5151                         candidateParamClasses = paramClasses;
5152                         candidate = meth;
5153                         break;
5154                     }
5155                     else {
5156                         if (MetaClass.isValidMethod(paramClasses, argsArray, true)){
5157                             candidateParamClasses = paramClasses;
5158                             candidate = meth;
5159                             break;
5160                         }
5161                     }
5162                 }
5163             }
5164         }
5165 
5166         if (candidate != null && candidateParamClasses != null) {
5167             // let's synth a MetaMethod from the MethodNode
5168             try {
5169                 return new MetaMethod(methName, null, candidateParamClasses, loadClass(candidate.getReturnType()), candidate.getModifiers());
5170             } catch (Exception e) {
5171                 log.warning(e.getMessage());
5172                 return null;
5173             }
5174         }
5175         else {
5176             // try super class
5177             Class superClass = null;
5178             try {
5179                 superClass = loadClass(classNode.getSuperClass());
5180             }
5181             catch(Exception e) {
5182                 // the super may be a groovy class that's not compiled yet
5183                 log.warning(e.getMessage());
5184             }
5185             // should I filter out GroovyObject super class here?
5186             if (superClass != null ) {
5187                 MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(superClass, methName, argsArray, isStaticCall);
5188                 if (mmethod == null)
5189                     return null;
5190                 int modies = mmethod.getModifiers();
5191                 if (Modifier.isPrivate(modies)) {
5192                     return null;
5193                 }
5194                 else if(modies == 0) {
5195                     // match package
5196                     int pThis = classNode.getName().lastIndexOf(".");
5197                     String packageNameThis = pThis > 0? classNode.getName().substring(0, pThis) : "";
5198 
5199                     int pSuper = classNode.getSuperClass().lastIndexOf(".");
5200                     String packageNameSuper = pSuper > 0? classNode.getSuperClass().substring(0, pSuper) : "";
5201                     if (packageNameThis.equals(packageNameSuper)) {
5202                         return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers());
5203                     }
5204                     else {
5205                         return null;
5206                     }
5207                 }
5208                 else {
5209                     // let changes the declaring class back to null (meaning "this"), so that proper class inheritance permission control is obeyed
5210                     return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers());
5211                 }
5212             }
5213             return null;
5214         }
5215     }
5216 
5217 
5218     /***
5219      *  to find out the real type of a Variable Object
5220      */
5221     public void resolve(VariableExpression expression) {
5222 
5223         String variableName = expression.getVariable();
5224 // todo process arrays!
5225       //-----------------------------------------------------------------------
5226       // SPECIAL CASES
5227 
5228         //
5229         // "this" for static methods is the Class instance
5230 
5231         if (isStaticMethod() && variableName.equals("this")) {
5232             expression.setTypeClass(Class.class);
5233             return;
5234         } else if (variableName.equals("super")) {
5235             if (isStaticMethod() ) {
5236                 expression.setTypeClass(Class.class);
5237                 return;
5238             }
5239             else {
5240                 try {
5241                     Class cls = loadClass(classNode.getSuperClass());
5242                     expression.setTypeClass(cls);
5243                     return ;
5244                 }
5245                 catch (Exception e) {
5246                     expression.setResolveFailed(true);
5247                     expression.setFailure(e.getMessage());
5248                     return ;
5249                 }
5250             }
5251         } else if (variableName.equals("this")){
5252             return ;
5253         } else {
5254 //            String className = resolveClassName(variableName);
5255 //            if (className != null) {
5256 //                return loadClass(className);
5257 //            }
5258         }
5259 
5260 
5261       //-----------------------------------------------------------------------
5262       // GENERAL VARIABLE LOOKUP
5263 
5264         //
5265         // We are handling only unqualified variables here.  Therefore,
5266         // we do not care about accessors, because local access doesn't
5267         // go through them.  Therefore, precedence is as follows:
5268         //   1) local variables, nearest block first
5269         //   2) class fields
5270         //   3) repeat search from 2) in next outer class
5271 
5272         boolean  handled  = false;
5273         Variable variable = (Variable)variableStack.get( variableName );
5274 
5275         try {
5276             if( variable != null ) {
5277                 Type t = variable.getType();
5278                 if (t.getRealName().length() == 0) {
5279                     String tname = t.getName();
5280                     if (tname.endsWith("[]")) {
5281                         expression.setResolveFailed(true);
5282                         expression.setFailure("array type to be supported later");
5283                         return ;  // todo hanlde array
5284                     }
5285                     else if (tname.equals(classNode.getName())){
5286                         expression.setResolveFailed(true);
5287                         return ;
5288                     }
5289                     else if (classNode.getOuterClass() != null && tname.equals(classNode.getOuterClass().getName())){
5290                         expression.setResolveFailed(true);
5291                         return ;
5292                     }
5293                     Class cls = loadClass(tname);
5294                     expression.setTypeClass(cls);
5295                     expression.setDynamic(t.isDynamic());
5296                     return ;
5297                 } else {
5298                     String tname = t.getRealName();
5299                     if (tname.endsWith("[]")) {
5300                         expression.setResolveFailed(true);
5301                         expression.setFailure("array type to be supported later");
5302                         return ;  // todo hanlde array
5303                     }
5304                     Class cls = loadClass(tname);
5305                     expression.setTypeClass(cls);
5306                     expression.setDynamic(t.isDynamic());
5307                     return ;
5308                 }
5309 
5310 //            if( variable.isProperty() ) {
5311 //                processPropertyVariable(variable );
5312 //            }
5313 //            else {
5314 //                processStackVariable(variable );
5315 //            }
5316 //
5317             } else {
5318                 int       steps   = 0;
5319                 ClassNode currentClassNode = classNode;
5320                 FieldNode field   = null;
5321                 do {
5322                     if( (field = currentClassNode.getField(variableName)) != null ) {
5323                         if (methodNode == null || !methodNode.isStatic() || field.isStatic() ) {
5324                             if (/*field.isDynamicType() || */field.isHolder()) {
5325                                 expression.setResolveFailed(true);
5326                                 expression.setFailure("reference type to be supported later");
5327                                 return ;
5328                             } else {
5329                                 String type = field.getType();
5330                                 Class cls = loadClass(type);
5331                                 expression.setTypeClass(cls);
5332                                 expression.setDynamic(field.isDynamicType());
5333                                 return ;
5334                             }
5335                         }
5336                     }
5337                     steps++;
5338                 } while( (currentClassNode = currentClassNode.getOuterClass()) != null );
5339             }
5340 
5341             //
5342             // Finally, a new variable
5343 
5344             String variableType = expression.getType();
5345             if (variableType.length() > 0 && !variableType.equals("java.lang.Object")) {
5346                 Class cls = loadClass(variableType);
5347                 expression.setTypeClass(cls);
5348                 return ;
5349             }
5350         } catch (Exception e) {
5351             log.warning(e.getMessage());
5352             expression.setResolveFailed(true);
5353             expression.setFailure(e.getMessage());
5354         }
5355         return ;
5356     }
5357 
5358     public MetaMethod resolve(StaticMethodCallExpression staticCall) {
5359        if (staticCall.isResolveFailed()) {
5360             return null;
5361         }
5362         else if (staticCall.isTypeResolved()) {
5363             return staticCall.getMetaMethod();
5364         }
5365 
5366         String ownerTypeName = staticCall.getOwnerType();
5367         String meth = staticCall.getMethod();
5368         Class ownerClass = null;
5369         try {
5370             ownerClass = loadClass(ownerTypeName);
5371         } catch (Exception e) {
5372             staticCall.setResolveFailed(true);
5373             staticCall.setFailure("Owner type could not be resolved: " + e);
5374             return null;
5375         }
5376 
5377         boolean isStaticCall = true;
5378         boolean isSuperCall = false;
5379 
5380         List arglist = new ArrayList();
5381         Expression args = staticCall.getArguments();
5382         if (args instanceof TupleExpression) {
5383             TupleExpression tupleExpression = (TupleExpression) args;
5384             List argexps = tupleExpression.getExpressions();
5385             for (int i = 0; i < argexps.size(); i++) {
5386                 Expression expression = (Expression) argexps.get(i);
5387                 Class cls = expression.getTypeClass();
5388                 if (cls == null) {
5389                     staticCall.setResolveFailed(true);
5390                     staticCall.setFailure("Argument type could not be resolved.");
5391                     return null;
5392                 }
5393                 else {
5394                     arglist.add(cls);
5395                 }
5396             }
5397         } else if (args instanceof ClosureExpression) {
5398             staticCall.setResolveFailed(true);
5399             staticCall.setFailure("Resolving on Closure call not implemented yet. ");
5400             return null;// todo
5401         } else {
5402             staticCall.setResolveFailed(true);
5403             staticCall.setFailure("Unknown argument expression type.");
5404             return null;
5405         }
5406 
5407 
5408         Class[] argsArray = new Class[arglist.size()];
5409         arglist.toArray(argsArray);
5410 
5411         if (ownerClass == Object.class) {
5412             staticCall.setResolveFailed(true);
5413             staticCall.setFailure("Resolving on java.lang.Object static call not supported. ");
5414             return null; // use late binding for dynamic types
5415         }
5416         else if (ownerClass == null)  {
5417             staticCall.setResolveFailed(true);
5418             staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. ");
5419             return null; // use dynamic dispatching for GroovyObject
5420         }
5421         else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call
5422             staticCall.setResolveFailed(true);
5423             staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. ");
5424             return null;
5425         }
5426         else if (ownerClass.isPrimitive()) {
5427             staticCall.setResolveFailed(true);
5428             staticCall.setFailure("Could not use primitive as method owner");
5429             return null; // todo handle primitives
5430         }
5431 
5432 
5433         //MetaMethod mmethod = InvokerHelper.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
5434         // todo is this thread safe?
5435         MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall);
5436         if (mmethod!= null) {
5437             staticCall.setMetaMethod(mmethod);
5438         }
5439         else {
5440             staticCall.setResolveFailed(true);
5441             staticCall.setFailure("Could not find MetaMethod in the MetaClass.");
5442         }
5443         return mmethod;
5444     }
5445 
5446     // the fowllowing asXXX() methods are copied from the Invoker class, to avoid initilization of an invoker instance,
5447     // which has lots of baggage with it, notably the meta class stuff.
5448     private static Object asType(Object object, Class type) {
5449         if (object == null) {
5450             return null;
5451         }
5452         if (type.isInstance(object)) {
5453             return object;
5454         }
5455         if (type.equals(String.class)) {
5456             return object.toString();
5457         }
5458         if (type.equals(Character.class)) {
5459             if (object instanceof Number) {
5460                 return asCharacter((Number) object);
5461             }
5462             else {
5463                 String text = object.toString();
5464                 if (text.length() == 1) {
5465                     return new Character(text.charAt(0));
5466                 }
5467                 else {
5468                     throw new ClassCastException("Cannot cast: " + text + " to a Character");
5469                 }
5470             }
5471         }
5472         if (Number.class.isAssignableFrom(type)) {
5473             if (object instanceof Character) {
5474                 return new Integer(((Character) object).charValue());
5475             }
5476             else if (object instanceof String) {
5477                 String c = (String) object;
5478                 if (c.length() == 1) {
5479                     return new Integer(c.charAt(0));
5480                 }
5481                 else {
5482                     throw new ClassCastException("Cannot cast: '" + c + "' to an Integer");
5483                 }
5484             }
5485         }
5486         if (object instanceof Number) {
5487             Number n = (Number) object;
5488             if (type.isPrimitive()) {
5489                 if (type == byte.class) {
5490                     return new Byte(n.byteValue());
5491                 }
5492                 if (type == char.class) {
5493                     return new Character((char) n.intValue());
5494                 }
5495                 if (type == short.class) {
5496                     return new Short(n.shortValue());
5497                 }
5498                 if (type == int.class) {
5499                     return new Integer(n.intValue());
5500                 }
5501                 if (type == long.class) {
5502                     return new Long(n.longValue());
5503                 }
5504                 if (type == float.class) {
5505                     return new Float(n.floatValue());
5506                 }
5507                 if (type == double.class) {
5508                     Double answer = new Double(n.doubleValue());
5509                     //throw a runtime exception if conversion would be out-of-range for the type.
5510                     if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
5511                             || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
5512                         throw new  GroovyRuntimeException("Automatic coercion of "+n.getClass().getName()
5513                                 +" value "+n+" to double failed.  Value is out of range.");
5514                     }
5515                     return answer;
5516                 }
5517             }
5518             else {
5519                 if (Number.class.isAssignableFrom(type)) {
5520                     if (type == Byte.class) {
5521                         return new Byte(n.byteValue());
5522                     }
5523                     if (type == Character.class) {
5524                         return new Character((char) n.intValue());
5525                     }
5526                     if (type == Short.class) {
5527                         return new Short(n.shortValue());
5528                     }
5529                     if (type == Integer.class) {
5530                         return new Integer(n.intValue());
5531                     }
5532                     if (type == Long.class) {
5533                         return new Long(n.longValue());
5534                     }
5535                     if (type == Float.class) {
5536                         return new Float(n.floatValue());
5537                     }
5538                     if (type == Double.class) {
5539                         Double answer = new Double(n.doubleValue());
5540                         //throw a runtime exception if conversion would be out-of-range for the type.
5541                         if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
5542                                 || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
5543                             throw new  GroovyRuntimeException("Automatic coercion of "+n.getClass().getName()
5544                                     +" value "+n+" to double failed.  Value is out of range.");
5545                         }
5546                         return answer;
5547                     }
5548 
5549                 }
5550             }
5551         }
5552         if (type == Boolean.class) {
5553             return asBool(object) ? Boolean.TRUE : Boolean.FALSE;
5554         }
5555         return object;
5556     }
5557 
5558     private static boolean asBool(Object object) {
5559        if (object instanceof Boolean) {
5560             Boolean booleanValue = (Boolean) object;
5561             return booleanValue.booleanValue();
5562         }
5563         else if (object instanceof Matcher) {
5564             Matcher matcher = (Matcher) object;
5565             RegexSupport.setLastMatcher(matcher);
5566             return matcher.find();
5567         }
5568         else if (object instanceof Collection) {
5569             Collection collection = (Collection) object;
5570             return !collection.isEmpty();
5571         }
5572         else if (object instanceof Number) {
5573             Number n = (Number) object;
5574             return n.intValue() != 0;
5575         }
5576         else {
5577             return object != null;
5578         }
5579     }
5580     private static Character asCharacter(Number value) {
5581         return new Character((char) value.intValue());
5582     }
5583 
5584     private static Character asCharacter(String text) {
5585         return new Character(text.charAt(0));
5586     }
5587 }