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