001 /*
002 * $Id: AsmClassGenerator.java,v 1.39 2005/06/01 14:46:53 blackdrag 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.39 $
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("it")};
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 }