001 /*
002 $Id: AsmClassGenerator.java,v 1.58 2005/11/13 16:42:10 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
008 that the following conditions are met:
009
010 1. Redistributions of source code must retain copyright
011 statements and notices. Redistributions must also contain a
012 copy of this document.
013
014 2. Redistributions in binary form must reproduce the
015 above copyright notice, this list of conditions and the
016 following disclaimer in the documentation and/or other
017 materials provided with the distribution.
018
019 3. The name "groovy" must not be used to endorse or promote
020 products derived from this Software without prior written
021 permission of The Codehaus. For written permission,
022 please contact info@codehaus.org.
023
024 4. Products derived from this Software may not be called "groovy"
025 nor may "groovy" appear in their names without prior written
026 permission of The Codehaus. "groovy" is a registered
027 trademark of The Codehaus.
028
029 5. Due credit should be given to The Codehaus -
030 http://groovy.codehaus.org/
031
032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043 OF THE POSSIBILITY OF SUCH DAMAGE.
044 */
045
046 package org.codehaus.groovy.classgen;
047
048 import groovy.lang.*;
049
050 import java.util.*;
051 import java.util.logging.Logger;
052
053 import org.codehaus.groovy.ast.ASTNode;
054 import org.codehaus.groovy.ast.AnnotatedNode;
055 import org.codehaus.groovy.ast.AnnotationNode;
056 import org.codehaus.groovy.ast.ClassHelper;
057 import org.codehaus.groovy.ast.ClassNode;
058 import org.codehaus.groovy.ast.CompileUnit;
059 import org.codehaus.groovy.ast.ConstructorNode;
060 import org.codehaus.groovy.ast.FieldNode;
061 import org.codehaus.groovy.ast.GroovyCodeVisitor;
062 import org.codehaus.groovy.ast.InnerClassNode;
063 import org.codehaus.groovy.ast.MethodNode;
064 import org.codehaus.groovy.ast.Parameter;
065 import org.codehaus.groovy.ast.PropertyNode;
066 import org.codehaus.groovy.ast.VariableScope;
067 import org.codehaus.groovy.ast.expr.*;
068 import org.codehaus.groovy.ast.stmt.AssertStatement;
069 import org.codehaus.groovy.ast.stmt.BlockStatement;
070 import org.codehaus.groovy.ast.stmt.BreakStatement;
071 import org.codehaus.groovy.ast.stmt.CaseStatement;
072 import org.codehaus.groovy.ast.stmt.CatchStatement;
073 import org.codehaus.groovy.ast.stmt.ContinueStatement;
074 import org.codehaus.groovy.ast.stmt.DoWhileStatement;
075 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
076 import org.codehaus.groovy.ast.stmt.ForStatement;
077 import org.codehaus.groovy.ast.stmt.IfStatement;
078 import org.codehaus.groovy.ast.stmt.ReturnStatement;
079 import org.codehaus.groovy.ast.stmt.Statement;
080 import org.codehaus.groovy.ast.stmt.SwitchStatement;
081 import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
082 import org.codehaus.groovy.ast.stmt.ThrowStatement;
083 import org.codehaus.groovy.ast.stmt.TryCatchStatement;
084 import org.codehaus.groovy.ast.stmt.WhileStatement;
085 import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
086 import org.codehaus.groovy.syntax.Token;
087 import org.codehaus.groovy.syntax.Types;
088 import org.codehaus.groovy.syntax.RuntimeParserException;
089 import org.objectweb.asm.AnnotationVisitor;
090 import org.objectweb.asm.ClassVisitor;
091 import org.objectweb.asm.MethodVisitor;
092 import org.objectweb.asm.Label;
093 import org.objectweb.asm.ClassWriter;
094
095
096 /**
097 * Generates Java class versions of Groovy classes using ASM.
098 *
099 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
100 * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
101 * @author Jochen Theodorou
102 *
103 * @version $Revision: 1.58 $
104 */
105 public class AsmClassGenerator extends ClassGenerator {
106
107 private Logger log = Logger.getLogger(getClass().getName());
108
109 private ClassVisitor cw;
110 private MethodVisitor cv;
111 private GeneratorContext context;
112
113 private String sourceFile;
114
115 // current class details
116 private ClassNode classNode;
117 private ClassNode outermostClass;
118 private String internalClassName;
119 private String internalBaseClassName;
120
121 /** maps the variable names to the JVM indices */
122 private Map variableStack = new HashMap();
123
124 /** have we output a return statement yet */
125 private boolean outputReturn;
126
127 /** are we on the left or right of an expression */
128 private boolean leftHandExpression;
129
130 // cached values
131 MethodCaller invokeMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethod");
132 MethodCaller invokeMethodSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethodSafe");
133 MethodCaller invokeMethodSpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethodSpreadSafe");
134 MethodCaller invokeStaticMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeStaticMethod");
135 MethodCaller invokeConstructorMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeConstructor");
136 MethodCaller invokeConstructorOfMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeConstructorOf");
137 MethodCaller invokeNoArgumentsConstructorOf = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsConstructorOf");
138 MethodCaller invokeConstructorAtMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeConstructorAt");
139 MethodCaller invokeNoArgumentsConstructorAt = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsConstructorAt");
140 MethodCaller invokeClosureMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeClosure");
141 MethodCaller invokeSuperMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeSuperMethod");
142 MethodCaller invokeNoArgumentsMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsMethod");
143 MethodCaller invokeNoArgumentsSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsSafeMethod");
144 MethodCaller invokeNoArgumentsSpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsSpreadSafeMethod");
145 MethodCaller invokeStaticNoArgumentsMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeStaticNoArgumentsMethod");
146
147 MethodCaller asIntMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asInt");
148 MethodCaller asTypeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asType");
149
150 MethodCaller getAttributeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttribute");
151 MethodCaller getAttributeSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttributeSafe");
152 MethodCaller getAttributeSpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttributeSpreadSafe");
153 MethodCaller setAttributeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setAttribute2");
154 MethodCaller setAttributeSafeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setAttributeSafe2");
155
156 MethodCaller getPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getProperty");
157 MethodCaller getPropertySafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getPropertySafe");
158 MethodCaller getPropertySpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getPropertySpreadSafe");
159 MethodCaller setPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setProperty");
160 MethodCaller setPropertyMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setProperty2");
161 MethodCaller setPropertySafeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setPropertySafe2");
162
163 MethodCaller getGroovyObjectPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getGroovyObjectProperty");
164 MethodCaller setGroovyObjectPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setGroovyObjectProperty");
165 MethodCaller asIteratorMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asIterator");
166 MethodCaller asBool = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asBool");
167 MethodCaller notBoolean = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "notBoolean");
168 MethodCaller notObject = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "notObject");
169 MethodCaller regexPattern = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "regexPattern");
170 MethodCaller spreadList = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "spreadList");
171 MethodCaller spreadMap = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "spreadMap");
172 MethodCaller getMethodPointer = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getMethodPointer");
173 MethodCaller negation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "negate");
174 MethodCaller bitNegation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "bitNegate");
175 MethodCaller convertPrimitiveArray = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "convertPrimitiveArray");
176 MethodCaller convertToPrimitiveArray = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "convertToPrimitiveArray");
177
178 MethodCaller compareIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareIdentical");
179 MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual");
180 MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual");
181 MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo");
182 MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex");
183 MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex");
184 MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan");
185 MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual");
186 MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan");
187 MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual");
188 MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");
189
190 MethodCaller createListMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createList");
191 MethodCaller createTupleMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createTuple");
192 MethodCaller createMapMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createMap");
193 MethodCaller createRangeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createRange");
194
195 MethodCaller assertFailedMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "assertFailed");
196
197 MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
198 MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
199
200
201 // current stack index
202 private int lastVariableIndex;
203 private static int tempVariableNameCounter;
204
205 // exception blocks list
206 private List exceptionBlocks = new ArrayList();
207
208 private boolean definingParameters;
209 private Set syntheticStaticFields = new HashSet();
210 private Set mutableVars = new HashSet();
211 private boolean passingClosureParams;
212
213 private ConstructorNode constructorNode;
214 private MethodNode methodNode;
215 //private PropertyNode propertyNode;
216 private BlockScope scope;
217 private BytecodeHelper helper = new BytecodeHelper(null);
218
219 private VariableScope variableScope;
220 public static final boolean CREATE_DEBUG_INFO = false;
221 public static final boolean CREATE_LINE_NUMBER_INFO = true;
222 private static final boolean MARK_START = true;
223
224 /*public static final String EB_SWITCH_NAME = "static.dispatching";
225 public boolean ENABLE_EARLY_BINDING;
226 { //
227 String ebSwitch = (String) AccessController.doPrivileged(new PrivilegedAction() {
228 public Object run() {
229 return System.getProperty(EB_SWITCH_NAME, "false"); // set default to true if early binding is on by default.
230 }
231 });
232 //System.out.println("ebSwitch = " + ebSwitch);
233 if (ebSwitch.equals("true")) {
234 ENABLE_EARLY_BINDING = true;
235 }
236 else if (ebSwitch.equals("false")) {
237 ENABLE_EARLY_BINDING = false;
238 }
239 else {
240 ENABLE_EARLY_BINDING = false;
241 log.warning("The value of system property " + EB_SWITCH_NAME + " is not recognized. Late dispatching is assumed. ");
242 }
243 }*/
244 public static final boolean ASM_DEBUG = false; // add marker in the bytecode to show source-byecode relationship
245 private int lineNumber = -1;
246 private int columnNumber = -1;
247 private ASTNode currentASTNode = null;
248
249 private DummyClassGenerator dummyGen = null;
250 private ClassWriter dummyClassWriter = null;
251
252 public AsmClassGenerator(
253 GeneratorContext context,
254 ClassVisitor classVisitor,
255 ClassLoader classLoader,
256 String sourceFile) {
257 super(classLoader);
258 this.context = context;
259 this.cw = classVisitor;
260 this.sourceFile = sourceFile;
261
262 this.dummyClassWriter = new ClassWriter(true);
263 dummyGen = new DummyClassGenerator(context, dummyClassWriter, classLoader, sourceFile);
264
265 }
266
267 // GroovyClassVisitor interface
268 //-------------------------------------------------------------------------
269 public void visitClass(ClassNode classNode) {
270 // todo to be tested
271 // createDummyClass(classNode);
272
273 try {
274 syntheticStaticFields.clear();
275 this.classNode = classNode;
276 this.outermostClass = null;
277 this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
278
279 //System.out.println("Generating class: " + classNode.getName());
280
281 this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
282
283 cw.visit(
284 asmJDKVersion,
285 classNode.getModifiers(),
286 internalClassName,
287 null,
288 internalBaseClassName,
289 BytecodeHelper.getClassInternalNames(classNode.getInterfaces())
290 );
291 cw.visitSource(sourceFile,null);
292 visitAnnotations(classNode);
293
294 // set the optional enclosing method attribute of the current inner class
295 // br comment out once Groovy uses the latest CVS HEAD of ASM
296 // MethodNode enclosingMethod = classNode.getEnclosingMethod();
297 // String ownerName = BytecodeHelper.getClassInternalName(enclosingMethod.getDeclaringClass().getName());
298 // String descriptor = BytecodeHelper.getMethodDescriptor(enclosingMethod.getReturnType(), enclosingMethod.getParameters());
299 // EnclosingMethodAttribute attr = new EnclosingMethodAttribute(ownerName,enclosingMethod.getName(),descriptor);
300 // cw.visitAttribute(attr);
301
302 classNode.visitContents(this);
303
304 createSyntheticStaticFields();
305
306 for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
307 ClassNode innerClass = (ClassNode) iter.next();
308 String innerClassName = innerClass.getName();
309 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
310 {
311 int index = innerClassName.lastIndexOf('$');
312 if (index>=0) innerClassName = innerClassName.substring(index+1);
313 }
314 String outerClassName = internalClassName; // default for inner classes
315 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
316 if (enclosingMethod != null) {
317 // local inner classes do not specify the outer class name
318 outerClassName = null;
319 innerClassName = null;
320 }
321 cw.visitInnerClass(
322 innerClassInternalName,
323 outerClassName,
324 innerClassName,
325 innerClass.getModifiers());
326 }
327 // br TODO an inner class should have an entry of itself
328 cw.visitEnd();
329 }
330 catch (GroovyRuntimeException e) {
331 e.setModule(classNode.getModule());
332 throw e;
333 }
334 }
335
336 public void visitConstructor(ConstructorNode node) {
337 // creates a MethodWriter for the (implicit) constructor
338 //String methodType = ClassNode.getMethodDescriptor(VOID_TYPE, )
339
340 this.constructorNode = node;
341 this.methodNode = null;
342 this.variableScope = null;
343
344 String methodType = BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, node.getParameters());
345 cv = cw.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
346 helper = new BytecodeHelper(cv);
347
348 findMutableVariables();
349 resetVariableStack(node.getParameters());
350
351 Statement code = node.getCode();
352 if (code == null || !firstStatementIsSuperInit(code)) {
353 // invokes the super class constructor
354 cv.visitVarInsn(ALOAD, 0);
355 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "()V");
356 }
357 if (code != null) {
358 code.visit(this);
359 }
360
361 cv.visitInsn(RETURN);
362 cv.visitMaxs(0, 0);
363 }
364
365 public void visitMethod(MethodNode node) {
366 //System.out.println("Visiting method: " + node.getName() + " with
367 // return type: " + node.getReturnType());
368 this.constructorNode = null;
369 this.methodNode = node;
370 this.variableScope = null;
371
372 String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
373 cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
374 visitAnnotations(node);
375 if (node.getCode()!=null) {
376 Label labelStart = new Label();
377 cv.visitLabel(labelStart);
378 helper = new BytecodeHelper(cv);
379
380 findMutableVariables();
381 resetVariableStack(node.getParameters());
382
383
384 outputReturn = false;
385
386 node.getCode().visit(this);
387
388 if (!outputReturn) {
389 cv.visitInsn(RETURN);
390 }
391
392 // lets do all the exception blocks
393 for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) {
394 Runnable runnable = (Runnable) iter.next();
395 runnable.run();
396 }
397 exceptionBlocks.clear();
398
399 Label labelEnd = new Label();
400 cv.visitLabel(labelEnd);
401
402 // br experiment with local var table so debuggers can retrieve variable names
403 if (CREATE_DEBUG_INFO) {
404 Set vars = this.variableStack.keySet();
405 for (Iterator iterator = vars.iterator(); iterator.hasNext();) {
406 String varName = (String) iterator.next();
407 Variable v = (Variable)variableStack.get(varName);
408 String type = v.getTypeName();
409 type = BytecodeHelper.getTypeDescription(type);
410 Label start = v.getStartLabel() != null ? v.getStartLabel() : labelStart;
411 Label end = v.getEndLabel() != null ? v.getEndLabel() : labelEnd;
412 cv.visitLocalVariable(varName, type, null, start, end, v.getIndex());
413 }
414 }
415 cv.visitMaxs(0, 0);
416 }
417 }
418
419 public void visitField(FieldNode fieldNode) {
420 onLineNumber(fieldNode, "visitField: " + fieldNode.getName());
421 ClassNode t = fieldNode.getType();
422 cw.visitField(
423 fieldNode.getModifiers(),
424 fieldNode.getName(),
425 BytecodeHelper.getTypeDescription(t),
426 null, //fieldValue, //br all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
427 null);
428 visitAnnotations(fieldNode);
429 }
430
431
432 /**
433 * Creates a getter, setter and field
434 */
435 public void visitProperty(PropertyNode statement) {
436 onLineNumber(statement, "visitProperty:" + statement.getField().getName());
437 //this.propertyNode = statement;
438 this.methodNode = null;
439 }
440
441 // GroovyCodeVisitor interface
442 //-------------------------------------------------------------------------
443
444 // Statements
445 //-------------------------------------------------------------------------
446
447 public void visitForLoop(ForStatement loop) {
448 onLineNumber(loop, "visitForLoop");
449 Class elemType = null;
450
451 //
452 // Declare the loop counter.
453 ClassNode variableType = loop.getVariableType();
454 Variable variable = defineVariable(loop.getVariable(), variableType, true);
455
456 if( isInScriptBody() ) {
457 variable.setProperty( true );
458 }
459
460
461 //
462 // Then initialize the iterator and generate the loop control
463
464 loop.getCollectionExpression().visit(this);
465
466 asIteratorMethod.call(cv);
467
468 final Variable iterTemp = storeInTemp("iterator", ClassHelper.make(java.util.Iterator.class));
469 final int iteratorIdx = iterTemp.getIndex();
470
471 // to push scope here allows the iterator available after the loop, such as the i in: for (i in 1..5)
472 // move it to the top will make the iterator a local var in the for loop.
473 pushBlockScope();
474
475 Label continueLabel = scope.getContinueLabel();
476 cv.visitJumpInsn(GOTO, continueLabel);
477 Label label2 = new Label();
478 cv.visitLabel(label2);
479
480 BytecodeExpression expression = new BytecodeExpression() {
481 public void visit(GroovyCodeVisitor visitor) {
482 cv.visitVarInsn(ALOAD, iteratorIdx);
483 iteratorNextMethod.call(cv);
484 }
485 };
486
487 evaluateEqual( BinaryExpression.newAssignmentExpression(loop.getVariable(), expression) );
488 cv.visitInsn(POP); // br now the evaluateEqual() will leave a value on the stack. pop it.
489
490 //
491 // Generate the loop body
492
493 loop.getLoopBlock().visit(this);
494
495
496 //
497 // Generate the loop tail
498
499 cv.visitLabel(continueLabel);
500 cv.visitVarInsn(ALOAD, iteratorIdx);
501
502 iteratorHasNextMethod.call(cv);
503
504 cv.visitJumpInsn(IFNE, label2);
505
506 cv.visitLabel(scope.getBreakLabel());
507 popScope();
508 }
509
510 public void visitWhileLoop(WhileStatement loop) {
511 onLineNumber(loop, "visitWhileLoop");
512
513 pushBlockScope();
514
515 Label continueLabel = scope.getContinueLabel();
516
517 cv.visitJumpInsn(GOTO, continueLabel);
518 Label l1 = new Label();
519 cv.visitLabel(l1);
520
521 loop.getLoopBlock().visit(this);
522
523 cv.visitLabel(continueLabel);
524
525 loop.getBooleanExpression().visit(this);
526
527 cv.visitJumpInsn(IFNE, l1);
528
529 cv.visitLabel(scope.getBreakLabel());
530 popScope();
531 }
532
533 public void visitDoWhileLoop(DoWhileStatement loop) {
534 onLineNumber(loop, "visitDoWhileLoop");
535
536 pushBlockScope();
537
538 Label breakLabel = scope.getBreakLabel();
539
540 Label continueLabel = scope.getContinueLabel();
541 cv.visitLabel(continueLabel);
542 Label l1 = new Label();
543
544 loop.getLoopBlock().visit(this);
545
546 cv.visitLabel(l1);
547
548 loop.getBooleanExpression().visit(this);
549
550 cv.visitJumpInsn(IFNE, continueLabel);
551
552 cv.visitLabel(breakLabel);
553 popScope();
554 }
555
556 public void visitIfElse(IfStatement ifElse) {
557 onLineNumber(ifElse, "visitIfElse");
558
559 ifElse.getBooleanExpression().visit(this);
560
561 Label l0 = new Label();
562 cv.visitJumpInsn(IFEQ, l0);
563 pushBlockScope(false, false);
564 ifElse.getIfBlock().visit(this);
565 popScope();
566
567 Label l1 = new Label();
568 cv.visitJumpInsn(GOTO, l1);
569 cv.visitLabel(l0);
570
571 pushBlockScope(false, false);
572 ifElse.getElseBlock().visit(this);
573 cv.visitLabel(l1);
574 popScope();
575 }
576
577 public void visitTernaryExpression(TernaryExpression expression) {
578 onLineNumber(expression, "visitTernaryExpression");
579
580 expression.getBooleanExpression().visit(this);
581
582 Label l0 = new Label();
583 cv.visitJumpInsn(IFEQ, l0);
584 expression.getTrueExpression().visit(this);
585
586 Label l1 = new Label();
587 cv.visitJumpInsn(GOTO, l1);
588 cv.visitLabel(l0);
589
590 expression.getFalseExpression().visit(this);
591 cv.visitLabel(l1);
592 }
593
594 public void visitAssertStatement(AssertStatement statement) {
595 onLineNumber(statement, "visitAssertStatement");
596
597 //System.out.println("Assert: " + statement.getLineNumber() + " for: "
598 // + statement.getText());
599
600 BooleanExpression booleanExpression = statement.getBooleanExpression();
601 booleanExpression.visit(this);
602
603 Label l0 = new Label();
604 cv.visitJumpInsn(IFEQ, l0);
605
606 // do nothing
607
608 Label l1 = new Label();
609 cv.visitJumpInsn(GOTO, l1);
610 cv.visitLabel(l0);
611
612 // push expression string onto stack
613 String expressionText = booleanExpression.getText();
614 List list = new ArrayList();
615 addVariableNames(booleanExpression, list);
616 if (list.isEmpty()) {
617 cv.visitLdcInsn(expressionText);
618 }
619 else {
620 boolean first = true;
621
622 // lets create a new expression
623 cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
624 cv.visitInsn(DUP);
625 cv.visitLdcInsn(expressionText + ". Values: ");
626
627 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V");
628
629 Variable assertTemp = visitASTOREInTemp("assert");
630 int tempIndex = assertTemp.getIndex();
631
632 for (Iterator iter = list.iterator(); iter.hasNext();) {
633 String name = (String) iter.next();
634 String text = name + " = ";
635 if (first) {
636 first = false;
637 }
638 else {
639 text = ", " + text;
640 }
641
642 cv.visitVarInsn(ALOAD, tempIndex);
643 cv.visitLdcInsn(text);
644 cv.visitMethodInsn(
645 INVOKEVIRTUAL,
646 "java/lang/StringBuffer",
647 "append",
648 "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
649 cv.visitInsn(POP);
650
651 cv.visitVarInsn(ALOAD, tempIndex);
652 new VariableExpression(name).visit(this);
653 cv.visitMethodInsn(
654 INVOKEVIRTUAL,
655 "java/lang/StringBuffer",
656 "append",
657 "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
658 cv.visitInsn(POP);
659
660 }
661 cv.visitVarInsn(ALOAD, tempIndex);
662 removeVar(assertTemp);
663 }
664 // now the optional exception expression
665 statement.getMessageExpression().visit(this);
666
667 assertFailedMethod.call(cv);
668 cv.visitLabel(l1);
669 }
670
671 private void addVariableNames(Expression expression, List list) {
672 if (expression instanceof BooleanExpression) {
673 BooleanExpression boolExp = (BooleanExpression) expression;
674 addVariableNames(boolExp.getExpression(), list);
675 }
676 else if (expression instanceof BinaryExpression) {
677 BinaryExpression binExp = (BinaryExpression) expression;
678 addVariableNames(binExp.getLeftExpression(), list);
679 addVariableNames(binExp.getRightExpression(), list);
680 }
681 else if (expression instanceof VariableExpression) {
682 VariableExpression varExp = (VariableExpression) expression;
683 list.add(varExp.getName());
684 }
685 }
686
687 public void visitTryCatchFinally(TryCatchStatement statement) {
688 onLineNumber(statement, "visitTryCatchFinally");
689 // todo need to add blockscope handling
690 CatchStatement catchStatement = statement.getCatchStatement(0);
691
692 Statement tryStatement = statement.getTryStatement();
693
694 if (tryStatement.isEmpty() || catchStatement == null) {
695 final Label l0 = new Label();
696 cv.visitLabel(l0);
697
698 tryStatement.visit(this);
699
700
701 int index1 = defineVariable(this.createVariableName("exception"), ClassHelper.OBJECT_TYPE).getIndex();
702 int index2 = defineVariable(this.createVariableName("exception"), ClassHelper.OBJECT_TYPE).getIndex();
703
704 final Label l1 = new Label();
705 cv.visitJumpInsn(JSR, l1);
706 final Label l2 = new Label();
707 cv.visitLabel(l2);
708 final Label l3 = new Label();
709 cv.visitJumpInsn(GOTO, l3);
710 final Label l4 = new Label();
711 cv.visitLabel(l4);
712 cv.visitVarInsn(ASTORE, index1);
713 cv.visitJumpInsn(JSR, l1);
714 final Label l5 = new Label();
715 cv.visitLabel(l5);
716 cv.visitVarInsn(ALOAD, index1);
717 cv.visitInsn(ATHROW);
718 cv.visitLabel(l1);
719 cv.visitVarInsn(ASTORE, index2);
720
721 statement.getFinallyStatement().visit(this);
722
723 cv.visitVarInsn(RET, index2);
724 cv.visitLabel(l3);
725
726 exceptionBlocks.add(new Runnable() {
727 public void run() {
728 cv.visitTryCatchBlock(l0, l2, l4, null);
729 cv.visitTryCatchBlock(l4, l5, l4, null);
730 }
731 });
732
733 }
734 else {
735 int finallySubAddress = defineVariable(this.createVariableName("exception"), ClassHelper.OBJECT_TYPE).getIndex();
736 int anyExceptionIndex = defineVariable(this.createVariableName("exception"), ClassHelper.OBJECT_TYPE).getIndex();
737
738 // start try block, label needed for exception table
739 final Label tryStart = new Label();
740 cv.visitLabel(tryStart);
741 tryStatement.visit(this);
742 // goto finally part
743 final Label finallyStart = new Label();
744 cv.visitJumpInsn(GOTO, finallyStart);
745 // marker needed for Exception table
746 final Label tryEnd = new Label();
747 cv.visitLabel(tryEnd);
748
749 for (Iterator it=statement.getCatchStatements().iterator(); it.hasNext();) {
750 catchStatement = (CatchStatement) it.next();
751 ClassNode exceptionType = catchStatement.getExceptionType();
752 int exceptionIndex = defineVariable(catchStatement.getVariable(), exceptionType, false).getIndex();
753
754 // start catch block, label needed for exception table
755 final Label catchStart = new Label();
756 cv.visitLabel(catchStart);
757 // store the exception
758 cv.visitVarInsn(ASTORE, exceptionIndex);
759 catchStatement.visit(this);
760 // goto finally start
761 cv.visitJumpInsn(GOTO, finallyStart);
762 // add exception to table
763 final String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType);
764 exceptionBlocks.add(new Runnable() {
765 public void run() {
766 cv.visitTryCatchBlock(tryStart, tryEnd, catchStart, exceptionTypeInternalName);
767 }
768 });
769 }
770
771 // marker needed for the exception table
772 final Label endOfAllCatches = new Label();
773 cv.visitLabel(endOfAllCatches);
774
775 // start finally
776 cv.visitLabel(finallyStart);
777 Label finallySub = new Label();
778 // run finally sub
779 cv.visitJumpInsn(JSR, finallySub);
780 // goto end of finally
781 Label afterFinally = new Label();
782 cv.visitJumpInsn(GOTO, afterFinally);
783
784 // start a block catching any Exception
785 final Label catchAny = new Label();
786 cv.visitLabel(catchAny);
787 //store exception
788 cv.visitVarInsn(ASTORE, anyExceptionIndex);
789 // run finally subroutine
790 cv.visitJumpInsn(JSR, finallySub);
791 // load the exception and rethrow it
792 cv.visitVarInsn(ALOAD, anyExceptionIndex);
793 cv.visitInsn(ATHROW);
794
795 // start the finally subroutine
796 cv.visitLabel(finallySub);
797 // store jump address
798 cv.visitVarInsn(ASTORE, finallySubAddress);
799 if (!statement.getFinallyStatement().isEmpty())
800 statement.getFinallyStatement().visit(this);
801 // return from subroutine
802 cv.visitVarInsn(RET, finallySubAddress);
803
804 // end of all catches and finally parts
805 cv.visitLabel(afterFinally);
806
807 // add catch any block to exception table
808 exceptionBlocks.add(new Runnable() {
809 public void run() {
810 cv.visitTryCatchBlock(tryStart, endOfAllCatches, catchAny, null);
811 }
812 });
813 }
814 }
815
816 private Variable storeInTemp(String name, ClassNode type) {
817 Variable var = defineVariable(createVariableName(name), type, false);
818 int varIdx = var.getIndex();
819 cv.visitVarInsn(ASTORE, varIdx);
820 if (CREATE_DEBUG_INFO) cv.visitLabel(var.getStartLabel());
821 return var;
822 }
823
824 public void visitSwitch(SwitchStatement statement) {
825 onLineNumber(statement, "visitSwitch");
826
827 statement.getExpression().visit(this);
828
829 // switch does not have a continue label. use its parent's for continue
830 pushBlockScope(false, true);
831 //scope.setContinueLabel(scope.getParent().getContinueLabel());
832
833
834 int switchVariableIndex = defineVariable(createVariableName("switch"), ClassHelper.OBJECT_TYPE).getIndex();
835 cv.visitVarInsn(ASTORE, switchVariableIndex);
836
837 List caseStatements = statement.getCaseStatements();
838 int caseCount = caseStatements.size();
839 Label[] labels = new Label[caseCount + 1];
840 for (int i = 0; i < caseCount; i++) {
841 labels[i] = new Label();
842 }
843
844 int i = 0;
845 for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
846 CaseStatement caseStatement = (CaseStatement) iter.next();
847 visitCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]);
848 }
849
850 statement.getDefaultStatement().visit(this);
851
852 cv.visitLabel(scope.getBreakLabel());
853
854 popScope();
855 }
856
857 public void visitCaseStatement(CaseStatement statement) {
858 }
859
860 public void visitCaseStatement(
861 CaseStatement statement,
862 int switchVariableIndex,
863 Label thisLabel,
864 Label nextLabel) {
865
866 onLineNumber(statement, "visitCaseStatement");
867
868 cv.visitVarInsn(ALOAD, switchVariableIndex);
869 statement.getExpression().visit(this);
870
871 isCaseMethod.call(cv);
872
873 Label l0 = new Label();
874 cv.visitJumpInsn(IFEQ, l0);
875
876 cv.visitLabel(thisLabel);
877
878 statement.getCode().visit(this);
879
880 // now if we don't finish with a break we need to jump past
881 // the next comparison
882 if (nextLabel != null) {
883 cv.visitJumpInsn(GOTO, nextLabel);
884 }
885
886 cv.visitLabel(l0);
887 }
888
889 public void visitBreakStatement(BreakStatement statement) {
890 onLineNumber(statement, "visitBreakStatement");
891
892 Label breakLabel = scope.getBreakLabel();
893 if (breakLabel != null ) {
894 cv.visitJumpInsn(GOTO, breakLabel);
895 } else {
896 // should warn that break is not allowed in the context.
897 }
898 }
899
900 public void visitContinueStatement(ContinueStatement statement) {
901 onLineNumber(statement, "visitContinueStatement");
902
903 Label continueLabel = scope.getContinueLabel();
904 if (continueLabel != null ) {
905 cv.visitJumpInsn(GOTO, continueLabel);
906 } else {
907 // should warn that continue is not allowed in the context.
908 }
909 }
910
911 public void visitSynchronizedStatement(SynchronizedStatement statement) {
912 onLineNumber(statement, "visitSynchronizedStatement");
913
914 statement.getExpression().visit(this);
915
916 int index = defineVariable(createVariableName("synchronized"), ClassHelper.Integer_TYPE).getIndex();
917
918 cv.visitVarInsn(ASTORE, index);
919 cv.visitVarInsn(ALOAD, index);
920 cv.visitInsn(MONITORENTER);
921 final Label l0 = new Label();
922 cv.visitLabel(l0);
923
924 statement.getCode().visit(this);
925
926 cv.visitVarInsn(ALOAD, index);
927 cv.visitInsn(MONITOREXIT);
928 final Label l1 = new Label();
929 cv.visitJumpInsn(GOTO, l1);
930 final Label l2 = new Label();
931 cv.visitLabel(l2);
932 cv.visitVarInsn(ALOAD, index);
933 cv.visitInsn(MONITOREXIT);
934 cv.visitInsn(ATHROW);
935 cv.visitLabel(l1);
936
937 exceptionBlocks.add(new Runnable() {
938 public void run() {
939 cv.visitTryCatchBlock(l0, l2, l2, null);
940 }
941 });
942 }
943
944 public void visitThrowStatement(ThrowStatement statement) {
945 statement.getExpression().visit(this);
946
947 // we should infer the type of the exception from the expression
948 cv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
949
950 cv.visitInsn(ATHROW);
951 }
952
953 public void visitReturnStatement(ReturnStatement statement) {
954 onLineNumber(statement, "visitReturnStatement");
955 ClassNode returnType = methodNode.getReturnType();
956 if (returnType==ClassHelper.VOID_TYPE) {
957 if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) {
958 throwException("Cannot use return statement with an expression on a method that returns void");
959 }
960 cv.visitInsn(RETURN);
961 outputReturn = true;
962 return;
963 }
964
965 Expression expression = statement.getExpression();
966 evaluateExpression(expression);
967 if (returnType==ClassHelper.OBJECT_TYPE && expression.getType() != null && expression.getType()==ClassHelper.VOID_TYPE) {
968 cv.visitInsn(ACONST_NULL); // cheat the caller
969 cv.visitInsn(ARETURN);
970 } else {
971 //return is based on class type
972 //TODO: make work with arrays
973 // we may need to cast
974 helper.unbox(returnType);
975 if (returnType==ClassHelper.double_TYPE) {
976 cv.visitInsn(DRETURN);
977 }
978 else if (returnType==ClassHelper.float_TYPE) {
979 cv.visitInsn(FRETURN);
980 }
981 else if (returnType==ClassHelper.long_TYPE) {
982 cv.visitInsn(LRETURN);
983 }
984 else if (returnType==ClassHelper.boolean_TYPE) {
985 cv.visitInsn(IRETURN);
986 }
987 else if (
988 returnType==ClassHelper.char_TYPE
989 || returnType==ClassHelper.byte_TYPE
990 || returnType==ClassHelper.int_TYPE
991 || returnType==ClassHelper.short_TYPE)
992 {
993 //byte,short,boolean,int are all IRETURN
994 cv.visitInsn(IRETURN);
995 }
996 else {
997 doConvertAndCast(returnType, expression, false, true);
998 cv.visitInsn(ARETURN);
999 }
1000 }
1001 outputReturn = true;
1002 }
1003
1004 /**
1005 * Casts to the given type unless it can be determined that the cast is unnecessary
1006 */
1007 protected void doConvertAndCast(ClassNode type, Expression expression, boolean ignoreAutoboxing, boolean forceCast) {
1008 ClassNode expType = getExpressionType(expression);
1009 // temp resolution: convert all primitive casting to corresponsing Object type
1010 if (!ignoreAutoboxing && ClassHelper.isPrimitiveType(type)) {
1011 type = ClassHelper.getWrapper(type);
1012 }
1013 if (forceCast || (type!=null && !type.equals(expType))) {
1014 doConvertAndCast(type);
1015 }
1016 }
1017
1018 /**
1019 * @param expression
1020 */
1021 protected void evaluateExpression(Expression expression) {
1022 visitAndAutoboxBoolean(expression);
1023 //expression.visit(this);
1024
1025 Expression assignExpr = createReturnLHSExpression(expression);
1026 if (assignExpr != null) {
1027 leftHandExpression = false;
1028 assignExpr.visit(this);
1029 }
1030 }
1031
1032 public void visitExpressionStatement(ExpressionStatement statement) {
1033 onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName());
1034
1035 Expression expression = statement.getExpression();
1036 // disabled in favor of JIT resolving
1037 // if (ENABLE_EARLY_BINDING)
1038 // expression.resolve(this);
1039
1040 visitAndAutoboxBoolean(expression);
1041
1042 if (isPopRequired(expression)) {
1043 cv.visitInsn(POP);
1044 }
1045 }
1046
1047 // Expressions
1048 //-------------------------------------------------------------------------
1049
1050 public void visitBinaryExpression(BinaryExpression expression) {
1051 onLineNumber(expression, "visitBinaryExpression: \"" + expression.getOperation().getText() + "\" ");
1052 switch (expression.getOperation().getType()) {
1053 case Types.EQUAL : // = assignment
1054 evaluateEqual(expression);
1055 break;
1056
1057 case Types.COMPARE_IDENTICAL : // ===
1058 evaluateBinaryExpression(compareIdenticalMethod, expression);
1059 break;
1060
1061 case Types.COMPARE_EQUAL : // ==
1062 evaluateBinaryExpression(compareEqualMethod, expression);
1063 break;
1064
1065 case Types.COMPARE_NOT_EQUAL :
1066 evaluateBinaryExpression(compareNotEqualMethod, expression);
1067 break;
1068
1069 case Types.COMPARE_TO :
1070 evaluateCompareTo(expression);
1071 break;
1072
1073 case Types.COMPARE_GREATER_THAN :
1074 evaluateBinaryExpression(compareGreaterThanMethod, expression);
1075 break;
1076
1077 case Types.COMPARE_GREATER_THAN_EQUAL :
1078 evaluateBinaryExpression(compareGreaterThanEqualMethod, expression);
1079 break;
1080
1081 case Types.COMPARE_LESS_THAN :
1082 evaluateBinaryExpression(compareLessThanMethod, expression);
1083 break;
1084
1085 case Types.COMPARE_LESS_THAN_EQUAL :
1086 evaluateBinaryExpression(compareLessThanEqualMethod, expression);
1087 break;
1088
1089 case Types.LOGICAL_AND :
1090 evaluateLogicalAndExpression(expression);
1091 break;
1092
1093 case Types.LOGICAL_OR :
1094 evaluateLogicalOrExpression(expression);
1095 break;
1096
1097 case Types.BITWISE_AND :
1098 evaluateBinaryExpression("and", expression);
1099 break;
1100
1101 case Types.BITWISE_AND_EQUAL :
1102 evaluateBinaryExpressionWithAsignment("and", expression);
1103 break;
1104
1105 case Types.BITWISE_OR :
1106 evaluateBinaryExpression("or", expression);
1107 break;
1108
1109 case Types.BITWISE_OR_EQUAL :
1110 evaluateBinaryExpressionWithAsignment("or", expression);
1111 break;
1112
1113 case Types.BITWISE_XOR :
1114 evaluateBinaryExpression("xor", expression);
1115 break;
1116
1117 case Types.BITWISE_XOR_EQUAL :
1118 evaluateBinaryExpressionWithAsignment("xor", expression);
1119 break;
1120
1121 case Types.PLUS :
1122 evaluateBinaryExpression("plus", expression);
1123 break;
1124
1125 case Types.PLUS_EQUAL :
1126 evaluateBinaryExpressionWithAsignment("plus", expression);
1127 break;
1128
1129 case Types.MINUS :
1130 evaluateBinaryExpression("minus", expression);
1131 break;
1132
1133 case Types.MINUS_EQUAL :
1134 evaluateBinaryExpressionWithAsignment("minus", expression);
1135 break;
1136
1137 case Types.MULTIPLY :
1138 evaluateBinaryExpression("multiply", expression);
1139 break;
1140
1141 case Types.MULTIPLY_EQUAL :
1142 evaluateBinaryExpressionWithAsignment("multiply", expression);
1143 break;
1144
1145 case Types.DIVIDE :
1146 evaluateBinaryExpression("div", expression);
1147 break;
1148
1149 case Types.DIVIDE_EQUAL :
1150 //SPG don't use divide since BigInteger implements directly
1151 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
1152 evaluateBinaryExpressionWithAsignment("div", expression);
1153 break;
1154
1155 case Types.INTDIV :
1156 evaluateBinaryExpression("intdiv", expression);
1157 break;
1158
1159 case Types.INTDIV_EQUAL :
1160 evaluateBinaryExpressionWithAsignment("intdiv", expression);
1161 break;
1162
1163 case Types.MOD :
1164 evaluateBinaryExpression("mod", expression);
1165 break;
1166
1167 case Types.MOD_EQUAL :
1168 evaluateBinaryExpressionWithAsignment("mod", expression);
1169 break;
1170
1171 case Types.POWER :
1172 evaluateBinaryExpression("power", expression);
1173 break;
1174
1175 case Types.POWER_EQUAL :
1176 evaluateBinaryExpressionWithAsignment("power", expression);
1177 break;
1178
1179 case Types.LEFT_SHIFT :
1180 evaluateBinaryExpression("leftShift", expression);
1181 break;
1182
1183 case Types.LEFT_SHIFT_EQUAL :
1184 evaluateBinaryExpressionWithAsignment("leftShift", expression);
1185 break;
1186
1187 case Types.RIGHT_SHIFT :
1188 evaluateBinaryExpression("rightShift", expression);
1189 break;
1190
1191 case Types.RIGHT_SHIFT_EQUAL :
1192 evaluateBinaryExpressionWithAsignment("rightShift", expression);
1193 break;
1194
1195 case Types.RIGHT_SHIFT_UNSIGNED :
1196 evaluateBinaryExpression("rightShiftUnsigned", expression);
1197 break;
1198
1199 case Types.RIGHT_SHIFT_UNSIGNED_EQUAL :
1200 evaluateBinaryExpressionWithAsignment("rightShiftUnsigned", expression);
1201 break;
1202
1203 case Types.KEYWORD_INSTANCEOF :
1204 evaluateInstanceof(expression);
1205 break;
1206
1207 case Types.FIND_REGEX :
1208 evaluateBinaryExpression(findRegexMethod, expression);
1209 break;
1210
1211 case Types.MATCH_REGEX :
1212 evaluateBinaryExpression(matchRegexMethod, expression);
1213 break;
1214
1215 case Types.LEFT_SQUARE_BRACKET :
1216 if (leftHandExpression) {
1217 throwException("Should not be called here. Possible reason: postfix operation on array.");
1218 // This is handled right now in the evaluateEqual()
1219 // should support this here later
1220 //evaluateBinaryExpression("putAt", expression);
1221 } else {
1222 evaluateBinaryExpression("getAt", expression);
1223 }
1224 break;
1225
1226 default :
1227 throwException("Operation: " + expression.getOperation() + " not supported");
1228 }
1229 }
1230
1231 private void load(Expression exp) {
1232
1233 boolean wasLeft = leftHandExpression;
1234 leftHandExpression = false;
1235 // if (CREATE_DEBUG_INFO)
1236 // helper.mark("-- loading expression: " + exp.getClass().getName() +
1237 // " at [" + exp.getLineNumber() + ":" + exp.getColumnNumber() + "]");
1238 //exp.visit(this);
1239 visitAndAutoboxBoolean(exp);
1240 // if (CREATE_DEBUG_INFO)
1241 // helper.mark(" -- end of loading --");
1242
1243 leftHandExpression = wasLeft;
1244 }
1245
1246 public void visitPostfixExpression(PostfixExpression expression) {
1247 switch (expression.getOperation().getType()) {
1248 case Types.PLUS_PLUS :
1249 evaluatePostfixMethod("next", expression.getExpression());
1250 break;
1251 case Types.MINUS_MINUS :
1252 evaluatePostfixMethod("previous", expression.getExpression());
1253 break;
1254 }
1255 }
1256
1257 // store the data on the stack to the expression (variablem, property, field, etc.
1258 private void store(Expression expression) {
1259 if (expression instanceof BinaryExpression) {
1260 throwException("BinaryExpression appeared on LHS. ");
1261 }
1262 if (ASM_DEBUG) {
1263 if (expression instanceof VariableExpression) {
1264 helper.mark(((VariableExpression)expression).getName());
1265 }
1266 }
1267 boolean wasLeft = leftHandExpression;
1268 leftHandExpression = true;
1269 expression.visit(this);
1270 //evaluateExpression(expression);
1271 leftHandExpression = wasLeft;
1272 return;
1273 }
1274
1275 private void throwException(String s) {
1276 //throw new ClassGeneratorException(s + ". Source: " + classNode.getName() + ":[" + this.lineNumber + ":" + this.columnNumber + "]");
1277 throw new RuntimeParserException(s, currentASTNode);
1278 }
1279
1280 public void visitPrefixExpression(PrefixExpression expression) {
1281 switch (expression.getOperation().getType()) {
1282 case Types.PLUS_PLUS :
1283 evaluatePrefixMethod("next", expression.getExpression());
1284 break;
1285 case Types.MINUS_MINUS :
1286 evaluatePrefixMethod("previous", expression.getExpression());
1287 break;
1288 }
1289 }
1290
1291 public void visitClosureExpression(ClosureExpression expression) {
1292 ClassNode innerClass = createClosureClass(expression);
1293 addInnerClass(innerClass);
1294 String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass);
1295
1296 ClassNode owner = innerClass.getOuterClass();
1297
1298 passingClosureParams = true;
1299 List constructors = innerClass.getDeclaredConstructors();
1300 ConstructorNode node = (ConstructorNode) constructors.get(0);
1301 Parameter[] localVariableParams = node.getParameters();
1302
1303
1304 //
1305 // Define in the context any variables that will be
1306 // created inside the closure. Note that the first two
1307 // parameters are always _outerInstance and _delegate,
1308 // so we don't worry about them.
1309 //
1310 for (int i = 2; i < localVariableParams.length; i++) {
1311 Parameter param = localVariableParams[i];
1312 String name = param.getName();
1313
1314 if (variableStack.get(name) == null && classNode.getField(name) == null) {
1315 defineVariable(name, ClassHelper.OBJECT_TYPE); // todo should use param type is available
1316 }
1317 }
1318
1319 cv.visitTypeInsn(NEW, innerClassinternalName);
1320 cv.visitInsn(DUP);
1321 if (isStaticMethod() || classNode.isStaticClass()) {
1322 visitClassExpression(new ClassExpression(owner));
1323 }
1324 else {
1325 loadThisOrOwner();
1326 }
1327
1328 if (innerClass.getSuperClass()==ClassHelper.CLOSURE_TYPE) {
1329 if (isStaticMethod()) {
1330 /**
1331 * todo could maybe stash this expression in a JVM variable
1332 * from previous statement above
1333 */
1334 visitClassExpression(new ClassExpression(owner));
1335 }
1336 else {
1337 cv.visitVarInsn(ALOAD, 0);
1338 }
1339 }
1340
1341 //String prototype = "(L" + BytecodeHelper.getClassInternalName(ownerTypeName) + ";Ljava/lang/Object;";
1342
1343 // now lets load the various parameters we're passing
1344 for (int i = 2; i < localVariableParams.length; i++) {
1345 Parameter param = localVariableParams[i];
1346 String name = param.getName();
1347
1348 if (variableStack.get(name) == null) {
1349 visitFieldExpression(new FieldExpression(classNode.getField(name)));
1350 }
1351 else {
1352 visitVariableExpression(new VariableExpression(name));
1353 }
1354 //prototype = prototype + "L" + BytecodeHelper.getClassInternalName(param.getType()) + ";";
1355 }
1356 passingClosureParams = false;
1357
1358 // we may need to pass in some other constructors
1359 //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
1360 cv.visitMethodInsn(
1361 INVOKESPECIAL,
1362 innerClassinternalName,
1363 "<init>",
1364 BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, localVariableParams));
1365 }
1366
1367 /**
1368 * Loads either this object or if we're inside a closure then load the top level owner
1369 */
1370 protected void loadThisOrOwner() {
1371 if (isInnerClass()) {
1372 visitFieldExpression(new FieldExpression(classNode.getField("owner")));
1373 }
1374 else {
1375 cv.visitVarInsn(ALOAD, 0);
1376 }
1377 }
1378
1379 public void visitRegexExpression(RegexExpression expression) {
1380 expression.getRegex().visit(this);
1381 regexPattern.call(cv);
1382 }
1383
1384 /**
1385 * Generate byte code for constants
1386 * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a>
1387 */
1388 public void visitConstantExpression(ConstantExpression expression) {
1389 Object value = expression.getValue();
1390 helper.loadConstant(value);
1391 }
1392
1393 public void visitSpreadExpression(SpreadExpression expression) {
1394 Expression subExpression = expression.getExpression();
1395 subExpression.visit(this);
1396 spreadList.call(cv);
1397 }
1398
1399 public void visitSpreadMapExpression(SpreadMapExpression expression) {
1400 Expression subExpression = expression.getExpression();
1401 subExpression.visit(this);
1402 spreadMap.call(cv);
1403 }
1404
1405 public void visitMethodPointerExpression(MethodPointerExpression expression) {
1406 Expression subExpression = expression.getExpression();
1407 subExpression.visit(this);
1408 helper.loadConstant(expression.getMethodName());
1409 getMethodPointer.call(cv);
1410 }
1411
1412 public void visitNegationExpression(NegationExpression expression) {
1413 Expression subExpression = expression.getExpression();
1414 subExpression.visit(this);
1415 negation.call(cv);
1416 }
1417
1418 public void visitBitwiseNegExpression(BitwiseNegExpression expression) {
1419 Expression subExpression = expression.getExpression();
1420 subExpression.visit(this);
1421 bitNegation.call(cv);
1422 }
1423
1424 public void visitCastExpression(CastExpression expression) {
1425 ClassNode type = expression.getType();
1426 visitAndAutoboxBoolean(expression.getExpression());
1427 doConvertAndCast(type, expression.getExpression(), expression.isIgnoringAutoboxing(),false);
1428 }
1429
1430 public void visitNotExpression(NotExpression expression) {
1431 Expression subExpression = expression.getExpression();
1432 subExpression.visit(this);
1433
1434 // This is not the best way to do this. Javac does it by reversing the
1435 // underlying expressions but that proved
1436 // fairly complicated for not much gain. Instead we'll just use a
1437 // utility function for now.
1438 if (isComparisonExpression(expression.getExpression())) {
1439 notBoolean.call(cv);
1440 }
1441 else {
1442 notObject.call(cv);
1443 }
1444 }
1445
1446 /**
1447 * return a primitive boolean value of the BooleanExpresion.
1448 * @param expression
1449 */
1450 public void visitBooleanExpression(BooleanExpression expression) {
1451 expression.getExpression().visit(this);
1452
1453 if (!isComparisonExpression(expression.getExpression())) {
1454 // comment out for optimization when boolean values are not autoboxed for eg. function calls.
1455 // Class typeClass = expression.getExpression().getTypeClass();
1456 // if (typeClass != null && typeClass != boolean.class) {
1457 asBool.call(cv); // to return a primitive boolean
1458 // }
1459 }
1460 }
1461
1462 public void visitMethodCallExpression(MethodCallExpression call) {
1463 onLineNumber(call, "visitMethodCallExpression: \"" + call.getMethod() + "\":");
1464
1465 this.leftHandExpression = false;
1466
1467 Expression arguments = call.getArguments();
1468 /*
1469 * if (arguments instanceof TupleExpression) { TupleExpression
1470 * tupleExpression = (TupleExpression) arguments; int size =
1471 * tupleExpression.getExpressions().size(); if (size == 0) { arguments =
1472 * ConstantExpression.EMPTY_ARRAY; } }
1473 */
1474 boolean superMethodCall = MethodCallExpression.isSuperMethodCall(call);
1475 String method = call.getMethod();
1476 if (superMethodCall && method.equals("<init>")) {
1477 /** todo handle method types! */
1478 cv.visitVarInsn(ALOAD, 0);
1479 if (isInClosureConstructor()) { // br use the second param to init the super class (Closure)
1480 cv.visitVarInsn(ALOAD, 2);
1481 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
1482 }
1483 else {
1484 cv.visitVarInsn(ALOAD, 1);
1485 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V");
1486 }
1487 }
1488 else {
1489 // are we a local variable
1490 if (isThisExpression(call.getObjectExpression()) && isFieldOrVariable(method) && ! classNode.hasPossibleMethod(method, arguments)) {
1491 /*
1492 * if (arguments instanceof TupleExpression) { TupleExpression
1493 * tupleExpression = (TupleExpression) arguments; int size =
1494 * tupleExpression.getExpressions().size(); if (size == 1) {
1495 * arguments = (Expression)
1496 * tupleExpression.getExpressions().get(0); } }
1497 */
1498
1499 // lets invoke the closure method
1500 visitVariableExpression(new VariableExpression(method));
1501 arguments.visit(this);
1502 invokeClosureMethod.call(cv);
1503 }
1504 else {
1505 if (superMethodCall) {
1506 if (method.equals("super") || method.equals("<init>")) {
1507 ConstructorNode superConstructorNode = findSuperConstructor(call);
1508
1509 cv.visitVarInsn(ALOAD, 0);
1510
1511 loadArguments(superConstructorNode.getParameters(), arguments);
1512
1513 String descriptor = BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, superConstructorNode.getParameters());
1514 cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", descriptor);
1515 }
1516 else {
1517 MethodNode superMethodNode = findSuperMethod(call);
1518
1519 cv.visitVarInsn(ALOAD, 0);
1520
1521 loadArguments(superMethodNode.getParameters(), arguments);
1522
1523 String descriptor = BytecodeHelper.getMethodDescriptor(superMethodNode.getReturnType(), superMethodNode.getParameters());
1524 cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superMethodNode.getDeclaringClass()), method, descriptor);
1525 }
1526 }
1527 else {
1528 if (emptyArguments(arguments) && !call.isSafe() && !call.isSpreadSafe()) {
1529 call.getObjectExpression().visit(this);
1530 cv.visitLdcInsn(method);
1531 invokeNoArgumentsMethod.call(cv); // todo try if we can do early binding
1532 }
1533 else {
1534 if (argumentsUseStack(arguments)) {
1535
1536 arguments.visit(this);
1537
1538 Variable tv = visitASTOREInTemp(method + "_arg");
1539 int paramIdx = tv.getIndex();
1540
1541 call.getObjectExpression().visit(this); // xxx
1542
1543 cv.visitLdcInsn(method);
1544
1545 cv.visitVarInsn(ALOAD, paramIdx);
1546 removeVar(tv);
1547 }
1548 else {
1549 call.getObjectExpression().visit(this);
1550 cv.visitLdcInsn(method);
1551 arguments.visit(this);
1552 }
1553
1554 if (call.isSpreadSafe()) {
1555 invokeMethodSpreadSafeMethod.call(cv);
1556 }
1557 else if (call.isSafe()) {
1558 invokeMethodSafeMethod.call(cv);
1559 }
1560 else {
1561 invokeMethodMethod.call(cv);
1562 }
1563 }
1564 }
1565 }
1566 }
1567 }
1568
1569 /**
1570 * Loads and coerces the argument values for the given method call
1571 */
1572 protected void loadArguments(Parameter[] parameters, Expression expression) {
1573 TupleExpression argListExp = (TupleExpression) expression;
1574 List arguments = argListExp.getExpressions();
1575 for (int i = 0, size = arguments.size(); i < size; i++) {
1576 Expression argExp = argListExp.getExpression(i);
1577 Parameter param = parameters[i];
1578 visitAndAutoboxBoolean(argExp);
1579
1580 ClassNode type = param.getType();
1581 ClassNode expType = getExpressionType(argExp);
1582 if (!type.equals(expType)) {
1583 doConvertAndCast(type);
1584 }
1585 }
1586 }
1587
1588 /**
1589 * Attempts to find the method of the given name in a super class
1590 */
1591 protected MethodNode findSuperMethod(MethodCallExpression call) {
1592 String methodName = call.getMethod();
1593 TupleExpression argExpr = (TupleExpression) call.getArguments();
1594 int argCount = argExpr.getExpressions().size();
1595 ClassNode superClassNode = classNode.getSuperClass();
1596 if (superClassNode != null) {
1597 List methods = superClassNode.getMethods(methodName);
1598 for (Iterator iter = methods.iterator(); iter.hasNext(); ) {
1599 MethodNode method = (MethodNode) iter.next();
1600 if (method.getParameters().length == argCount) {
1601 return method;
1602 }
1603 }
1604 }
1605 throwException("No such method: " + methodName + " for class: " + classNode.getName());
1606 return null; // should not come here
1607 }
1608
1609 /**
1610 * Attempts to find the constructor in a super class
1611 */
1612 protected ConstructorNode findSuperConstructor(MethodCallExpression call) {
1613 TupleExpression argExpr = (TupleExpression) call.getArguments();
1614 int argCount = argExpr.getExpressions().size();
1615 ClassNode superClassNode = classNode.getSuperClass();
1616 if (superClassNode != null) {
1617 List constructors = superClassNode.getDeclaredConstructors();
1618 for (Iterator iter = constructors.iterator(); iter.hasNext(); ) {
1619 ConstructorNode constructor = (ConstructorNode) iter.next();
1620 if (constructor.getParameters().length == argCount) {
1621 return constructor;
1622 }
1623 }
1624 }
1625 throwException("No such constructor for class: " + classNode.getName());
1626 return null; // should not come here
1627 }
1628
1629 protected boolean emptyArguments(Expression arguments) {
1630 if (arguments instanceof TupleExpression) {
1631 TupleExpression tupleExpression = (TupleExpression) arguments;
1632 int size = tupleExpression.getExpressions().size();
1633 return size == 0;
1634 }
1635 return false;
1636 }
1637
1638 public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
1639 this.leftHandExpression = false;
1640
1641 Expression arguments = call.getArguments();
1642 if (emptyArguments(arguments)) {
1643 cv.visitLdcInsn(call.getOwnerType().getName());
1644 cv.visitLdcInsn(call.getMethod());
1645
1646 invokeStaticNoArgumentsMethod.call(cv);
1647 }
1648 else {
1649 if (arguments instanceof TupleExpression) {
1650 TupleExpression tupleExpression = (TupleExpression) arguments;
1651 int size = tupleExpression.getExpressions().size();
1652 if (size == 1) {
1653 arguments = (Expression) tupleExpression.getExpressions().get(0);
1654 }
1655 }
1656
1657 cv.visitLdcInsn(call.getOwnerType().getName());
1658 cv.visitLdcInsn(call.getMethod());
1659 arguments.visit(this);
1660
1661 invokeStaticMethodMethod.call(cv);
1662 }
1663 }
1664
1665 public void visitConstructorCallExpression(ConstructorCallExpression call) {
1666 onLineNumber(call, "visitConstructorCallExpression: \"" + call.getType().getName() + "\":");
1667 this.leftHandExpression = false;
1668
1669 Expression arguments = call.getArguments();
1670 if (arguments instanceof TupleExpression) {
1671 TupleExpression tupleExpression = (TupleExpression) arguments;
1672 int size = tupleExpression.getExpressions().size();
1673 if (size == 0) {
1674 arguments = null;
1675 }
1676 }
1677
1678 // lets check that the type exists
1679 ClassNode type = call.getType();
1680
1681 if (this.classNode != null) {
1682 // TODO: GROOVY-435
1683 pushClassTypeArgument(this.classNode, this.classNode);
1684 pushClassTypeArgument(this.classNode, type);
1685
1686 if (arguments != null) {
1687 arguments.visit(this);
1688 invokeConstructorAtMethod.call(cv);
1689 } else {
1690 invokeNoArgumentsConstructorAt.call(cv);
1691 }
1692 }
1693 else {
1694 pushClassTypeArgument(this.classNode, type);
1695
1696 if (arguments !=null) {
1697 arguments.visit(this);
1698 invokeConstructorOfMethod.call(cv);
1699 } else {
1700 invokeNoArgumentsConstructorOf.call(cv);
1701 }
1702 }
1703 }
1704
1705 private static String getStaticFieldName(ClassNode type) {
1706 String name = "class$" + BytecodeHelper.getClassInternalName(type).replace('/', '$').replace('[', '_').replace(';', '_');
1707 return name;
1708 }
1709
1710 protected void pushClassTypeArgument(ClassNode ownerType, ClassNode type) {
1711 String name = type.getName();
1712 String staticFieldName = getStaticFieldName(type);
1713 String ownerName = ownerType.getName().replace('.','/');
1714
1715 syntheticStaticFields.add(staticFieldName);
1716 cv.visitFieldInsn(GETSTATIC, ownerName, staticFieldName, "Ljava/lang/Class;");
1717 Label l0 = new Label();
1718 cv.visitJumpInsn(IFNONNULL, l0);
1719 cv.visitLdcInsn(name);
1720 cv.visitMethodInsn(INVOKESTATIC, ownerName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
1721 cv.visitInsn(DUP);
1722 cv.visitFieldInsn(PUTSTATIC, ownerName, staticFieldName, "Ljava/lang/Class;");
1723 Label l1 = new Label();
1724 cv.visitJumpInsn(GOTO, l1);
1725 cv.visitLabel(l0);
1726 cv.visitFieldInsn(GETSTATIC, ownerName, staticFieldName, "Ljava/lang/Class;");
1727 cv.visitLabel(l1);
1728 }
1729
1730 public void visitPropertyExpression(PropertyExpression expression) {
1731 Expression objectExpression = expression.getObjectExpression();
1732 if (isThisExpression(objectExpression)) {
1733 // lets use the field expression if its available
1734 String name = expression.getProperty();
1735 FieldNode field = classNode.getField(name);
1736 if (field != null) {
1737 visitFieldExpression(new FieldExpression(field));
1738 return;
1739 }
1740 }
1741
1742 // we need to clear the LHS flag to avoid "this." evaluating as ASTORE
1743 // rather than ALOAD
1744 boolean left = leftHandExpression;
1745 leftHandExpression = false;
1746 objectExpression.visit(this);
1747 leftHandExpression = left;
1748
1749 cv.visitLdcInsn(expression.getProperty());
1750
1751 if (isGroovyObject(objectExpression) && ! expression.isSafe()) {
1752 if (left) {
1753 setGroovyObjectPropertyMethod.call(cv);
1754 }
1755 else {
1756 getGroovyObjectPropertyMethod.call(cv);
1757 }
1758 }
1759 else {
1760 if (expression.isSafe()) {
1761 if (left) {
1762 setPropertySafeMethod2.call(cv);
1763 }
1764 else {
1765 if (expression.isSpreadSafe()) {
1766 getPropertySpreadSafeMethod.call(cv);
1767 }
1768 else {
1769 getPropertySafeMethod.call(cv);
1770 }
1771 }
1772 }
1773 else {
1774 if (left) {
1775 setPropertyMethod2.call(cv);
1776 }
1777 else {
1778 getPropertyMethod.call(cv);
1779 }
1780 }
1781 }
1782 }
1783
1784 public void visitAttributeExpression(AttributeExpression expression) {
1785 Expression objectExpression = expression.getObjectExpression();
1786 if (isThisExpression(objectExpression)) {
1787 // lets use the field expression if its available
1788 String name = expression.getProperty();
1789 FieldNode field = classNode.getField(name);
1790 if (field != null) {
1791 visitFieldExpression(new FieldExpression(field));
1792 return;
1793 }
1794 }
1795
1796 // we need to clear the LHS flag to avoid "this." evaluating as ASTORE
1797 // rather than ALOAD
1798 boolean left = leftHandExpression;
1799 leftHandExpression = false;
1800 objectExpression.visit(this);
1801 leftHandExpression = left;
1802
1803 cv.visitLdcInsn(expression.getProperty());
1804
1805 if (expression.isSafe()) {
1806 if (left) {
1807 setAttributeSafeMethod2.call(cv);
1808 }
1809 else {
1810 if (expression.isSpreadSafe()) {
1811 getAttributeSpreadSafeMethod.call(cv);
1812 }
1813 else {
1814 getAttributeSafeMethod.call(cv);
1815 }
1816 }
1817 }
1818 else {
1819 if (left) {
1820 setAttributeMethod2.call(cv);
1821 }
1822 else {
1823 getAttributeMethod.call(cv);
1824 }
1825 }
1826 }
1827
1828 protected boolean isGroovyObject(Expression objectExpression) {
1829 return isThisExpression(objectExpression);
1830 }
1831
1832 public void visitFieldExpression(FieldExpression expression) {
1833 FieldNode field = expression.getField();
1834
1835
1836 if (field.isStatic()) {
1837 if (leftHandExpression) {
1838 storeStaticField(expression);
1839 }
1840 else {
1841 loadStaticField(expression);
1842 }
1843 } else {
1844 if (leftHandExpression) {
1845 storeThisInstanceField(expression);
1846 }
1847 else {
1848 loadInstanceField(expression);
1849 }
1850 }
1851 }
1852
1853 /**
1854 *
1855 * @param fldExp
1856 */
1857 public void loadStaticField(FieldExpression fldExp) {
1858 FieldNode field = fldExp.getField();
1859 boolean holder = field.isHolder() && !isInClosureConstructor();
1860 ClassNode type = field.getType();
1861
1862 String ownerName = (field.getOwner().equals(classNode))
1863 ? internalClassName
1864 : BytecodeHelper.getClassInternalName(field.getOwner());
1865 if (holder) {
1866 cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
1867 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
1868 }
1869 else {
1870 cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
1871 if (ClassHelper.isPrimitiveType(type)) {
1872 helper.box(type);
1873 } else {
1874 }
1875 }
1876 }
1877
1878 /**
1879 * RHS instance field. should move most of the code in the BytecodeHelper
1880 * @param fldExp
1881 */
1882 public void loadInstanceField(FieldExpression fldExp) {
1883 FieldNode field = fldExp.getField();
1884 boolean holder = field.isHolder() && !isInClosureConstructor();
1885 ClassNode type = field.getType();
1886 String ownerName = (field.getOwner().equals(classNode))
1887 ? internalClassName
1888 : helper.getClassInternalName(field.getOwner());
1889
1890 cv.visitVarInsn(ALOAD, 0);
1891 cv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
1892
1893 if (holder) {
1894 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
1895 } else {
1896 if (ClassHelper.isPrimitiveType(type)) {
1897 helper.box(type);
1898 } else {
1899 }
1900 }
1901 }
1902
1903 public void storeThisInstanceField(FieldExpression expression) {
1904 FieldNode field = expression.getField();
1905
1906 boolean holder = field.isHolder() && !isInClosureConstructor();
1907 ClassNode type = field.getType();
1908
1909 String ownerName = (field.getOwner().equals(classNode)) ?
1910 internalClassName : BytecodeHelper.getClassInternalName(field.getOwner());
1911 if (holder) {
1912 Variable tv = visitASTOREInTemp(field.getName());
1913 int tempIndex = tv.getIndex();
1914 cv.visitVarInsn(ALOAD, 0);
1915 cv.visitFieldInsn(GETFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
1916 cv.visitVarInsn(ALOAD, tempIndex);
1917 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
1918 removeVar(tv);
1919 }
1920 else {
1921 if (isInClosureConstructor()) {
1922 helper.doCast(type);
1923 }
1924 else {
1925 doConvertAndCast(type);
1926 }
1927 //Variable tmpVar = defineVariable(createVariableName(field.getName()), "java.lang.Object", false);
1928 Variable tmpVar = defineVariable(createVariableName(field.getName()), field.getType(), false);
1929 //int tempIndex = tmpVar.getIndex();
1930 //helper.store(field.getType(), tempIndex);
1931 helper.store(tmpVar, MARK_START);
1932 helper.loadThis(); //cv.visitVarInsn(ALOAD, 0);
1933 helper.load(tmpVar);
1934 helper.putField(field, ownerName);
1935 //cv.visitFieldInsn(PUTFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
1936 // let's remove the temp var
1937 removeVar(tmpVar);
1938 }
1939 }
1940
1941
1942 public void storeStaticField(FieldExpression expression) {
1943 FieldNode field = expression.getField();
1944
1945 boolean holder = field.isHolder() && !isInClosureConstructor();
1946
1947 ClassNode type = field.getType();
1948
1949 String ownerName = (field.getOwner().equals(classNode))
1950 ? internalClassName
1951 : helper.getClassInternalName(field.getOwner());
1952 if (holder) {
1953 Variable tv = visitASTOREInTemp(field.getName());
1954 int tempIndex = tv.getIndex();
1955 cv.visitFieldInsn(GETSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
1956 cv.visitVarInsn(ALOAD, tempIndex);
1957 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
1958 removeVar(tv);
1959 }
1960 else {
1961 if (isInClosureConstructor()) {
1962 helper.doCast(type);
1963 }
1964 else {
1965 // this may be superfluous
1966 //doConvertAndCast(type);
1967 // use weaker cast
1968 helper.doCast(type);
1969 }
1970 cv.visitFieldInsn(PUTSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
1971 }
1972 }
1973
1974 protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first ) {
1975 FieldNode field = expression.getField();
1976 boolean isStatic = field.isStatic();
1977
1978 Variable fieldTemp = defineVariable(createVariableName(field.getName()), ClassHelper.OBJECT_TYPE, false);
1979 int valueIdx = fieldTemp.getIndex();
1980
1981 if (leftHandExpression && first) {
1982 cv.visitVarInsn(ASTORE, valueIdx);
1983 visitVariableStartLabel(fieldTemp);
1984 }
1985
1986 if (steps > 1 || !isStatic) {
1987 cv.visitVarInsn(ALOAD, 0);
1988 cv.visitFieldInsn(
1989 GETFIELD,
1990 internalClassName,
1991 "owner",
1992 BytecodeHelper.getTypeDescription(outerClassNode));
1993 }
1994
1995 if( steps == 1 ) {
1996 int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
1997 String ownerName = BytecodeHelper.getClassInternalName(outerClassNode);
1998
1999 if (leftHandExpression) {
2000 cv.visitVarInsn(ALOAD, valueIdx);
2001 boolean holder = field.isHolder() && !isInClosureConstructor();
2002 if ( !holder) {
2003 doConvertAndCast(field.getType());
2004 }
2005 }
2006 cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType()));
2007 if (!leftHandExpression) {
2008 if (ClassHelper.isPrimitiveType(field.getType())) {
2009 helper.box(field.getType());
2010 }
2011 }
2012 }
2013
2014 else {
2015 visitOuterFieldExpression( expression, outerClassNode.getOuterClass(), steps - 1, false );
2016 }
2017 }
2018
2019
2020
2021 /**
2022 * Visits a bare (unqualified) variable expression.
2023 */
2024
2025 public void visitVariableExpression(VariableExpression expression) {
2026
2027 String variableName = expression.getName();
2028
2029 //-----------------------------------------------------------------------
2030 // SPECIAL CASES
2031
2032 //
2033 // "this" for static methods is the Class instance
2034
2035 if (isStaticMethod() && variableName.equals("this")) {
2036 visitClassExpression(new ClassExpression(classNode));
2037 return; // <<< FLOW CONTROL <<<<<<<<<
2038 }
2039
2040 //
2041 // "super" also requires special handling
2042
2043 if (variableName.equals("super")) {
2044 visitClassExpression(new ClassExpression(classNode.getSuperClass()));
2045 return; // <<< FLOW CONTROL <<<<<<<<<
2046 }
2047
2048
2049 //
2050 // class names return a Class instance, too
2051
2052 // if (!variableName.equals("this")) {
2053 // String className = resolveClassName(variableName);
2054 // if (className != null) {
2055 // if (leftHandExpression) {
2056 // throw new RuntimeParserException(
2057 // "Cannot use a class expression on the left hand side of an assignment",
2058 // expression);
2059 // }
2060 // visitClassExpression(new ClassExpression(className));
2061 // return; // <<< FLOW CONTROL <<<<<<<<<
2062 // }
2063 // }
2064
2065
2066 //-----------------------------------------------------------------------
2067 // GENERAL VARIABLE LOOKUP
2068
2069
2070 //
2071 // We are handling only unqualified variables here. Therefore,
2072 // we do not care about accessors, because local access doesn't
2073 // go through them. Therefore, precedence is as follows:
2074 // 1) local variables, nearest block first
2075 // 2) class fields
2076 // 3) repeat search from 2) in next outer class
2077
2078 boolean handled = false;
2079 Variable variable = (Variable)variableStack.get( variableName );
2080
2081 if( variable != null ) {
2082
2083 if( variable.isProperty() ) {
2084 processPropertyVariable(variable );
2085 }
2086 else {
2087 processStackVariable(variable );
2088 }
2089
2090 handled = true;
2091 } else {
2092 //
2093 // Loop through outer classes for fields
2094
2095 int steps = 0;
2096 ClassNode currentClassNode = classNode;
2097 FieldNode field = null;
2098
2099 do {
2100 if( (field = currentClassNode.getField(variableName)) != null ) {
2101 if (methodNode == null || !methodNode.isStatic() || field.isStatic() )
2102 break; //this is a match. break out. todo to be tested
2103 }
2104 steps++;
2105
2106 } while( (currentClassNode = currentClassNode.getOuterClass()) != null );
2107
2108 if( field != null ) {
2109 processFieldAccess( variableName, field, steps );
2110 handled = true;
2111 }
2112 }
2113
2114 //
2115 // Finally, if unhandled, create a variable for it.
2116 // Except there a stack variable should be created,
2117 // we define the variable as a property accessor and
2118 // let other parts of the classgen report the error
2119 // if the property doesn't exist.
2120
2121 if( !handled ) {
2122 ClassNode variableType = expression.getType();
2123 variable = defineVariable( variableName, variableType );
2124
2125 if (leftHandExpression && variableType==ClassHelper.DYNAMIC_TYPE) {
2126 variable.setDynamicTyped(true); // false by default
2127 }
2128 else {
2129 variable.setDynamicTyped(false);
2130 }
2131
2132 if( isInScriptBody() || !leftHandExpression ) { // todo problematic: if on right hand not defined, should I report undefined var error?
2133 variable.setProperty( true );
2134 processPropertyVariable(variable );
2135 }
2136 else {
2137 processStackVariable(variable );
2138 }
2139 }
2140 }
2141
2142
2143 protected void processStackVariable(Variable variable ) {
2144 boolean holder = variable.isHolder() && !passingClosureParams;
2145
2146 if( leftHandExpression ) {
2147 helper.storeVar(variable, holder);
2148 }
2149 else {
2150 helper.loadVar(variable, holder);
2151 }
2152 if (ASM_DEBUG) {
2153 helper.mark("var: " + variable.getName());
2154 }
2155 }
2156
2157 private void visitVariableStartLabel(Variable variable) {
2158 if (CREATE_DEBUG_INFO) {
2159 Label l = variable.getStartLabel();
2160 if (l != null) {
2161 cv.visitLabel(l);
2162 } else {
2163 System.out.println("start label == null! what to do about this?");
2164 }
2165 }
2166 }
2167
2168 protected void processPropertyVariable(Variable variable ) {
2169 String name = variable.getName();
2170 if (variable.isHolder() && passingClosureParams && isInScriptBody() ) {
2171 // lets create a ScriptReference to pass into the closure
2172 cv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference");
2173 cv.visitInsn(DUP);
2174
2175 loadThisOrOwner();
2176 cv.visitLdcInsn(name);
2177
2178 cv.visitMethodInsn(
2179 INVOKESPECIAL,
2180 "org/codehaus/groovy/runtime/ScriptReference",
2181 "<init>",
2182 "(Lgroovy/lang/Script;Ljava/lang/String;)V");
2183 }
2184 else {
2185 visitPropertyExpression(new PropertyExpression(VariableExpression.THIS_EXPRESSION, name));
2186 }
2187 }
2188
2189
2190 protected void processFieldAccess( String name, FieldNode field, int steps ) {
2191 FieldExpression expression = new FieldExpression(field);
2192
2193 if( steps == 0 ) {
2194 visitFieldExpression( expression );
2195 }
2196 else {
2197 visitOuterFieldExpression( expression, classNode.getOuterClass(), steps, true );
2198 }
2199 }
2200
2201
2202
2203 /**
2204 * @return true if we are in a script body, where all variables declared are no longer
2205 * local variables but are properties
2206 */
2207 protected boolean isInScriptBody() {
2208 if (classNode.isScriptBody()) {
2209 return true;
2210 }
2211 else {
2212 return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
2213 }
2214 }
2215
2216 /**
2217 * @return true if this expression will have left a value on the stack
2218 * that must be popped
2219 */
2220 protected boolean isPopRequired(Expression expression) {
2221 if (expression instanceof MethodCallExpression) {
2222 if (expression.getType()==ClassHelper.VOID_TYPE) { // nothing on the stack
2223 return false;
2224 } else {
2225 return !MethodCallExpression.isSuperMethodCall((MethodCallExpression) expression);
2226 }
2227 }
2228 if (expression instanceof BinaryExpression) {
2229 BinaryExpression binExp = (BinaryExpression) expression;
2230 switch (binExp.getOperation().getType()) { // br todo should leave a copy of the value on the stack for all the assignemnt.
2231 // 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
2232 // case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAsignment()
2233 // case Types.MINUS_EQUAL :
2234 // case Types.MULTIPLY_EQUAL :
2235 // case Types.DIVIDE_EQUAL :
2236 // case Types.INTDIV_EQUAL :
2237 // case Types.MOD_EQUAL :
2238 // return false;
2239 }
2240 }
2241 return true;
2242 }
2243
2244 protected boolean firstStatementIsSuperInit(Statement code) {
2245 ExpressionStatement expStmt = null;
2246 if (code instanceof ExpressionStatement) {
2247 expStmt = (ExpressionStatement) code;
2248 }
2249 else if (code instanceof BlockStatement) {
2250 BlockStatement block = (BlockStatement) code;
2251 if (!block.getStatements().isEmpty()) {
2252 Object expr = block.getStatements().get(0);
2253 if (expr instanceof ExpressionStatement) {
2254 expStmt = (ExpressionStatement) expr;
2255 }
2256 }
2257 }
2258 if (expStmt != null) {
2259 Expression expr = expStmt.getExpression();
2260 if (expr instanceof MethodCallExpression) {
2261 MethodCallExpression call = (MethodCallExpression) expr;
2262 if (MethodCallExpression.isSuperMethodCall(call)) {
2263 // not sure which one is constantly used as the super class ctor call. To cover both for now
2264 return call.getMethod().equals("<init>") || call.getMethod().equals("super");
2265 }
2266 }
2267 }
2268 return false;
2269 }
2270
2271 protected void createSyntheticStaticFields() {
2272 for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
2273 String staticFieldName = (String) iter.next();
2274 // generate a field node
2275 cw.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null);
2276 }
2277
2278 if (!syntheticStaticFields.isEmpty()) {
2279 cv =
2280 cw.visitMethod(
2281 ACC_STATIC + ACC_SYNTHETIC,
2282 "class$",
2283 "(Ljava/lang/String;)Ljava/lang/Class;",
2284 null,
2285 null);
2286 helper = new BytecodeHelper(cv);
2287
2288 Label l0 = new Label();
2289 cv.visitLabel(l0);
2290 cv.visitVarInsn(ALOAD, 0);
2291 cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
2292 Label l1 = new Label();
2293 cv.visitLabel(l1);
2294 cv.visitInsn(ARETURN);
2295 Label l2 = new Label();
2296 cv.visitLabel(l2);
2297 cv.visitVarInsn(ASTORE, 1);
2298 cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
2299 cv.visitInsn(DUP);
2300 cv.visitVarInsn(ALOAD, 1);
2301 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;");
2302 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V");
2303 cv.visitInsn(ATHROW);
2304 cv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
2305 cv.visitMaxs(3, 2);
2306
2307 cw.visitEnd();
2308 }
2309 }
2310
2311 /** load class object on stack */
2312 public void visitClassExpression(ClassExpression expression) {
2313 ClassNode type = expression.getType();
2314 //type = checkValidType(type, expression, "Must be a valid type name for a constructor call");
2315
2316
2317 if (ClassHelper.isPrimitiveType(type)) {
2318 ClassNode objectType = ClassHelper.getWrapper(type);
2319 cv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;");
2320 }
2321 else {
2322 final String staticFieldName =
2323 (type.equals(classNode)) ? "class$0" : getStaticFieldName(type);
2324
2325 syntheticStaticFields.add(staticFieldName);
2326
2327 cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
2328 Label l0 = new Label();
2329 cv.visitJumpInsn(IFNONNULL, l0);
2330 cv.visitLdcInsn(type.getName());
2331 cv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
2332 cv.visitInsn(DUP);
2333 cv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
2334 Label l1 = new Label();
2335 cv.visitJumpInsn(GOTO, l1);
2336 cv.visitLabel(l0);
2337 cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
2338 cv.visitLabel(l1);
2339 }
2340 }
2341
2342 public void visitRangeExpression(RangeExpression expression) {
2343 leftHandExpression = false;
2344 expression.getFrom().visit(this);
2345
2346 leftHandExpression = false;
2347 expression.getTo().visit(this);
2348
2349 helper.pushConstant(expression.isInclusive());
2350
2351 createRangeMethod.call(cv);
2352 }
2353
2354 public void visitMapEntryExpression(MapEntryExpression expression) {
2355 }
2356
2357 public void visitMapExpression(MapExpression expression) {
2358 List entries = expression.getMapEntryExpressions();
2359 int size = entries.size();
2360 helper.pushConstant(size * 2);
2361
2362 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2363
2364 int i = 0;
2365 for (Iterator iter = entries.iterator(); iter.hasNext();) {
2366 Object object = iter.next();
2367 MapEntryExpression entry = (MapEntryExpression) object;
2368
2369 cv.visitInsn(DUP);
2370 helper.pushConstant(i++);
2371 visitAndAutoboxBoolean(entry.getKeyExpression());
2372 cv.visitInsn(AASTORE);
2373
2374 cv.visitInsn(DUP);
2375 helper.pushConstant(i++);
2376 visitAndAutoboxBoolean(entry.getValueExpression());
2377 cv.visitInsn(AASTORE);
2378 }
2379 createMapMethod.call(cv);
2380 }
2381
2382 public void visitTupleExpression(TupleExpression expression) {
2383 int size = expression.getExpressions().size();
2384
2385 helper.pushConstant(size);
2386
2387 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2388
2389 for (int i = 0; i < size; i++) {
2390 cv.visitInsn(DUP);
2391 helper.pushConstant(i);
2392 visitAndAutoboxBoolean(expression.getExpression(i));
2393 cv.visitInsn(AASTORE);
2394 }
2395 //createTupleMethod.call(cv);
2396 }
2397
2398 public void visitArrayExpression(ArrayExpression expression) {
2399 ClassNode type = expression.getType().getComponentType();
2400 String typeName = BytecodeHelper.getClassInternalName(type);
2401 Expression sizeExpression = expression.getSizeExpression();
2402
2403 int size=0;
2404 if (sizeExpression != null) {
2405 // lets convert to an int
2406 visitAndAutoboxBoolean(sizeExpression);
2407 asIntMethod.call(cv);
2408 } else {
2409 size = expression.getExpressions().size();
2410 helper.pushConstant(size);
2411 }
2412
2413 int storeIns=AASTORE;
2414 if (ClassHelper.isPrimitiveType(type)) {
2415 int primType=0;
2416 if (type==ClassHelper.boolean_TYPE) {
2417 primType = T_BOOLEAN;
2418 storeIns = BASTORE;
2419 } else if (type==ClassHelper.char_TYPE) {
2420 primType = T_CHAR;
2421 storeIns = CASTORE;
2422 } else if (type==ClassHelper.float_TYPE) {
2423 primType = T_FLOAT;
2424 storeIns = FASTORE;
2425 } else if (type==ClassHelper.double_TYPE) {
2426 primType = T_DOUBLE;
2427 storeIns = DASTORE;
2428 } else if (type==ClassHelper.byte_TYPE) {
2429 primType = T_BYTE;
2430 storeIns = BASTORE;
2431 } else if (type==ClassHelper.short_TYPE) {
2432 primType = T_SHORT;
2433 storeIns = SASTORE;
2434 } else if (type==ClassHelper.int_TYPE) {
2435 primType = T_INT;
2436 storeIns=IASTORE;
2437 } else if (type==ClassHelper.long_TYPE) {
2438 primType = T_LONG;
2439 storeIns = LASTORE;
2440 }
2441 cv.visitIntInsn(NEWARRAY, primType);
2442 } else {
2443 cv.visitTypeInsn(ANEWARRAY, typeName);
2444 }
2445
2446 for (int i = 0; i < size; i++) {
2447 cv.visitInsn(DUP);
2448 helper.pushConstant(i);
2449 Expression elementExpression = expression.getExpression(i);
2450 if (elementExpression == null) {
2451 ConstantExpression.NULL.visit(this);
2452 } else {
2453 if (!type.equals(elementExpression.getType())) {
2454 visitCastExpression(new CastExpression(type, elementExpression, true));
2455 } else {
2456 visitAndAutoboxBoolean(elementExpression);
2457 }
2458 }
2459 cv.visitInsn(storeIns);
2460 }
2461
2462 if (ClassHelper.isPrimitiveType(type)) {
2463 int par = defineVariable("par",ClassHelper.OBJECT_TYPE).getIndex();
2464 cv.visitVarInsn(ASTORE, par);
2465 cv.visitVarInsn(ALOAD, par);
2466 }
2467 }
2468
2469 public void visitListExpression(ListExpression expression) {
2470 int size = expression.getExpressions().size();
2471 helper.pushConstant(size);
2472
2473 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2474
2475 for (int i = 0; i < size; i++) {
2476 cv.visitInsn(DUP);
2477 helper.pushConstant(i);
2478 visitAndAutoboxBoolean(expression.getExpression(i));
2479 cv.visitInsn(AASTORE);
2480 }
2481 createListMethod.call(cv);
2482 }
2483
2484 public void visitGStringExpression(GStringExpression expression) {
2485 int size = expression.getValues().size();
2486 helper.pushConstant(size);
2487
2488 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2489
2490 for (int i = 0; i < size; i++) {
2491 cv.visitInsn(DUP);
2492 helper.pushConstant(i);
2493 visitAndAutoboxBoolean(expression.getValue(i));
2494 cv.visitInsn(AASTORE);
2495 }
2496
2497 Variable tv = visitASTOREInTemp("iterator");
2498 int paramIdx = tv.getIndex();
2499
2500 ClassNode innerClass = createGStringClass(expression);
2501 addInnerClass(innerClass);
2502 String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass);
2503
2504 cv.visitTypeInsn(NEW, innerClassinternalName);
2505 cv.visitInsn(DUP);
2506 cv.visitVarInsn(ALOAD, paramIdx);
2507
2508 cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", "([Ljava/lang/Object;)V");
2509 removeVar(tv);
2510 }
2511
2512 private Variable visitASTOREInTemp(String s) {
2513 return storeInTemp(s, ClassHelper.OBJECT_TYPE);
2514 }
2515
2516 public void visitAnnotations(AnnotatedNode node) {
2517 Map annotionMap = node.getAnnotations();
2518 if (annotionMap.isEmpty()) return;
2519 Iterator it = annotionMap.values().iterator();
2520 while (it.hasNext()) {
2521 AnnotationNode an = (AnnotationNode) it.next();
2522 //skip builtin properties
2523 if (an.isBuiltIn()) continue;
2524 ClassNode type = an.getClassNode();
2525
2526 String clazz = type.getName();
2527 AnnotationVisitor av = cw.visitAnnotation(BytecodeHelper.formatNameForClassLoading(clazz),false);
2528
2529 Iterator mIt = an.getMembers().keySet().iterator();
2530 while (mIt.hasNext()) {
2531 String name = (String) mIt.next();
2532 ConstantExpression exp = (ConstantExpression) an.getMember(name);
2533 av.visit(name,exp.getValue());
2534 }
2535 av.visitEnd();
2536 }
2537 }
2538
2539
2540 // Implementation methods
2541 //-------------------------------------------------------------------------
2542 protected boolean addInnerClass(ClassNode innerClass) {
2543 innerClass.setModule(classNode.getModule());
2544 return innerClasses.add(innerClass);
2545 }
2546
2547 protected ClassNode createClosureClass(ClosureExpression expression) {
2548 ClassNode owner = getOutermostClass();
2549 ClassNode outerClass = owner;
2550 String name = owner.getName() + "$"
2551 + context.getNextClosureInnerName(owner, classNode, methodNode); // br added a more infomative name
2552 boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass();
2553 if (staticMethodOrInStaticClass) {
2554 outerClass = ClassHelper.make(Class.class);
2555 }
2556 Parameter[] parameters = expression.getParameters();
2557 if (parameters == null || parameters.length == 0) {
2558 // lets create a default 'it' parameter
2559 parameters = new Parameter[] { new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL)};
2560 }
2561
2562 Parameter[] localVariableParams = getClosureSharedVariables(expression);
2563
2564 InnerClassNode answer = new InnerClassNode(owner, name, 0, ClassHelper.CLOSURE_TYPE); // clsures are local inners and not public
2565 answer.setEnclosingMethod(this.methodNode);
2566 answer.setSynthetic(true);
2567
2568 if (staticMethodOrInStaticClass) {
2569 answer.setStaticClass(true);
2570 }
2571 if (isInScriptBody()) {
2572 answer.setScriptBody(true);
2573 }
2574 MethodNode method =
2575 answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, expression.getCode());
2576
2577 method.setLineNumber(expression.getLineNumber());
2578 method.setColumnNumber(expression.getColumnNumber());
2579
2580 VariableScope varScope = expression.getVariableScope();
2581 if (varScope == null) {
2582 throw new RuntimeException(
2583 "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
2584 }
2585 else {
2586 method.setVariableScope(varScope);
2587 }
2588 if (parameters.length > 1
2589 || (parameters.length == 1
2590 && parameters[0].getType() != null
2591 && parameters[0].getType() != ClassHelper.OBJECT_TYPE)) {
2592
2593 // lets add a typesafe call method
2594 answer.addMethod(
2595 "call",
2596 ACC_PUBLIC,
2597 ClassHelper.OBJECT_TYPE,
2598 parameters,
2599 new ReturnStatement(
2600 new MethodCallExpression(
2601 VariableExpression.THIS_EXPRESSION,
2602 "doCall",
2603 new ArgumentListExpression(parameters))));
2604 }
2605
2606 FieldNode ownerField = answer.addField("owner", ACC_PRIVATE, outerClass, null);
2607
2608 // lets make the constructor
2609 BlockStatement block = new BlockStatement();
2610 block.addStatement(
2611 new ExpressionStatement(
2612 new MethodCallExpression(
2613 new VariableExpression("super"),
2614 "<init>",
2615 new VariableExpression("_outerInstance"))));
2616 block.addStatement(
2617 new ExpressionStatement(
2618 new BinaryExpression(
2619 new FieldExpression(ownerField),
2620 Token.newSymbol(Types.EQUAL, -1, -1),
2621 new VariableExpression("_outerInstance"))));
2622
2623 // lets assign all the parameter fields from the outer context
2624 for (int i = 0; i < localVariableParams.length; i++) {
2625 Parameter param = localVariableParams[i];
2626 String paramName = param.getName();
2627 boolean holder = mutableVars.contains(paramName);
2628 Expression initialValue = null;
2629 ClassNode type = param.getType();
2630 FieldNode paramField = null;
2631 if (holder) {
2632 initialValue = new VariableExpression(paramName);
2633 ClassNode realType = type;
2634 type = ClassHelper.makeReference();
2635 param.setType(type);
2636 paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue);
2637 paramField.setHolder(true);
2638 String methodName = Verifier.capitalize(paramName);
2639
2640 // lets add a getter & setter
2641 Expression fieldExp = new FieldExpression(paramField);
2642 answer.addMethod(
2643 "get" + methodName,
2644 ACC_PUBLIC,
2645 realType,
2646 Parameter.EMPTY_ARRAY,
2647 new ReturnStatement(fieldExp));
2648
2649 /*
2650 answer.addMethod(
2651 "set" + methodName,
2652 ACC_PUBLIC,
2653 "void",
2654 new Parameter[] { new Parameter(realType, "__value") },
2655 new ExpressionStatement(
2656 new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
2657 */
2658 }
2659 else {
2660 PropertyNode propertyNode = answer.addProperty(paramName, ACC_PUBLIC, type, initialValue, null, null);
2661 paramField = propertyNode.getField();
2662 block.addStatement(
2663 new ExpressionStatement(
2664 new BinaryExpression(
2665 new FieldExpression(paramField),
2666 Token.newSymbol(Types.EQUAL, -1, -1),
2667 new VariableExpression(paramName))));
2668 }
2669 }
2670
2671 Parameter[] params = new Parameter[2 + localVariableParams.length];
2672 params[0] = new Parameter(outerClass, "_outerInstance");
2673 params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_delegate");
2674 System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
2675
2676 answer.addConstructor(ACC_PUBLIC, params, block);
2677 return answer;
2678 }
2679
2680 protected ClassNode getOutermostClass() {
2681 if (outermostClass == null) {
2682 outermostClass = classNode;
2683 while (outermostClass instanceof InnerClassNode) {
2684 outermostClass = outermostClass.getOuterClass();
2685 }
2686 }
2687 return outermostClass;
2688 }
2689
2690 protected ClassNode createGStringClass(GStringExpression expression) {
2691 ClassNode owner = classNode;
2692 if (owner instanceof InnerClassNode) {
2693 owner = owner.getOuterClass();
2694 }
2695 String outerClassName = owner.getName();
2696 String name = outerClassName + "$" + context.getNextInnerClassIdx();
2697 InnerClassNode answer = new InnerClassNode(owner, name, 0, ClassHelper.GSTRING_TYPE);
2698 answer.setEnclosingMethod(this.methodNode);
2699 FieldNode stringsField =
2700 answer.addField(
2701 "strings",
2702 ACC_PRIVATE /*| ACC_STATIC*/,
2703 ClassHelper.STRING_TYPE.makeArray(),
2704 new ArrayExpression(ClassHelper.STRING_TYPE, expression.getStrings()));
2705 answer.addMethod(
2706 "getStrings",
2707 ACC_PUBLIC,
2708 ClassHelper.STRING_TYPE.makeArray(),
2709 Parameter.EMPTY_ARRAY,
2710 new ReturnStatement(new FieldExpression(stringsField)));
2711 // lets make the constructor
2712 BlockStatement block = new BlockStatement();
2713 block.addStatement(
2714 new ExpressionStatement(
2715 new MethodCallExpression(new VariableExpression("super"), "<init>", new VariableExpression("values"))));
2716 Parameter[] contructorParams = new Parameter[] { new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "values")};
2717 answer.addConstructor(ACC_PUBLIC, contructorParams, block);
2718 return answer;
2719 }
2720
2721 protected void doConvertAndCast(ClassNode type) {
2722 if (type==ClassHelper.OBJECT_TYPE) return;
2723 if (isValidTypeForCast(type)) {
2724 visitClassExpression(new ClassExpression(type));
2725 asTypeMethod.call(cv);
2726 }
2727 helper.doCast(type);
2728 }
2729
2730 protected void evaluateLogicalOrExpression(BinaryExpression expression) {
2731 visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
2732 Label l0 = new Label();
2733 Label l2 = new Label();
2734 cv.visitJumpInsn(IFEQ, l0);
2735
2736 cv.visitLabel(l2);
2737
2738 visitConstantExpression(ConstantExpression.TRUE);
2739
2740 Label l1 = new Label();
2741 cv.visitJumpInsn(GOTO, l1);
2742 cv.visitLabel(l0);
2743
2744 visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
2745
2746 cv.visitJumpInsn(IFNE, l2);
2747
2748 visitConstantExpression(ConstantExpression.FALSE);
2749 cv.visitLabel(l1);
2750 }
2751
2752 // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for
2753 // consistancy.
2754 protected void evaluateLogicalAndExpression(BinaryExpression expression) {
2755 visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
2756 Label l0 = new Label();
2757 cv.visitJumpInsn(IFEQ, l0);
2758
2759 visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
2760
2761 cv.visitJumpInsn(IFEQ, l0);
2762
2763 visitConstantExpression(ConstantExpression.TRUE);
2764
2765 Label l1 = new Label();
2766 cv.visitJumpInsn(GOTO, l1);
2767 cv.visitLabel(l0);
2768
2769 visitConstantExpression(ConstantExpression.FALSE);
2770
2771 cv.visitLabel(l1);
2772 }
2773
2774 protected void evaluateBinaryExpression(String method, BinaryExpression expression) {
2775 Expression leftExpression = expression.getLeftExpression();
2776 leftHandExpression = false;
2777 leftExpression.visit(this);
2778 cv.visitLdcInsn(method);
2779 leftHandExpression = false;
2780 new ArgumentListExpression(new Expression[] { expression.getRightExpression()}).visit(this);
2781 // expression.getRightExpression().visit(this);
2782 invokeMethodMethod.call(cv);
2783 }
2784
2785 protected void evaluateCompareTo(BinaryExpression expression) {
2786 Expression leftExpression = expression.getLeftExpression();
2787 leftHandExpression = false;
2788 leftExpression.visit(this);
2789 if (isComparisonExpression(leftExpression)) {
2790 helper.boxBoolean();
2791 }
2792
2793 // if the right hand side is a boolean expression, we need to autobox
2794 Expression rightExpression = expression.getRightExpression();
2795 rightExpression.visit(this);
2796 if (isComparisonExpression(rightExpression)) {
2797 helper.boxBoolean();
2798 }
2799 compareToMethod.call(cv);
2800 }
2801
2802 protected void evaluateBinaryExpressionWithAsignment(String method, BinaryExpression expression) {
2803 Expression leftExpression = expression.getLeftExpression();
2804 if (leftExpression instanceof BinaryExpression) {
2805 BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
2806 if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
2807 // lets replace this assignment to a subscript operator with a
2808 // method call
2809 // e.g. x[5] += 10
2810 // -> (x, [], 5), =, x[5] + 10
2811 // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
2812
2813 MethodCallExpression methodCall =
2814 new MethodCallExpression(
2815 expression.getLeftExpression(),
2816 method,
2817 new ArgumentListExpression(new Expression[] { expression.getRightExpression()}));
2818
2819 Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression());
2820
2821 visitMethodCallExpression(
2822 new MethodCallExpression(
2823 leftBinExpr.getLeftExpression(),
2824 "putAt",
2825 new ArgumentListExpression(new Expression[] { safeIndexExpr, methodCall })));
2826 //cv.visitInsn(POP);
2827 return;
2828 }
2829 }
2830
2831 evaluateBinaryExpression(method, expression);
2832
2833 // br to leave a copy of rvalue on the stack. see also isPopRequired()
2834 cv.visitInsn(DUP);
2835
2836 leftHandExpression = true;
2837 evaluateExpression(leftExpression);
2838 leftHandExpression = false;
2839 }
2840
2841 private void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression bin) {
2842 evalBinaryExp_LateBinding(compareMethod, bin);
2843 }
2844
2845 protected void evalBinaryExp_LateBinding(MethodCaller compareMethod, BinaryExpression expression) {
2846 Expression leftExp = expression.getLeftExpression();
2847 Expression rightExp = expression.getRightExpression();
2848 load(leftExp);
2849 load(rightExp);
2850 compareMethod.call(cv);
2851 }
2852
2853 protected void evaluateEqual(BinaryExpression expression) {
2854
2855 Expression leftExpression = expression.getLeftExpression();
2856 if (leftExpression instanceof BinaryExpression) {
2857 BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
2858 if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
2859 // lets replace this assignment to a subscript operator with a
2860 // method call
2861 // e.g. x[5] = 10
2862 // -> (x, [], 5), =, 10
2863 // -> methodCall(x, "putAt", [5, 10])
2864
2865 visitMethodCallExpression(
2866 new MethodCallExpression(
2867 leftBinExpr.getLeftExpression(),
2868 "putAt",
2869 new ArgumentListExpression(
2870 new Expression[] { leftBinExpr.getRightExpression(), expression.getRightExpression()})));
2871 // cv.visitInsn(POP); //this is realted to isPopRequired()
2872 return;
2873 }
2874 }
2875
2876 // lets evaluate the RHS then hopefully the LHS will be a field
2877 leftHandExpression = false;
2878 Expression rightExpression = expression.getRightExpression();
2879
2880 ClassNode type = getLHSType(leftExpression);
2881 // lets not cast for primitive types as we handle these in field setting etc
2882 if (ClassHelper.isPrimitiveType(type)) {
2883 rightExpression.visit(this);
2884 } else {
2885 if (type!=ClassHelper.OBJECT_TYPE){
2886 visitCastExpression(new CastExpression(type, rightExpression));
2887 } else {
2888 visitAndAutoboxBoolean(rightExpression);
2889 }
2890 }
2891
2892 cv.visitInsn(DUP); // to leave a copy of the rightexpression value on the stack after the assignment.
2893 leftHandExpression = true;
2894 leftExpression.visit(this);
2895 leftHandExpression = false;
2896 }
2897
2898 /**
2899 * Deduces the type name required for some casting
2900 *
2901 * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
2902 */
2903 protected ClassNode getLHSType(Expression leftExpression) {
2904 do {
2905 // commented out. not quiteworking yet. would complain something like:
2906 //java.lang.ClassFormatError: Foo$1 (Illegal Field name "class$[Ljava$lang$String;")
2907 //
2908 // if (ENABLE_EARLY_BINDING) {
2909 // String type = leftExpression.getType();
2910 // if (type == null)
2911 // break;
2912 // return isValidTypeForCast(type) ? type : null;
2913 // }
2914 } while (false);
2915
2916 if (leftExpression instanceof VariableExpression) {
2917 VariableExpression varExp = (VariableExpression) leftExpression;
2918 ClassNode type = varExp.getType();
2919 if (isValidTypeForCast(type)) {
2920 return type;
2921 }
2922 String variableName = varExp.getName();
2923 Variable variable = (Variable) variableStack.get(variableName);
2924 if (variable != null) {
2925 if (variable.isHolder() || variable.isProperty()) {
2926 return variable.getType();
2927 }
2928 type = variable.getType();
2929 if (isValidTypeForCast(type)) {
2930 return type;
2931 }
2932 }
2933 else {
2934 FieldNode field = classNode.getField(variableName);
2935 if (field == null) {
2936 field = classNode.getOuterField(variableName);
2937 }
2938 if (field != null) {
2939 type = field.getType();
2940 if (!field.isHolder() && isValidTypeForCast(type)) {
2941 return type;
2942 }
2943 }
2944 }
2945 }
2946 else if (leftExpression instanceof FieldExpression) {
2947 FieldExpression fieldExp = (FieldExpression) leftExpression;
2948 ClassNode type = fieldExp.getType();
2949 if (isValidTypeForCast(type)) {
2950 return type;
2951 }
2952 }
2953 return ClassHelper.DYNAMIC_TYPE;
2954 }
2955
2956 protected boolean isValidTypeForCast(ClassNode type) {
2957 return type!=ClassHelper.DYNAMIC_TYPE && !type.getName().equals("groovy.lang.Reference") && !ClassHelper.isPrimitiveType(type);
2958 }
2959
2960 protected void visitAndAutoboxBoolean(Expression expression) {
2961 expression.visit(this);
2962
2963 if (isComparisonExpression(expression)) {
2964 helper.boxBoolean(); // convert boolean to Boolean
2965 }
2966 }
2967
2968 protected void evaluatePrefixMethod(String method, Expression expression) {
2969 if (isNonStaticField(expression) && ! isHolderVariable(expression) && !isStaticMethod()) {
2970 cv.visitVarInsn(ALOAD, 0);
2971 }
2972 expression.visit(this);
2973 cv.visitLdcInsn(method);
2974 invokeNoArgumentsMethod.call(cv);
2975
2976 leftHandExpression = true;
2977 expression.visit(this);
2978 leftHandExpression = false;
2979 expression.visit(this);
2980 }
2981
2982 protected void evaluatePostfixMethod(String method, Expression expression) {
2983 leftHandExpression = false;
2984 expression.visit(this);
2985
2986 Variable tv = visitASTOREInTemp("postfix_" + method);
2987 int tempIdx = tv.getIndex();
2988 cv.visitVarInsn(ALOAD, tempIdx);
2989
2990 cv.visitLdcInsn(method);
2991 invokeNoArgumentsMethod.call(cv);
2992
2993 store(expression);
2994
2995 cv.visitVarInsn(ALOAD, tempIdx);
2996 removeVar(tv);
2997 }
2998
2999 protected boolean isHolderVariable(Expression expression) {
3000 if (expression instanceof FieldExpression) {
3001 FieldExpression fieldExp = (FieldExpression) expression;
3002 return fieldExp.getField().isHolder();
3003 }
3004 if (expression instanceof VariableExpression) {
3005 VariableExpression varExp = (VariableExpression) expression;
3006 Variable variable = (Variable) variableStack.get(varExp.getName());
3007 if (variable != null) {
3008 return variable.isHolder();
3009 }
3010 FieldNode field = classNode.getField(varExp.getName());
3011 if (field != null) {
3012 return field.isHolder();
3013 }
3014 }
3015 return false;
3016 }
3017
3018 protected void evaluateInstanceof(BinaryExpression expression) {
3019 expression.getLeftExpression().visit(this);
3020 Expression rightExp = expression.getRightExpression();
3021 ClassNode classType = ClassHelper.DYNAMIC_TYPE;
3022 if (rightExp instanceof ClassExpression) {
3023 ClassExpression classExp = (ClassExpression) rightExp;
3024 classType = classExp.getType();
3025 }
3026 else {
3027 throw new RuntimeException(
3028 "Right hand side of the instanceof keyworld must be a class name, not: " + rightExp);
3029 }
3030 String classInternalName = BytecodeHelper.getClassInternalName(classType);
3031 cv.visitTypeInsn(INSTANCEOF, classInternalName);
3032 }
3033
3034 /**
3035 * @return true if the given argument expression requires the stack, in
3036 * which case the arguments are evaluated first, stored in the
3037 * variable stack and then reloaded to make a method call
3038 */
3039 protected boolean argumentsUseStack(Expression arguments) {
3040 return arguments instanceof TupleExpression || arguments instanceof ClosureExpression;
3041 }
3042
3043 /**
3044 * @return true if the given expression represents a non-static field
3045 */
3046 protected boolean isNonStaticField(Expression expression) {
3047 FieldNode field = null;
3048 if (expression instanceof VariableExpression) {
3049 VariableExpression varExp = (VariableExpression) expression;
3050 field = classNode.getField(varExp.getName());
3051 }
3052 else if (expression instanceof FieldExpression) {
3053 FieldExpression fieldExp = (FieldExpression) expression;
3054 field = classNode.getField(fieldExp.getFieldName());
3055 }
3056 else if (expression instanceof PropertyExpression) {
3057 PropertyExpression fieldExp = (PropertyExpression) expression;
3058 field = classNode.getField(fieldExp.getProperty());
3059 }
3060 if (field != null) {
3061 return !field.isStatic();
3062 }
3063 return false;
3064 }
3065
3066 protected boolean isThisExpression(Expression expression) {
3067 if (expression instanceof VariableExpression) {
3068 VariableExpression varExp = (VariableExpression) expression;
3069 return varExp.getName().equals("this");
3070 }
3071 return false;
3072 }
3073
3074 /**
3075 * For assignment expressions, return a safe expression for the LHS we can use
3076 * to return the value
3077 */
3078 protected Expression createReturnLHSExpression(Expression expression) {
3079 if (expression instanceof BinaryExpression) {
3080 BinaryExpression binExpr = (BinaryExpression) expression;
3081 if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
3082 return createReusableExpression(binExpr.getLeftExpression());
3083 }
3084 }
3085 return null;
3086 }
3087
3088 protected Expression createReusableExpression(Expression expression) {
3089 ExpressionTransformer transformer = new ExpressionTransformer() {
3090 public Expression transform(Expression expression) {
3091 if (expression instanceof PostfixExpression) {
3092 PostfixExpression postfixExp = (PostfixExpression) expression;
3093 return postfixExp.getExpression();
3094 }
3095 else if (expression instanceof PrefixExpression) {
3096 PrefixExpression prefixExp = (PrefixExpression) expression;
3097 return prefixExp.getExpression();
3098 }
3099 return expression;
3100 }
3101 };
3102
3103 // could just be a postfix / prefix expression or nested inside some other expression
3104 return transformer.transform(expression.transformExpression(transformer));
3105 }
3106
3107 protected boolean isComparisonExpression(Expression expression) {
3108 if (expression instanceof BinaryExpression) {
3109 BinaryExpression binExpr = (BinaryExpression) expression;
3110 switch (binExpr.getOperation().getType()) {
3111 case Types.COMPARE_EQUAL :
3112 case Types.MATCH_REGEX :
3113 case Types.COMPARE_GREATER_THAN :
3114 case Types.COMPARE_GREATER_THAN_EQUAL :
3115 case Types.COMPARE_LESS_THAN :
3116 case Types.COMPARE_LESS_THAN_EQUAL :
3117 case Types.COMPARE_IDENTICAL :
3118 case Types.COMPARE_NOT_EQUAL :
3119 case Types.KEYWORD_INSTANCEOF :
3120 return true;
3121 }
3122 }
3123 else if (expression instanceof BooleanExpression) {
3124 return true;
3125 }
3126 return false;
3127 }
3128
3129 protected void onLineNumber(ASTNode statement, String message) {
3130 int line = statement.getLineNumber();
3131 int col = statement.getColumnNumber();
3132 this.currentASTNode = statement;
3133
3134 if (line >=0) {
3135 lineNumber = line;
3136 columnNumber = col;
3137 }
3138 if (CREATE_LINE_NUMBER_INFO && line >= 0 && cv != null) {
3139 Label l = new Label();
3140 cv.visitLabel(l);
3141 cv.visitLineNumber(line, l);
3142 if (ASM_DEBUG) {
3143 helper.mark(message + "[" + statement.getLineNumber() + ":" + statement.getColumnNumber() + "]");
3144 }
3145 }
3146 }
3147
3148 protected VariableScope getVariableScope() {
3149 if (variableScope == null) {
3150 if (methodNode != null) {
3151 // if we're a closure method we'll have our variable scope already created
3152 variableScope = methodNode.getVariableScope();
3153 }
3154 else if (constructorNode != null) {
3155 variableScope = constructorNode.getVariableScope();
3156 }
3157 else {
3158 throw new RuntimeException("Can't create a variable scope outside of a method or constructor");
3159 }
3160 }
3161 return variableScope;
3162 }
3163
3164 /**
3165 * @return a list of parameters for each local variable which needs to be
3166 * passed into a closure
3167 */
3168 protected Parameter[] getClosureSharedVariables(ClosureExpression expression) {
3169 List vars = new ArrayList();
3170
3171 //
3172 // First up, get the scopes for outside and inside the closure.
3173 // The inner scope must cover all nested closures, as well, as
3174 // everything that will be needed must be imported.
3175
3176 VariableScope outerScope = getVariableScope().createRecursiveParentScope();
3177 VariableScope innerScope = expression.getVariableScope();
3178 if (innerScope == null) {
3179 System.out.println(
3180 "No variable scope for: " + expression + " method: " + methodNode + " constructor: " + constructorNode);
3181 innerScope = new VariableScope(getVariableScope());
3182 }
3183 else {
3184 innerScope = innerScope.createRecursiveChildScope();
3185 }
3186
3187
3188 //
3189 // DeclaredVariables include any name that was assigned to within
3190 // the scope. ReferencedVariables include any name that was read
3191 // from within the scope. We get the sets from each and must piece
3192 // together the stack variable import list for the closure. Note
3193 // that we don't worry about field variables here, as we don't have
3194 // to do anything special with them. Stack variables, on the other
3195 // hand, have to be wrapped up in References for use.
3196
3197 Set outerDecls = outerScope.getDeclaredVariables();
3198 Set outerRefs = outerScope.getReferencedVariables();
3199 Set innerDecls = innerScope.getDeclaredVariables();
3200 Set innerRefs = innerScope.getReferencedVariables();
3201
3202
3203 //
3204 // So, we care about any name referenced in the closure UNLESS:
3205 // 1) it's not declared in the outer context;
3206 // 2) it's a parameter;
3207 // 3) it's a field in the context class that isn't overridden
3208 // by a stack variable in the outer context.
3209 //
3210 // BUG: We don't actually have the necessary information to do
3211 // this right! The outer declarations don't distinguish
3212 // between assignments and variable declarations. Therefore
3213 // we can't tell when field variables have been overridden
3214 // by stack variables in the outer context. This must
3215 // be fixed!
3216
3217 Set varSet = new HashSet();
3218 for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
3219 String var = (String) iter.next();
3220 // lets not pass in fields from the most-outer class, but pass in values from an outer closure
3221 if (outerDecls.contains(var) && (isNotFieldOfOutermostClass(var))) {
3222 ClassNode type = getVariableType(var);
3223 vars.add(new Parameter(type, var));
3224 varSet.add(var);
3225 }
3226 }
3227 for (Iterator iter = outerRefs.iterator(); iter.hasNext();) {
3228 String var = (String) iter.next();
3229 // lets not pass in fields from the most-outer class, but pass in values from an outer closure
3230 if (innerDecls.contains(var) && (isNotFieldOfOutermostClass(var)) && !varSet.contains(var)) {
3231 ClassNode type = getVariableType(var);
3232 vars.add(new Parameter(type, var));
3233 }
3234 }
3235
3236
3237 Parameter[] answer = new Parameter[vars.size()];
3238 vars.toArray(answer);
3239 return answer;
3240 }
3241
3242 protected boolean isNotFieldOfOutermostClass(String var) {
3243 //return classNode.getField(var) == null || isInnerClass();
3244 return getOutermostClass().getField(var) == null;
3245 }
3246
3247 protected void findMutableVariables() {
3248 /*
3249 VariableScopeCodeVisitor outerVisitor = new VariableScopeCodeVisitor(true);
3250 node.getCode().visit(outerVisitor);
3251
3252 addFieldsToVisitor(outerVisitor);
3253
3254 VariableScopeCodeVisitor innerVisitor = outerVisitor.getClosureVisitor();
3255 */
3256 VariableScope outerScope = getVariableScope();
3257
3258 // lets create a scope concatenating all the closure expressions
3259 VariableScope innerScope = outerScope.createCompositeChildScope();
3260
3261 Set outerDecls = outerScope.getDeclaredVariables();
3262 Set outerRefs = outerScope.getReferencedVariables();
3263 Set innerDecls = innerScope.getDeclaredVariables();
3264 Set innerRefs = innerScope.getReferencedVariables();
3265
3266 mutableVars.clear();
3267
3268 for (Iterator iter = innerDecls.iterator(); iter.hasNext();) {
3269 String var = (String) iter.next();
3270 if ((outerDecls.contains(var) || outerRefs.contains(var)) && classNode.getField(var) == null) {
3271 mutableVars.add(var);
3272 }
3273 }
3274
3275 // we may call the closure twice and modify the variable in the outer scope
3276 // so for now lets assume that all variables are mutable
3277 for (Iterator iter = innerRefs.iterator(); iter.hasNext();) {
3278 String var = (String) iter.next();
3279 if (outerDecls.contains(var) && classNode.getField(var) == null) {
3280 mutableVars.add(var);
3281 }
3282 }
3283
3284 // System.out.println();
3285 // System.out.println("method: " + methodNode + " classNode: " + classNode);
3286 // System.out.println("child scopes: " + outerScope.getChildren());
3287 // System.out.println("outerDecls: " + outerDecls);
3288 // System.out.println("outerRefs: " + outerRefs);
3289 // System.out.println("innerDecls: " + innerDecls);
3290 // System.out.println("innerRefs: " + innerRefs);
3291 }
3292
3293 private boolean isInnerClass() {
3294 return classNode instanceof InnerClassNode;
3295 }
3296
3297 protected ClassNode getVariableType(String name) {
3298 Variable variable = (Variable) variableStack.get(name);
3299 if (variable != null) {
3300 return variable.getType();
3301 }
3302 return ClassHelper.DYNAMIC_TYPE;
3303 }
3304
3305 protected void resetVariableStack(Parameter[] parameters) {
3306 lastVariableIndex = -1;
3307 variableStack.clear();
3308
3309 scope = new BlockScope(null);
3310 //pushBlockScope();
3311
3312 // lets push this onto the stack
3313 definingParameters = true;
3314 if (!isStaticMethod()) {
3315 defineVariable("this", classNode).getIndex();
3316 } // now lets create indices for the parameteres
3317 for (int i = 0; i < parameters.length; i++) {
3318 Parameter parameter = parameters[i];
3319 ClassNode type = parameter.getType();
3320 Variable v = defineVariable(parameter.getName(), type);
3321 int idx = v.getIndex();
3322 if (ClassHelper.isPrimitiveType(type)) {
3323 helper.load(type, idx);
3324 helper.box(type);
3325 cv.visitVarInsn(ASTORE, idx);
3326 }
3327 }
3328 definingParameters = false;
3329 }
3330
3331 protected void popScope() {
3332 int lastID = scope.getFirstVariableIndex();
3333
3334 List removeKeys = new ArrayList();
3335 for (Iterator iter = variableStack.entrySet().iterator(); iter.hasNext();) {
3336 Map.Entry entry = (Map.Entry) iter.next();
3337 String name = (String) entry.getKey();
3338 Variable value = (Variable) entry.getValue();
3339 if (value.getIndex() >= lastID) {
3340 removeKeys.add(name);
3341 }
3342 }
3343 for (Iterator iter = removeKeys.iterator(); iter.hasNext();) {
3344 Variable v = (Variable) variableStack.remove(iter.next());
3345 if (CREATE_DEBUG_INFO) { // set localvartable
3346 if (v != null) {
3347 visitVariableEndLabel(v);
3348 cv.visitLocalVariable(
3349 v.getName(),
3350 BytecodeHelper.getTypeDescription(v.getTypeName()),
3351 null,
3352 v.getStartLabel(),
3353 v.getEndLabel(),
3354 v.getIndex()
3355 );
3356 }
3357 }
3358 }
3359 scope = scope.getParent();
3360 }
3361
3362 void removeVar(Variable v ) {
3363 variableStack.remove(v.getName());
3364 if (CREATE_DEBUG_INFO) { // set localvartable
3365 Label endl = new Label();
3366 cv.visitLabel(endl);
3367 cv.visitLocalVariable(
3368 v.getName(),
3369 BytecodeHelper.getTypeDescription(v.getTypeName()),
3370 null,
3371 v.getStartLabel(),
3372 endl,
3373 v.getIndex()
3374 );
3375 }
3376 }
3377 private void visitVariableEndLabel(Variable v) {
3378 if (CREATE_DEBUG_INFO) {
3379 if(v.getEndLabel() == null) {
3380 Label end = new Label();
3381 v.setEndLabel(end);
3382 }
3383 cv.visitLabel(v.getEndLabel());
3384 }
3385 }
3386
3387 protected void pushBlockScope() {
3388 pushBlockScope(true, true);
3389 }
3390
3391 /**
3392 * create a new scope. Set break/continue label if the canXXX parameter is true. Otherwise
3393 * inherit parent's label.
3394 * @param canContinue true if the start of the scope can take continue label
3395 * @param canBreak true if the end of the scope can take break label
3396 */
3397 protected void pushBlockScope(boolean canContinue, boolean canBreak) {
3398 BlockScope parentScope = scope;
3399 scope = new BlockScope(parentScope);
3400 scope.setContinueLabel(canContinue ? new Label() : (parentScope == null ? null : parentScope.getContinueLabel()));
3401 scope.setBreakLabel(canBreak? new Label() : (parentScope == null ? null : parentScope.getBreakLabel()));
3402 scope.setFirstVariableIndex(getNextVariableID());
3403 }
3404
3405 /**
3406 * Defines the given variable in scope and assigns it to the stack
3407 */
3408 protected Variable defineVariable(String name, ClassNode type) {
3409 return defineVariable(name, type, true);
3410 }
3411
3412 private Variable defineVariable(String name, ClassNode type, boolean define) {
3413 Variable answer = (Variable) variableStack.get(name);
3414 if (answer == null) {
3415 lastVariableIndex = getNextVariableID();
3416 answer = new Variable(lastVariableIndex, type, name);
3417 if (mutableVars.contains(name)) {
3418 answer.setHolder(true);
3419 }
3420 variableStack.put(name, answer);
3421
3422 Label startLabel = new Label();
3423 answer.setStartLabel(startLabel);
3424 if (define) {
3425 if (definingParameters) {
3426 if (answer.isHolder()) {
3427 cv.visitTypeInsn(NEW, "groovy/lang/Reference"); // br todo to associate a label with the variable
3428 cv.visitInsn(DUP);
3429 cv.visitVarInsn(ALOAD, lastVariableIndex);
3430 cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V");
3431 cv.visitVarInsn(ASTORE, lastVariableIndex);
3432 cv.visitLabel(startLabel);
3433 }
3434 }
3435 else {
3436 // using new variable inside a comparison expression
3437 // so lets initialize it too
3438 if (answer.isHolder() && !isInScriptBody()) {
3439 //cv.visitVarInsn(ASTORE, lastVariableIndex + 1); // I might need this to set the reference value
3440
3441 cv.visitTypeInsn(NEW, "groovy/lang/Reference");
3442 cv.visitInsn(DUP);
3443 cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "()V");
3444
3445 cv.visitVarInsn(ASTORE, lastVariableIndex);
3446 cv.visitLabel(startLabel);
3447 //cv.visitVarInsn(ALOAD, idx + 1);
3448 }
3449 else {
3450 if (!leftHandExpression) { // new var on the RHS: init with null
3451 cv.visitInsn(ACONST_NULL);
3452 cv.visitVarInsn(ASTORE, lastVariableIndex);
3453 cv.visitLabel(startLabel);
3454 }
3455 }
3456 }
3457 }
3458 }
3459 return answer;
3460 }
3461
3462 private boolean isDoubleSizeVariable(ClassNode type) {
3463 return type==ClassHelper.long_TYPE || type==ClassHelper.double_TYPE;
3464 }
3465
3466 private int getNextVariableID() {
3467 int index = 0;
3468 for (Iterator iter = variableStack.values().iterator(); iter.hasNext();) {
3469 Variable var = (Variable) iter.next();
3470 if (isDoubleSizeVariable(var.getType())) {
3471 index += 2;
3472 } else {
3473 index++;
3474 }
3475 }
3476 return index;
3477 }
3478
3479 /** @return true if the given name is a local variable or a field */
3480 protected boolean isFieldOrVariable(String name) {
3481 return variableStack.containsKey(name) || classNode.getField(name) != null;
3482 }
3483
3484 /*protected String resolveClassName(String type) {
3485 return classNode.resolveClassName(type);
3486 }*/
3487
3488 protected String createVariableName(String type) {
3489 return "__" + type + (++tempVariableNameCounter);
3490 }
3491
3492 /**
3493 * @return if the type of the expression can be determined at compile time
3494 * then this method returns the type - otherwise null
3495 */
3496 protected ClassNode getExpressionType(Expression expression) {
3497 if (isComparisonExpression(expression)) {
3498 return ClassHelper.boolean_TYPE;
3499 }
3500 if (expression instanceof VariableExpression) {
3501 VariableExpression varExpr = (VariableExpression) expression;
3502 Variable variable = (Variable) variableStack.get(varExpr.getName());
3503 if (variable != null && !variable.isHolder()) {
3504 ClassNode type = variable.getType();
3505 if (! variable.isDynamicTyped()) return type;
3506 }
3507 }
3508 return expression.getType();
3509 }
3510
3511 protected boolean isInClosureConstructor() {
3512 return constructorNode != null
3513 && classNode.getOuterClass() != null
3514 && classNode.getSuperClass()==ClassHelper.CLOSURE_TYPE;
3515 }
3516
3517 protected boolean isStaticMethod() {
3518 if (methodNode == null) { // we're in a constructor
3519 return false;
3520 }
3521 return methodNode.isStatic();
3522 }
3523
3524 protected CompileUnit getCompileUnit() {
3525 CompileUnit answer = classNode.getCompileUnit();
3526 if (answer == null) {
3527 answer = context.getCompileUnit();
3528 }
3529 return answer;
3530 }
3531
3532 }