001 /*
002 $Id: AsmClassGenerator.java 4598 2006-12-22 20:21:21Z blackdrag $
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.GroovyObject;
049 import groovy.lang.GroovyRuntimeException;
050
051 import java.util.ArrayList;
052 import java.util.Collections;
053 import java.util.Comparator;
054 import java.util.HashSet;
055 import java.util.Iterator;
056 import java.util.LinkedList;
057 import java.util.List;
058 import java.util.Map;
059 import java.util.Set;
060 import java.util.logging.Logger;
061
062 import org.codehaus.groovy.GroovyBugError;
063 import org.codehaus.groovy.ast.ASTNode;
064 import org.codehaus.groovy.ast.AnnotatedNode;
065 import org.codehaus.groovy.ast.AnnotationNode;
066 import org.codehaus.groovy.ast.ClassHelper;
067 import org.codehaus.groovy.ast.ClassNode;
068 import org.codehaus.groovy.ast.CompileUnit;
069 import org.codehaus.groovy.ast.ConstructorNode;
070 import org.codehaus.groovy.ast.FieldNode;
071 import org.codehaus.groovy.ast.InnerClassNode;
072 import org.codehaus.groovy.ast.MethodNode;
073 import org.codehaus.groovy.ast.Parameter;
074 import org.codehaus.groovy.ast.PropertyNode;
075 import org.codehaus.groovy.ast.VariableScope;
076 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
077 import org.codehaus.groovy.ast.expr.ArrayExpression;
078 import org.codehaus.groovy.ast.expr.AttributeExpression;
079 import org.codehaus.groovy.ast.expr.BinaryExpression;
080 import org.codehaus.groovy.ast.expr.BitwiseNegExpression;
081 import org.codehaus.groovy.ast.expr.BooleanExpression;
082 import org.codehaus.groovy.ast.expr.CastExpression;
083 import org.codehaus.groovy.ast.expr.ClassExpression;
084 import org.codehaus.groovy.ast.expr.ClosureExpression;
085 import org.codehaus.groovy.ast.expr.ConstantExpression;
086 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
087 import org.codehaus.groovy.ast.expr.DeclarationExpression;
088 import org.codehaus.groovy.ast.expr.Expression;
089 import org.codehaus.groovy.ast.expr.ExpressionTransformer;
090 import org.codehaus.groovy.ast.expr.FieldExpression;
091 import org.codehaus.groovy.ast.expr.GStringExpression;
092 import org.codehaus.groovy.ast.expr.ListExpression;
093 import org.codehaus.groovy.ast.expr.MapEntryExpression;
094 import org.codehaus.groovy.ast.expr.MapExpression;
095 import org.codehaus.groovy.ast.expr.MethodCallExpression;
096 import org.codehaus.groovy.ast.expr.MethodPointerExpression;
097 import org.codehaus.groovy.ast.expr.NegationExpression;
098 import org.codehaus.groovy.ast.expr.NotExpression;
099 import org.codehaus.groovy.ast.expr.PostfixExpression;
100 import org.codehaus.groovy.ast.expr.PrefixExpression;
101 import org.codehaus.groovy.ast.expr.PropertyExpression;
102 import org.codehaus.groovy.ast.expr.RangeExpression;
103 import org.codehaus.groovy.ast.expr.RegexExpression;
104 import org.codehaus.groovy.ast.expr.SpreadExpression;
105 import org.codehaus.groovy.ast.expr.SpreadMapExpression;
106 import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
107 import org.codehaus.groovy.ast.expr.TernaryExpression;
108 import org.codehaus.groovy.ast.expr.TupleExpression;
109 import org.codehaus.groovy.ast.expr.VariableExpression;
110 import org.codehaus.groovy.ast.stmt.AssertStatement;
111 import org.codehaus.groovy.ast.stmt.BlockStatement;
112 import org.codehaus.groovy.ast.stmt.BreakStatement;
113 import org.codehaus.groovy.ast.stmt.CaseStatement;
114 import org.codehaus.groovy.ast.stmt.CatchStatement;
115 import org.codehaus.groovy.ast.stmt.ContinueStatement;
116 import org.codehaus.groovy.ast.stmt.DoWhileStatement;
117 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
118 import org.codehaus.groovy.ast.stmt.ForStatement;
119 import org.codehaus.groovy.ast.stmt.IfStatement;
120 import org.codehaus.groovy.ast.stmt.ReturnStatement;
121 import org.codehaus.groovy.ast.stmt.Statement;
122 import org.codehaus.groovy.ast.stmt.SwitchStatement;
123 import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
124 import org.codehaus.groovy.ast.stmt.ThrowStatement;
125 import org.codehaus.groovy.ast.stmt.TryCatchStatement;
126 import org.codehaus.groovy.ast.stmt.WhileStatement;
127 import org.codehaus.groovy.control.SourceUnit;
128 import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
129 import org.codehaus.groovy.syntax.RuntimeParserException;
130 import org.codehaus.groovy.syntax.Types;
131 import org.objectweb.asm.AnnotationVisitor;
132 import org.objectweb.asm.ClassVisitor;
133 import org.objectweb.asm.ClassWriter;
134 import org.objectweb.asm.Label;
135 import org.objectweb.asm.MethodVisitor;
136 import org.objectweb.asm.Opcodes;
137
138
139 /**
140 * Generates Java class versions of Groovy classes using ASM.
141 *
142 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
143 * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
144 * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
145 *
146 * @version $Revision: 4598 $
147 */
148 public class AsmClassGenerator extends ClassGenerator {
149
150 private Logger log = Logger.getLogger(getClass().getName());
151
152 private ClassVisitor cw;
153 private MethodVisitor cv;
154 private GeneratorContext context;
155
156 private String sourceFile;
157
158 // current class details
159 private ClassNode classNode;
160 private ClassNode outermostClass;
161 private String internalClassName;
162 private String internalBaseClassName;
163
164 /** maps the variable names to the JVM indices */
165 private CompileStack compileStack;
166
167 /** have we output a return statement yet */
168 private boolean outputReturn;
169
170 /** are we on the left or right of an expression */
171 private boolean leftHandExpression=false;
172 /**
173 * Notes for leftHandExpression:
174 * The default is false, that menas the right side is default.
175 * The right side means that variables are read and not written.
176 * Any change of leftHandExpression to true, should be made carefully.
177 * If such a change is needed, then it should be set to false as soon as
178 * possible, but most important in the same method. Setting
179 * leftHandExpression to false is needed for writing variables.
180 */
181
182 // method invocation
183 MethodCallerMultiAdapter invokeMethodOnCurrent = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"invokeMethodOnCurrent",true,false);
184 MethodCallerMultiAdapter invokeMethodOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"invokeMethodOnSuper",true,false);
185 MethodCallerMultiAdapter invokeMethod = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"invokeMethod",true,false);
186 MethodCallerMultiAdapter invokeStaticMethod = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"invokeStaticMethod",true,true);
187 MethodCallerMultiAdapter invokeNew = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"invokeNew",true,true);
188
189 // fields & properties
190 MethodCallerMultiAdapter setField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"setField",false,false);
191 MethodCallerMultiAdapter getField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"getField",false,false);
192 MethodCallerMultiAdapter setGroovyObjectField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"setGroovyObjectField",false,false);
193 MethodCallerMultiAdapter getGroovyObjectField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"getGroovyObjectField",false,false);
194 MethodCallerMultiAdapter setFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"setFieldOnSuper",false,false);
195 MethodCallerMultiAdapter getFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"getFieldOnSuper",false,false);
196
197 MethodCallerMultiAdapter setProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"setProperty",false,false);
198 MethodCallerMultiAdapter getProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"getProperty",false,false);
199 MethodCallerMultiAdapter setGroovyObjectProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"setGroovyObjectProperty",false,false);
200 MethodCallerMultiAdapter getGroovyObjectProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"getGroovyObjectProperty",false,false);
201 MethodCallerMultiAdapter setPropertyOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"setPropertyOnSuper",false,false);
202 MethodCallerMultiAdapter getPropertyOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class,"getPropertyOnSuper",false,false);
203
204 // iterator
205 MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
206 MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
207 // assert
208 MethodCaller assertFailedMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "assertFailed");
209 // isCase
210 MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");
211 //compare
212 MethodCaller compareIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareIdentical");
213 MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual");
214 MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual");
215 MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo");
216 MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan");
217 MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual");
218 MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan");
219 MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual");
220 //regexpr
221 MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex");
222 MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex");
223 MethodCaller regexPattern = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "regexPattern");
224 // spread expressions
225 MethodCaller spreadMap = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "spreadMap");
226 MethodCaller despreadList = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "despreadList");
227 // Closure
228 MethodCaller getMethodPointer = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getMethodPointer");
229 MethodCaller invokeClosureMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeClosure");
230 //negation
231 MethodCaller negation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "negate");
232 MethodCaller bitNegation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "bitNegate");
233
234 // type converions
235 MethodCaller asTypeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asType");
236 MethodCaller castToTypeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "castToType");
237 MethodCaller createListMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createList");
238 MethodCaller createTupleMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createTuple");
239 MethodCaller createMapMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createMap");
240 MethodCaller createRangeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createRange");
241
242 // wrapper creation methods
243 MethodCaller createPojoWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createPojoWrapper");
244 MethodCaller createGroovyObjectWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createGroovyObjectWrapper");
245
246 // constructor calls with this() and super()
247 MethodCaller selectConstructorAndTransformArguments = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "selectConstructorAndTransformArguments");
248
249 // exception blocks list
250 private List exceptionBlocks = new ArrayList();
251
252 private Set syntheticStaticFields = new HashSet();
253 private boolean passingClosureParams;
254
255 private ConstructorNode constructorNode;
256 private MethodNode methodNode;
257 private BytecodeHelper helper = new BytecodeHelper(null);
258
259 public static final boolean CREATE_DEBUG_INFO = true;
260 public static final boolean CREATE_LINE_NUMBER_INFO = true;
261 private static final boolean MARK_START = true;
262
263 public static final boolean ASM_DEBUG = false; // add marker in the bytecode to show source-byecode relationship
264 private int lineNumber = -1;
265 private int columnNumber = -1;
266 private ASTNode currentASTNode = null;
267
268 private DummyClassGenerator dummyGen = null;
269 private ClassWriter dummyClassWriter = null;
270
271 private ClassNode interfaceClassLoadingClass;
272
273 private boolean implicitThis = false;
274
275 public AsmClassGenerator(
276 GeneratorContext context, ClassVisitor classVisitor,
277 ClassLoader classLoader, String sourceFile
278 ) {
279 super(classLoader);
280 this.context = context;
281 this.cw = classVisitor;
282 this.sourceFile = sourceFile;
283
284 this.dummyClassWriter = new ClassWriter(true);
285 dummyGen = new DummyClassGenerator(context, dummyClassWriter, classLoader, sourceFile);
286 compileStack = new CompileStack();
287
288 }
289
290 protected SourceUnit getSourceUnit() {
291 return null;
292 }
293
294 // GroovyClassVisitor interface
295 //-------------------------------------------------------------------------
296 public void visitClass(ClassNode classNode) {
297 // todo to be tested
298 // createDummyClass(classNode);
299
300 try {
301 syntheticStaticFields.clear();
302 this.classNode = classNode;
303 this.outermostClass = null;
304 this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
305
306 this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
307
308 cw.visit(
309 asmJDKVersion,
310 classNode.getModifiers(),
311 internalClassName,
312 null,
313 internalBaseClassName,
314 BytecodeHelper.getClassInternalNames(classNode.getInterfaces())
315 );
316 cw.visitSource(sourceFile,null);
317
318 if (classNode.isInterface()) {
319 ClassNode owner = classNode;
320 if (owner instanceof InnerClassNode) {
321 owner = owner.getOuterClass();
322 }
323 String outerClassName = owner.getName();
324 String name = outerClassName + "$" + context.getNextInnerClassIdx();
325 interfaceClassLoadingClass = new InnerClassNode(owner, name, 4128, ClassHelper.OBJECT_TYPE);
326
327 super.visitClass(classNode);
328 createInterfaceSyntheticStaticFields();
329 } else {
330 super.visitClass(classNode);
331 createMopMethods();
332 createSyntheticStaticFields();
333 }
334
335 for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
336 ClassNode innerClass = (ClassNode) iter.next();
337 String innerClassName = innerClass.getName();
338 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
339 {
340 int index = innerClassName.lastIndexOf('$');
341 if (index>=0) innerClassName = innerClassName.substring(index+1);
342 }
343 String outerClassName = internalClassName; // default for inner classes
344 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
345 if (enclosingMethod != null) {
346 // local inner classes do not specify the outer class name
347 outerClassName = null;
348 innerClassName = null;
349 }
350 cw.visitInnerClass(
351 innerClassInternalName,
352 outerClassName,
353 innerClassName,
354 innerClass.getModifiers());
355 }
356 //TODO: an inner class should have an entry of itself
357 cw.visitEnd();
358 }
359 catch (GroovyRuntimeException e) {
360 e.setModule(classNode.getModule());
361 throw e;
362 }
363 }
364
365 private void createMopMethods() {
366 visitMopMethodList(classNode.getMethods(), true);
367 visitMopMethodList(classNode.getSuperClass().getAllDeclaredMethods(), false);
368 }
369
370 private String[] buildExceptions(ClassNode[] exceptions) {
371 if (exceptions==null) return null;
372 String[] ret = new String[exceptions.length];
373 for (int i = 0; i < exceptions.length; i++) {
374 ret[i] = BytecodeHelper.getClassInternalName(exceptions[i]);
375 }
376 return ret;
377 }
378
379 /**
380 * filters a list of method for MOP methods. For all methods that are no
381 * MOP methods a MOP method is created if the method is not public and the
382 * call would be a call on "this" (isThis == true). If the call is not on
383 * "this", then the call is a call on "super" and all methods are used,
384 * unless they are already a MOP method
385 *
386 * @see #generateMopCalls(LinkedList, boolean)
387 *
388 * @param methods unfiltered list of methods for MOP
389 * @param isThis if true, then we are creating a MOP method on "this", "super" else
390 */
391 private void visitMopMethodList(List methods, boolean isThis){
392 LinkedList mopCalls = new LinkedList();
393 for (Iterator iter = methods.iterator(); iter.hasNext();) {
394 MethodNode mn = (MethodNode) iter.next();
395 if ((mn.getModifiers() & ACC_ABSTRACT) !=0 ) continue;
396 // no this$ methods for protected/public isThis=true
397 // super$ method for protected/public isThis=false
398 // --> results in XOR
399 if (isThis ^ (mn.getModifiers() & (ACC_PUBLIC|ACC_PROTECTED)) == 0) continue;
400 String methodName = mn.getName();
401 if (isMopMethod(methodName) || methodName.startsWith("<")) continue;
402 String name = getMopMethodName(mn,isThis);
403 if (containsMethod(methods,name,mn.getParameters())) continue;
404 mopCalls.add(mn);
405 }
406 generateMopCalls(mopCalls, isThis);
407 mopCalls.clear();
408 }
409
410 private boolean containsMethod(List methods, String name, Parameter[] paras) {
411 for (Iterator iter = methods.iterator(); iter.hasNext();) {
412 MethodNode element = (MethodNode) iter.next();
413 if (element.getName().equals(name) && equalParameterTypes(paras,element.getParameters())) return true;
414 }
415 return false;
416 }
417
418 private boolean equalParameterTypes(Parameter[] p1, Parameter[] p2) {
419 if (p1.length!=p2.length) return false;
420 for (int i=0; i<p1.length; i++) {
421 if (!p1[i].getType().equals(p2[i].getType())) return false;
422 }
423 return true;
424 }
425
426 /**
427 * generates a Meta Object Protocoll method, that is used to call a non public
428 * method, or to make a call to super.
429 * @param mopCalls list of methods a mop call method should be generated for
430 * @param useThis true if "this" should be used for the naming
431 */
432 private void generateMopCalls(LinkedList mopCalls, boolean useThis) {
433 for (Iterator iter = mopCalls.iterator(); iter.hasNext();) {
434 MethodNode method = (MethodNode) iter.next();
435 String name = getMopMethodName(method,useThis);
436 Parameter[] parameters = method.getParameters();
437 String methodDescriptor = BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameters());
438 cv = cw.visitMethod(Opcodes.ACC_PUBLIC & Opcodes.ACC_SYNTHETIC, name, methodDescriptor, null, null);
439 cv.visitVarInsn(ALOAD,0);
440 BytecodeHelper helper = new BytecodeHelper(cv);
441 int newRegister = 1;
442 for (int i=0; i<parameters.length; i++) {
443 ClassNode type = parameters[i].getType();
444 helper.load(parameters[i].getType(),newRegister);
445 // increment to next register, double/long are using two places
446 newRegister++;
447 if (type == ClassHelper.double_TYPE || type == ClassHelper.long_TYPE) newRegister++;
448 }
449 cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(method.getDeclaringClass()), method.getName(), methodDescriptor);
450 helper.doReturn(method.getReturnType());
451 cv.visitMaxs(0, 0);
452 cv.visitEnd();
453 classNode.addMethod(name,Opcodes.ACC_PUBLIC & Opcodes.ACC_SYNTHETIC,method.getReturnType(),parameters,null,null);
454 }
455 }
456
457 /**
458 * creates a MOP method name from a method
459 * @param method the method to be called by the mop method
460 * @param useThis if true, then it is a call on "this", "super" else
461 * @return the mop method name
462 */
463 public static String getMopMethodName(MethodNode method, boolean useThis) {
464 ClassNode declaringNode = method.getDeclaringClass();
465 int distance = 0;
466 for (;declaringNode!=null; declaringNode=declaringNode.getSuperClass()) {
467 distance++;
468 }
469 return (useThis?"this":"super")+"$"+distance+"$"+method.getName();
470 }
471
472 /**
473 * method to determine if a method is a MOP method. This is done by the
474 * method name. If the name starts with "this$" or "super$", then it is
475 * a MOP method
476 * @param methodName name of the method to test
477 * @return true if the method is a MOP method
478 */
479 public static boolean isMopMethod(String methodName) {
480 return methodName.startsWith("this$") ||
481 methodName.startsWith("super$");
482 }
483
484 protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
485 String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
486
487 cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, buildExceptions(node.getExceptions()));
488 helper = new BytecodeHelper(cv);
489 if (!node.isAbstract()) {
490 Statement code = node.getCode();
491 if (isConstructor && (code == null || !firstStatementIsSpecialConstructorCall(node))) {
492 // invokes the super class constructor
493 cv.visitVarInsn(ALOAD, 0);
494 cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", "()V");
495 }
496
497 compileStack.init(node.getVariableScope(),node.getParameters(),cv, classNode);
498
499 // ensure we save the current (meta) class in a register
500 (new ClassExpression(classNode)).visit(this);
501 cv.visitInsn(POP);
502 (new ClassExpression(ClassHelper.METACLASS_TYPE)).visit(this);
503 cv.visitInsn(POP);
504
505 // handle body
506 super.visitConstructorOrMethod(node, isConstructor);
507 if (!outputReturn || node.isVoidMethod()) {
508 cv.visitInsn(RETURN);
509 }
510 compileStack.clear();
511
512 // lets do all the exception blocks
513 for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) {
514 Runnable runnable = (Runnable) iter.next();
515 runnable.run();
516 }
517 exceptionBlocks.clear();
518
519 cv.visitMaxs(0, 0);
520 }
521 }
522
523 private boolean firstStatementIsSpecialConstructorCall(MethodNode node) {
524 Statement code = node.getFirstStatement();
525 if (code == null || !(code instanceof ExpressionStatement)) return false;
526
527 Expression expression = ((ExpressionStatement)code).getExpression();
528 if (!(expression instanceof ConstructorCallExpression)) return false;
529 ConstructorCallExpression cce = (ConstructorCallExpression) expression;
530 return cce.isSpecialCall();
531 }
532
533 public void visitConstructor(ConstructorNode node) {
534 this.constructorNode = node;
535 this.methodNode = null;
536 outputReturn = false;
537 super.visitConstructor(node);
538 }
539
540 public void visitMethod(MethodNode node) {
541 this.constructorNode = null;
542 this.methodNode = node;
543 outputReturn = false;
544
545 super.visitMethod(node);
546 }
547
548 public void visitField(FieldNode fieldNode) {
549 onLineNumber(fieldNode, "visitField: " + fieldNode.getName());
550 ClassNode t = fieldNode.getType();
551 cw.visitField(
552 fieldNode.getModifiers(),
553 fieldNode.getName(),
554 BytecodeHelper.getTypeDescription(t),
555 null, //fieldValue, //br all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
556 null);
557 visitAnnotations(fieldNode);
558 }
559
560 public void visitProperty(PropertyNode statement) {
561 // the verifyer created the field and the setter/getter methods, so here is
562 // not really something to do
563 onLineNumber(statement, "visitProperty:" + statement.getField().getName());
564 this.methodNode = null;
565 }
566
567 // GroovyCodeVisitor interface
568 //-------------------------------------------------------------------------
569
570 // Statements
571 //-------------------------------------------------------------------------
572
573 protected void visitStatement(Statement statement) {
574 String name = statement.getStatementLabel();
575 if (name!=null) {
576 Label label = compileStack.createLocalLabel(name);
577 cv.visitLabel(label);
578 }
579 }
580
581 public void visitBlockStatement(BlockStatement block) {
582 onLineNumber(block, "visitBlockStatement");
583 visitStatement(block);
584
585 compileStack.pushVariableScope(block.getVariableScope());
586 super.visitBlockStatement(block);
587 compileStack.pop();
588 }
589
590 public void visitForLoop(ForStatement loop) {
591 onLineNumber(loop, "visitForLoop");
592 visitStatement(loop);
593
594 compileStack.pushLoop(loop.getVariableScope(),loop.getStatementLabel());
595
596 //
597 // Declare the loop counter.
598 Variable variable = compileStack.defineVariable(loop.getVariable(),false);
599
600 //
601 // Then get the iterator and generate the loop control
602 MethodCallExpression iterator = new MethodCallExpression(loop.getCollectionExpression(),"iterator",new ArgumentListExpression());
603 iterator.visit(this);
604
605 final int iteratorIdx = compileStack.defineTemporaryVariable("iterator", ClassHelper.make(java.util.Iterator.class),true);
606
607 Label continueLabel = compileStack.getContinueLabel();
608 Label breakLabel = compileStack.getBreakLabel();
609
610 cv.visitLabel(continueLabel);
611 cv.visitVarInsn(ALOAD, iteratorIdx);
612 iteratorHasNextMethod.call(cv);
613 // note: ifeq tests for ==0, a boolean is 0 if it is false
614 cv.visitJumpInsn(IFEQ, breakLabel);
615
616 cv.visitVarInsn(ALOAD, iteratorIdx);
617 iteratorNextMethod.call(cv);
618 helper.storeVar(variable);
619
620 // Generate the loop body
621 loop.getLoopBlock().visit(this);
622
623 cv.visitJumpInsn(GOTO, continueLabel);
624 cv.visitLabel(breakLabel);
625
626 compileStack.pop();
627 }
628
629 public void visitWhileLoop(WhileStatement loop) {
630 onLineNumber(loop, "visitWhileLoop");
631 visitStatement(loop);
632
633 compileStack.pushLoop(loop.getStatementLabel());
634 Label continueLabel = compileStack.getContinueLabel();
635 Label breakLabel = compileStack.getBreakLabel();
636
637 cv.visitLabel(continueLabel);
638 loop.getBooleanExpression().visit(this);
639 cv.visitJumpInsn(IFEQ, breakLabel);
640
641 loop.getLoopBlock().visit(this);
642
643 cv.visitJumpInsn(GOTO, continueLabel);
644 cv.visitLabel(breakLabel);
645
646 compileStack.pop();
647 }
648
649 public void visitDoWhileLoop(DoWhileStatement loop) {
650 onLineNumber(loop, "visitDoWhileLoop");
651 visitStatement(loop);
652
653 compileStack.pushLoop(loop.getStatementLabel());
654 Label breakLabel = compileStack.getBreakLabel();
655 Label continueLabel = compileStack.getContinueLabel();
656 cv.visitLabel(continueLabel);
657
658 loop.getLoopBlock().visit(this);
659
660 loop.getBooleanExpression().visit(this);
661 cv.visitJumpInsn(IFEQ, continueLabel);
662 cv.visitLabel(breakLabel);
663
664 compileStack.pop();
665 }
666
667 public void visitIfElse(IfStatement ifElse) {
668 onLineNumber(ifElse, "visitIfElse");
669 visitStatement(ifElse);
670 ifElse.getBooleanExpression().visit(this);
671
672 Label l0 = new Label();
673 cv.visitJumpInsn(IFEQ, l0);
674
675 ifElse.getIfBlock().visit(this);
676
677 Label l1 = new Label();
678 cv.visitJumpInsn(GOTO, l1);
679 cv.visitLabel(l0);
680
681 ifElse.getElseBlock().visit(this);
682 cv.visitLabel(l1);
683 }
684
685 public void visitTernaryExpression(TernaryExpression expression) {
686 onLineNumber(expression, "visitTernaryExpression");
687
688 expression.getBooleanExpression().visit(this);
689
690 Label l0 = new Label();
691 cv.visitJumpInsn(IFEQ, l0);
692 visitAndAutoboxBoolean(expression.getTrueExpression());
693
694 Label l1 = new Label();
695 cv.visitJumpInsn(GOTO, l1);
696 cv.visitLabel(l0);
697
698 visitAndAutoboxBoolean(expression.getFalseExpression());
699 cv.visitLabel(l1);
700 }
701
702 public void visitAssertStatement(AssertStatement statement) {
703 onLineNumber(statement, "visitAssertStatement");
704 visitStatement(statement);
705
706 BooleanExpression booleanExpression = statement.getBooleanExpression();
707 booleanExpression.visit(this);
708
709 Label l0 = new Label();
710 cv.visitJumpInsn(IFEQ, l0);
711
712 // do nothing
713
714 Label l1 = new Label();
715 cv.visitJumpInsn(GOTO, l1);
716 cv.visitLabel(l0);
717
718 // push expression string onto stack
719 String expressionText = booleanExpression.getText();
720 List list = new ArrayList();
721 addVariableNames(booleanExpression, list);
722 if (list.isEmpty()) {
723 cv.visitLdcInsn(expressionText);
724 }
725 else {
726 boolean first = true;
727
728 // lets create a new expression
729 cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
730 cv.visitInsn(DUP);
731 cv.visitLdcInsn(expressionText + ". Values: ");
732
733 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V");
734
735 int tempIndex = compileStack.defineTemporaryVariable("assert",true);
736
737 for (Iterator iter = list.iterator(); iter.hasNext();) {
738 String name = (String) iter.next();
739 String text = name + " = ";
740 if (first) {
741 first = false;
742 }
743 else {
744 text = ", " + text;
745 }
746
747 cv.visitVarInsn(ALOAD, tempIndex);
748 cv.visitLdcInsn(text);
749 cv.visitMethodInsn(
750 INVOKEVIRTUAL,
751 "java/lang/StringBuffer",
752 "append",
753 "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
754 cv.visitInsn(POP);
755
756 cv.visitVarInsn(ALOAD, tempIndex);
757 new VariableExpression(name).visit(this);
758 cv.visitMethodInsn(
759 INVOKEVIRTUAL,
760 "java/lang/StringBuffer",
761 "append",
762 "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
763 cv.visitInsn(POP);
764
765 }
766 cv.visitVarInsn(ALOAD, tempIndex);
767 compileStack.removeVar(tempIndex);
768 }
769 // now the optional exception expression
770 statement.getMessageExpression().visit(this);
771
772 assertFailedMethod.call(cv);
773 cv.visitLabel(l1);
774 }
775
776 private void addVariableNames(Expression expression, List list) {
777 if (expression instanceof BooleanExpression) {
778 BooleanExpression boolExp = (BooleanExpression) expression;
779 addVariableNames(boolExp.getExpression(), list);
780 }
781 else if (expression instanceof BinaryExpression) {
782 BinaryExpression binExp = (BinaryExpression) expression;
783 addVariableNames(binExp.getLeftExpression(), list);
784 addVariableNames(binExp.getRightExpression(), list);
785 }
786 else if (expression instanceof VariableExpression) {
787 VariableExpression varExp = (VariableExpression) expression;
788 list.add(varExp.getName());
789 }
790 }
791
792 public void visitTryCatchFinally(TryCatchStatement statement) {
793 onLineNumber(statement, "visitTryCatchFinally");
794 visitStatement(statement);
795
796 CatchStatement catchStatement = statement.getCatchStatement(0);
797 Statement tryStatement = statement.getTryStatement();
798 final Statement finallyStatement = statement.getFinallyStatement();
799
800 int anyExceptionIndex = compileStack.defineTemporaryVariable("exception",false);
801 if (!finallyStatement.isEmpty()) {
802 compileStack.pushFinallyBlock(
803 new Runnable(){
804 public void run(){finallyStatement.visit(AsmClassGenerator.this);}
805 }
806 );
807 }
808
809 // start try block, label needed for exception table
810 final Label tryStart = new Label();
811 cv.visitLabel(tryStart);
812 tryStatement.visit(this);
813 // goto finally part
814 final Label finallyStart = new Label();
815 cv.visitJumpInsn(GOTO, finallyStart);
816 // marker needed for Exception table
817 final Label tryEnd = new Label();
818 cv.visitLabel(tryEnd);
819
820 for (Iterator it=statement.getCatchStatements().iterator(); it.hasNext();) {
821 catchStatement = (CatchStatement) it.next();
822 ClassNode exceptionType = catchStatement.getExceptionType();
823 // start catch block, label needed for exception table
824 final Label catchStart = new Label();
825 cv.visitLabel(catchStart);
826 // create exception variable and store the exception
827 compileStack.defineVariable(catchStatement.getVariable(),true);
828 // handle catch body
829 catchStatement.visit(this);
830 // goto finally start
831 cv.visitJumpInsn(GOTO, finallyStart);
832 // add exception to table
833 final String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType);
834 exceptionBlocks.add(new Runnable() {
835 public void run() {
836 cv.visitTryCatchBlock(tryStart, tryEnd, catchStart, exceptionTypeInternalName);
837 }
838 });
839 }
840
841 // marker needed for the exception table
842 final Label endOfAllCatches = new Label();
843 cv.visitLabel(endOfAllCatches);
844
845 // remove the finally, don't let it visit itself
846 if (!finallyStatement.isEmpty()) compileStack.popFinallyBlock();
847
848 // start finally
849 cv.visitLabel(finallyStart);
850 finallyStatement.visit(this);
851 // goto end of finally
852 Label afterFinally = new Label();
853 cv.visitJumpInsn(GOTO, afterFinally);
854
855 // start a block catching any Exception
856 final Label catchAny = new Label();
857 cv.visitLabel(catchAny);
858 //store exception
859 cv.visitVarInsn(ASTORE, anyExceptionIndex);
860 finallyStatement.visit(this);
861 // load the exception and rethrow it
862 cv.visitVarInsn(ALOAD, anyExceptionIndex);
863 cv.visitInsn(ATHROW);
864
865 // end of all catches and finally parts
866 cv.visitLabel(afterFinally);
867
868 // add catch any block to exception table
869 exceptionBlocks.add(new Runnable() {
870 public void run() {
871 cv.visitTryCatchBlock(tryStart, endOfAllCatches, catchAny, null);
872 }
873 });
874 }
875
876 public void visitSwitch(SwitchStatement statement) {
877 onLineNumber(statement, "visitSwitch");
878 visitStatement(statement);
879
880 statement.getExpression().visit(this);
881
882 // switch does not have a continue label. use its parent's for continue
883 Label breakLabel = compileStack.pushSwitch();
884
885 int switchVariableIndex = compileStack.defineTemporaryVariable("switch",true);
886
887 List caseStatements = statement.getCaseStatements();
888 int caseCount = caseStatements.size();
889 Label[] labels = new Label[caseCount + 1];
890 for (int i = 0; i < caseCount; i++) {
891 labels[i] = new Label();
892 }
893
894 int i = 0;
895 for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
896 CaseStatement caseStatement = (CaseStatement) iter.next();
897 visitCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]);
898 }
899
900 statement.getDefaultStatement().visit(this);
901
902 cv.visitLabel(breakLabel);
903
904 compileStack.pop();
905 }
906
907 public void visitCaseStatement(CaseStatement statement) {
908 }
909
910 public void visitCaseStatement(
911 CaseStatement statement,
912 int switchVariableIndex,
913 Label thisLabel,
914 Label nextLabel) {
915
916 onLineNumber(statement, "visitCaseStatement");
917
918 cv.visitVarInsn(ALOAD, switchVariableIndex);
919 statement.getExpression().visit(this);
920
921 isCaseMethod.call(cv);
922
923 Label l0 = new Label();
924 cv.visitJumpInsn(IFEQ, l0);
925
926 cv.visitLabel(thisLabel);
927
928 statement.getCode().visit(this);
929
930 // now if we don't finish with a break we need to jump past
931 // the next comparison
932 if (nextLabel != null) {
933 cv.visitJumpInsn(GOTO, nextLabel);
934 }
935
936 cv.visitLabel(l0);
937 }
938
939 public void visitBreakStatement(BreakStatement statement) {
940 onLineNumber(statement, "visitBreakStatement");
941 visitStatement(statement);
942
943 String name = statement.getLabel();
944 Label breakLabel = compileStack.getNamedBreakLabel(name);
945 compileStack.applyFinallyBlocks(breakLabel, true);
946
947 cv.visitJumpInsn(GOTO, breakLabel);
948 }
949
950 public void visitContinueStatement(ContinueStatement statement) {
951 onLineNumber(statement, "visitContinueStatement");
952 visitStatement(statement);
953
954 String name = statement.getLabel();
955 Label continueLabel = compileStack.getContinueLabel();
956 if (name!=null) continueLabel = compileStack.getNamedContinueLabel(name);
957 compileStack.applyFinallyBlocks(continueLabel, false);
958 cv.visitJumpInsn(GOTO, continueLabel);
959 }
960
961 public void visitSynchronizedStatement(SynchronizedStatement statement) {
962 onLineNumber(statement, "visitSynchronizedStatement");
963 visitStatement(statement);
964
965 statement.getExpression().visit(this);
966 final int index = compileStack.defineTemporaryVariable("synchronized", ClassHelper.Integer_TYPE,true);
967
968 final Label synchronizedStart = new Label();
969 final Label synchronizedEnd = new Label();
970 final Label catchAll = new Label();
971
972 cv.visitVarInsn(ALOAD, index);
973 cv.visitInsn(MONITORENTER);
974 cv.visitLabel(synchronizedStart);
975
976 Runnable finallyPart = new Runnable(){
977 public void run(){
978 cv.visitVarInsn(ALOAD, index);
979 cv.visitInsn(MONITOREXIT);
980 }
981 };
982 compileStack.pushFinallyBlock(finallyPart);
983 statement.getCode().visit(this);
984
985 finallyPart.run();
986 cv.visitJumpInsn(GOTO, synchronizedEnd);
987 cv.visitLabel(catchAll);
988 finallyPart.run();
989 cv.visitInsn(ATHROW);
990 cv.visitLabel(synchronizedEnd);
991
992 compileStack.popFinallyBlock();
993 exceptionBlocks.add(new Runnable() {
994 public void run() {
995 cv.visitTryCatchBlock(synchronizedStart, catchAll, catchAll, null);
996 }
997 });
998 }
999
1000 public void visitThrowStatement(ThrowStatement statement) {
1001 onLineNumber(statement, "visitThrowStatement");
1002 visitStatement(statement);
1003
1004 statement.getExpression().visit(this);
1005
1006 // we should infer the type of the exception from the expression
1007 cv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
1008
1009 cv.visitInsn(ATHROW);
1010 }
1011
1012 public void visitReturnStatement(ReturnStatement statement) {
1013 onLineNumber(statement, "visitReturnStatement");
1014 visitStatement(statement);
1015
1016 ClassNode returnType = methodNode.getReturnType();
1017 if (returnType==ClassHelper.VOID_TYPE) {
1018 if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) {
1019 throwException("Cannot use return statement with an expression on a method that returns void");
1020 }
1021 compileStack.applyFinallyBlocks();
1022 cv.visitInsn(RETURN);
1023 outputReturn = true;
1024 return;
1025 }
1026
1027 Expression expression = statement.getExpression();
1028 evaluateExpression(expression);
1029 if (returnType==ClassHelper.OBJECT_TYPE && expression.getType() != null && expression.getType()==ClassHelper.VOID_TYPE) {
1030 cv.visitInsn(ACONST_NULL); // cheat the caller
1031 } else {
1032 // return is based on class type
1033 // we may need to cast
1034 doConvertAndCast(returnType, expression, false, true, false);
1035 helper.unbox(returnType);
1036 }
1037 if (compileStack.hasFinallyBlocks()) {
1038 int returnValueIdx = compileStack.defineTemporaryVariable("returnValue",returnType,true);
1039 compileStack.applyFinallyBlocks();
1040 helper.load(returnType,returnValueIdx);
1041 }
1042 helper.doReturn(returnType);
1043 outputReturn = true;
1044 }
1045
1046 /**
1047 * Casts to the given type unless it can be determined that the cast is unnecessary
1048 */
1049 protected void doConvertAndCast(ClassNode type, Expression expression, boolean ignoreAutoboxing, boolean forceCast, boolean coerce) {
1050 ClassNode expType = getExpressionType(expression);
1051 // temp resolution: convert all primitive casting to corresponsing Object type
1052 if (!ignoreAutoboxing && ClassHelper.isPrimitiveType(type)) {
1053 type = ClassHelper.getWrapper(type);
1054 }
1055 if (forceCast || (type!=null && !type.equals(expType))) {
1056 doConvertAndCast(type,coerce);
1057 }
1058 }
1059
1060 /**
1061 * @param expression
1062 */
1063 protected void evaluateExpression(Expression expression) {
1064 visitAndAutoboxBoolean(expression);
1065
1066 Expression assignExpr = createReturnLHSExpression(expression);
1067 if (assignExpr != null) {
1068 leftHandExpression = false;
1069 assignExpr.visit(this);
1070 }
1071 }
1072
1073 public void visitExpressionStatement(ExpressionStatement statement) {
1074 onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName());
1075 visitStatement(statement);
1076
1077 Expression expression = statement.getExpression();
1078
1079 visitAndAutoboxBoolean(expression);
1080
1081 if (isPopRequired(expression)) {
1082 cv.visitInsn(POP);
1083 }
1084 }
1085
1086 // Expressions
1087 //-------------------------------------------------------------------------
1088
1089 public void visitDeclarationExpression(DeclarationExpression expression) {
1090 onLineNumber(expression, "visitDeclarationExpression: \""+expression.getVariableExpression().getName()+"\"");
1091
1092 Expression rightExpression = expression.getRightExpression();
1093 // no need to visit left side, just get the variable name
1094 VariableExpression vex = expression.getVariableExpression();
1095 ClassNode type = vex.getType();
1096
1097 // lets not cast for primitive types as we handle these in field setting etc
1098 if (ClassHelper.isPrimitiveType(type)) {
1099 rightExpression.visit(this);
1100 } else {
1101 if (type!=ClassHelper.OBJECT_TYPE){
1102 visitCastExpression(new CastExpression(type, rightExpression));
1103 } else {
1104 visitAndAutoboxBoolean(rightExpression);
1105 }
1106 }
1107 compileStack.defineVariable(vex,true);
1108 }
1109
1110 public void visitBinaryExpression(BinaryExpression expression) {
1111 onLineNumber(expression, "visitBinaryExpression: \"" + expression.getOperation().getText() + "\" ");
1112 switch (expression.getOperation().getType()) {
1113 case Types.EQUAL : // = assignment
1114 evaluateEqual(expression);
1115 break;
1116
1117 case Types.COMPARE_IDENTICAL : // ===
1118 evaluateBinaryExpression(compareIdenticalMethod, expression);
1119 break;
1120
1121 case Types.COMPARE_EQUAL : // ==
1122 evaluateBinaryExpression(compareEqualMethod, expression);
1123 break;
1124
1125 case Types.COMPARE_NOT_EQUAL :
1126 evaluateBinaryExpression(compareNotEqualMethod, expression);
1127 break;
1128
1129 case Types.COMPARE_TO :
1130 evaluateCompareTo(expression);
1131 break;
1132
1133 case Types.COMPARE_GREATER_THAN :
1134 evaluateBinaryExpression(compareGreaterThanMethod, expression);
1135 break;
1136
1137 case Types.COMPARE_GREATER_THAN_EQUAL :
1138 evaluateBinaryExpression(compareGreaterThanEqualMethod, expression);
1139 break;
1140
1141 case Types.COMPARE_LESS_THAN :
1142 evaluateBinaryExpression(compareLessThanMethod, expression);
1143 break;
1144
1145 case Types.COMPARE_LESS_THAN_EQUAL :
1146 evaluateBinaryExpression(compareLessThanEqualMethod, expression);
1147 break;
1148
1149 case Types.LOGICAL_AND :
1150 evaluateLogicalAndExpression(expression);
1151 break;
1152
1153 case Types.LOGICAL_OR :
1154 evaluateLogicalOrExpression(expression);
1155 break;
1156
1157 case Types.BITWISE_AND :
1158 evaluateBinaryExpression("and", expression);
1159 break;
1160
1161 case Types.BITWISE_AND_EQUAL :
1162 evaluateBinaryExpressionWithAsignment("and", expression);
1163 break;
1164
1165 case Types.BITWISE_OR :
1166 evaluateBinaryExpression("or", expression);
1167 break;
1168
1169 case Types.BITWISE_OR_EQUAL :
1170 evaluateBinaryExpressionWithAsignment("or", expression);
1171 break;
1172
1173 case Types.BITWISE_XOR :
1174 evaluateBinaryExpression("xor", expression);
1175 break;
1176
1177 case Types.BITWISE_XOR_EQUAL :
1178 evaluateBinaryExpressionWithAsignment("xor", expression);
1179 break;
1180
1181 case Types.PLUS :
1182 evaluateBinaryExpression("plus", expression);
1183 break;
1184
1185 case Types.PLUS_EQUAL :
1186 evaluateBinaryExpressionWithAsignment("plus", expression);
1187 break;
1188
1189 case Types.MINUS :
1190 evaluateBinaryExpression("minus", expression);
1191 break;
1192
1193 case Types.MINUS_EQUAL :
1194 evaluateBinaryExpressionWithAsignment("minus", expression);
1195 break;
1196
1197 case Types.MULTIPLY :
1198 evaluateBinaryExpression("multiply", expression);
1199 break;
1200
1201 case Types.MULTIPLY_EQUAL :
1202 evaluateBinaryExpressionWithAsignment("multiply", expression);
1203 break;
1204
1205 case Types.DIVIDE :
1206 evaluateBinaryExpression("div", expression);
1207 break;
1208
1209 case Types.DIVIDE_EQUAL :
1210 //SPG don't use divide since BigInteger implements directly
1211 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
1212 evaluateBinaryExpressionWithAsignment("div", expression);
1213 break;
1214
1215 case Types.INTDIV :
1216 evaluateBinaryExpression("intdiv", expression);
1217 break;
1218
1219 case Types.INTDIV_EQUAL :
1220 evaluateBinaryExpressionWithAsignment("intdiv", expression);
1221 break;
1222
1223 case Types.MOD :
1224 evaluateBinaryExpression("mod", expression);
1225 break;
1226
1227 case Types.MOD_EQUAL :
1228 evaluateBinaryExpressionWithAsignment("mod", expression);
1229 break;
1230
1231 case Types.POWER :
1232 evaluateBinaryExpression("power", expression);
1233 break;
1234
1235 case Types.POWER_EQUAL :
1236 evaluateBinaryExpressionWithAsignment("power", expression);
1237 break;
1238
1239 case Types.LEFT_SHIFT :
1240 evaluateBinaryExpression("leftShift", expression);
1241 break;
1242
1243 case Types.LEFT_SHIFT_EQUAL :
1244 evaluateBinaryExpressionWithAsignment("leftShift", expression);
1245 break;
1246
1247 case Types.RIGHT_SHIFT :
1248 evaluateBinaryExpression("rightShift", expression);
1249 break;
1250
1251 case Types.RIGHT_SHIFT_EQUAL :
1252 evaluateBinaryExpressionWithAsignment("rightShift", expression);
1253 break;
1254
1255 case Types.RIGHT_SHIFT_UNSIGNED :
1256 evaluateBinaryExpression("rightShiftUnsigned", expression);
1257 break;
1258
1259 case Types.RIGHT_SHIFT_UNSIGNED_EQUAL :
1260 evaluateBinaryExpressionWithAsignment("rightShiftUnsigned", expression);
1261 break;
1262
1263 case Types.KEYWORD_INSTANCEOF :
1264 evaluateInstanceof(expression);
1265 break;
1266
1267 case Types.FIND_REGEX :
1268 evaluateBinaryExpression(findRegexMethod, expression);
1269 break;
1270
1271 case Types.MATCH_REGEX :
1272 evaluateBinaryExpression(matchRegexMethod, expression);
1273 break;
1274
1275 case Types.LEFT_SQUARE_BRACKET :
1276 if (leftHandExpression) {
1277 throwException("Should not be called here. Possible reason: postfix operation on array.");
1278 // This is handled right now in the evaluateEqual()
1279 // should support this here later
1280 //evaluateBinaryExpression("putAt", expression);
1281 } else {
1282 evaluateBinaryExpression("getAt", expression);
1283 }
1284 break;
1285
1286 case Types.KEYWORD_IN :
1287 evaluateBinaryExpression(isCaseMethod, expression);
1288 break;
1289
1290 default :
1291 throwException("Operation: " + expression.getOperation() + " not supported");
1292 }
1293 }
1294
1295 private void load(Expression exp) {
1296
1297 boolean wasLeft = leftHandExpression;
1298 leftHandExpression = false;
1299 // if (CREATE_DEBUG_INFO)
1300 // helper.mark("-- loading expression: " + exp.getClass().getName() +
1301 // " at [" + exp.getLineNumber() + ":" + exp.getColumnNumber() + "]");
1302 //exp.visit(this);
1303 visitAndAutoboxBoolean(exp);
1304 // if (CREATE_DEBUG_INFO)
1305 // helper.mark(" -- end of loading --");
1306
1307 leftHandExpression = wasLeft;
1308 }
1309
1310 public void visitPostfixExpression(PostfixExpression expression) {
1311 switch (expression.getOperation().getType()) {
1312 case Types.PLUS_PLUS :
1313 evaluatePostfixMethod("next", expression.getExpression());
1314 break;
1315 case Types.MINUS_MINUS :
1316 evaluatePostfixMethod("previous", expression.getExpression());
1317 break;
1318 }
1319 }
1320
1321 private void throwException(String s) {
1322 throw new RuntimeParserException(s, currentASTNode);
1323 }
1324
1325 public void visitPrefixExpression(PrefixExpression expression) {
1326 switch (expression.getOperation().getType()) {
1327 case Types.PLUS_PLUS :
1328 evaluatePrefixMethod("next", expression.getExpression());
1329 break;
1330 case Types.MINUS_MINUS :
1331 evaluatePrefixMethod("previous", expression.getExpression());
1332 break;
1333 }
1334 }
1335
1336 public void visitClosureExpression(ClosureExpression expression) {
1337 ClassNode innerClass = createClosureClass(expression);
1338 addInnerClass(innerClass);
1339 String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass);
1340
1341 passingClosureParams = true;
1342 List constructors = innerClass.getDeclaredConstructors();
1343 ConstructorNode node = (ConstructorNode) constructors.get(0);
1344
1345 Parameter[] localVariableParams = node.getParameters();
1346
1347 cv.visitTypeInsn(NEW, innerClassinternalName);
1348 cv.visitInsn(DUP);
1349 if (isStaticMethod() || classNode.isStaticClass()) {
1350 visitClassExpression(new ClassExpression(classNode));
1351 visitClassExpression(new ClassExpression(getOutermostClass()));
1352 } else {
1353 cv.visitVarInsn(ALOAD, 0);
1354 loadThis();
1355 }
1356
1357 // now lets load the various parameters we're passing
1358 // we start at index 1 because the first variable we pass
1359 // is the owner instance and at this point it is already
1360 // on the stack
1361 for (int i = 2; i < localVariableParams.length; i++) {
1362 Parameter param = localVariableParams[i];
1363 String name = param.getName();
1364
1365 // compileStack.containsVariable(name) means to ask if the variable is already declared
1366 // compileStack.getScope().isReferencedClassVariable(name) means to ask if the variable is a field
1367 // If it is no field and is not yet declared, then it is either a closure shared variable or
1368 // an already declared variable.
1369 if (!compileStack.containsVariable(name) && compileStack.getScope().isReferencedClassVariable(name)) {
1370 visitFieldExpression(new FieldExpression(classNode.getField(name)));
1371 } else {
1372 Variable v = compileStack.getVariable(name,classNode.getSuperClass()!=ClassHelper.CLOSURE_TYPE);
1373 if (v==null) {
1374 // variable is not on stack because we are
1375 // inside a nested Closure and this variable
1376 // was not used before
1377 // then load it from the Closure field
1378 FieldNode field = classNode.getField(name);
1379 cv.visitVarInsn(ALOAD, 0);
1380 cv.visitFieldInsn(GETFIELD, internalClassName, name, BytecodeHelper.getTypeDescription(field.getType()));
1381 // and define it
1382 // Note:
1383 // we can simply define it here and don't have to
1384 // be afraid about name problems because a second
1385 // variable with that name is not allowed inside the closure
1386 param.setClosureSharedVariable(false);
1387 v = compileStack.defineVariable(param,true);
1388 param.setClosureSharedVariable(true);
1389 v.setHolder(true);
1390 }
1391 cv.visitVarInsn(ALOAD, v.getIndex());
1392 }
1393 }
1394 passingClosureParams = false;
1395
1396 // we may need to pass in some other constructors
1397 //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
1398 cv.visitMethodInsn(
1399 INVOKESPECIAL,
1400 innerClassinternalName,
1401 "<init>",
1402 BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, localVariableParams));
1403 }
1404
1405 /**
1406 * Loads either this object or if we're inside a closure then load the top level owner
1407 */
1408 protected void loadThisOrOwner() {
1409 if (isInnerClass()) {
1410 visitFieldExpression(new FieldExpression(classNode.getField("owner")));
1411 } else {
1412 loadThis();
1413 }
1414 }
1415
1416 public void visitRegexExpression(RegexExpression expression) {
1417 expression.getRegex().visit(this);
1418 regexPattern.call(cv);
1419 }
1420
1421 /**
1422 * Generate byte code for constants
1423 * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a>
1424 */
1425 public void visitConstantExpression(ConstantExpression expression) {
1426 Object value = expression.getValue();
1427 helper.loadConstant(value);
1428 }
1429
1430 public void visitSpreadExpression(SpreadExpression expression) {
1431 throw new GroovyBugError("SpreadExpression should not be visited here");
1432 }
1433
1434 public void visitSpreadMapExpression(SpreadMapExpression expression) {
1435 Expression subExpression = expression.getExpression();
1436 subExpression.visit(this);
1437 spreadMap.call(cv);
1438 }
1439
1440 public void visitMethodPointerExpression(MethodPointerExpression expression) {
1441 Expression subExpression = expression.getExpression();
1442 subExpression.visit(this);
1443 helper.loadConstant(expression.getMethodName());
1444 getMethodPointer.call(cv);
1445 }
1446
1447 public void visitNegationExpression(NegationExpression expression) {
1448 Expression subExpression = expression.getExpression();
1449 subExpression.visit(this);
1450 negation.call(cv);
1451 }
1452
1453 public void visitBitwiseNegExpression(BitwiseNegExpression expression) {
1454 Expression subExpression = expression.getExpression();
1455 subExpression.visit(this);
1456 bitNegation.call(cv);
1457 }
1458
1459 public void visitCastExpression(CastExpression expression) {
1460 ClassNode type = expression.getType();
1461 visitAndAutoboxBoolean(expression.getExpression());
1462 doConvertAndCast(type, expression.getExpression(), expression.isIgnoringAutoboxing(),false,expression.isCoerce());
1463 }
1464
1465 public void visitNotExpression(NotExpression expression) {
1466 Expression subExpression = expression.getExpression();
1467 subExpression.visit(this);
1468 // if we do !object, then the cast to boolean will
1469 // do the conversion of Object to boolean. so a simple
1470 // call to unbox is enough here.
1471 if (
1472 !isComparisonExpression(subExpression) &&
1473 !(subExpression instanceof BooleanExpression))
1474 {
1475 helper.unbox(boolean.class);
1476 }
1477 helper.negateBoolean();
1478 }
1479
1480 /**
1481 * return a primitive boolean value of the BooleanExpresion.
1482 * @param expression
1483 */
1484 public void visitBooleanExpression(BooleanExpression expression) {
1485 compileStack.pushBooleanExpression();
1486 expression.getExpression().visit(this);
1487
1488 if (!isComparisonExpression(expression.getExpression())) {
1489 // comment out for optimization when boolean values are not autoboxed for eg. function calls.
1490 // Class typeClass = expression.getExpression().getTypeClass();
1491 // if (typeClass != null && typeClass != boolean.class) {
1492 helper.unbox(boolean.class); // to return a primitive boolean
1493 // }
1494 }
1495 compileStack.pop();
1496 }
1497
1498 private void makeInvokeMethodCall(MethodCallExpression call, boolean useSuper, MethodCallerMultiAdapter adapter) {
1499 // receiver
1500 // we operate on GroovyObject if possible
1501 Expression objectExpression = call.getObjectExpression();
1502 if (!isStaticMethod() && !isStaticContext() && isThisExpression(call.getObjectExpression()))
1503 {
1504 objectExpression = new CastExpression(ClassHelper.make(GroovyObject.class),objectExpression);
1505 }
1506 // message name
1507 Expression messageName = new CastExpression(ClassHelper.STRING_TYPE,call.getMethod());
1508 if (useSuper) {
1509 makeCall(new ClassExpression(getOutermostClass().getSuperClass()),
1510 objectExpression, messageName,
1511 call.getArguments(), adapter,
1512 call.isSafe(), call.isSpreadSafe(),
1513 false
1514 );
1515 } else {
1516 makeCall(objectExpression, messageName,
1517 call.getArguments(), adapter,
1518 call.isSafe(), call.isSpreadSafe(),
1519 call.isImplicitThis()
1520 );
1521 }
1522 }
1523
1524 private void makeCall(
1525 Expression receiver, Expression message, Expression arguments,
1526 MethodCallerMultiAdapter adapter,
1527 boolean safe, boolean spreadSafe, boolean implicitThis
1528 ) {
1529 ClassNode cn = classNode;
1530 if (isInClosure() && !implicitThis) {
1531 cn = getOutermostClass();
1532 }
1533 makeCall(new ClassExpression(cn), receiver, message, arguments,
1534 adapter, safe, spreadSafe, implicitThis);
1535 }
1536
1537 private void makeCall(
1538 ClassExpression sender,
1539 Expression receiver, Expression message, Expression arguments,
1540 MethodCallerMultiAdapter adapter,
1541 boolean safe, boolean spreadSafe, boolean implicitThis
1542 ) {
1543 // ensure VariableArguments are read, not stored
1544 boolean lhs = leftHandExpression;
1545 leftHandExpression = false;
1546
1547 // sender
1548 sender.visit(this);
1549 // receiver
1550 boolean oldVal = this.implicitThis;
1551 this.implicitThis = implicitThis;
1552 receiver.visit(this);
1553 this.implicitThis = oldVal;
1554 // message
1555 if (message!=null) message.visit(this);
1556
1557 // arguments
1558 boolean containsSpreadExpression = containsSpreadExpression(arguments);
1559 int numberOfArguments = containsSpreadExpression?-1:argumentSize(arguments);
1560 if (numberOfArguments > adapter.maxArgs || containsSpreadExpression) {
1561 ArgumentListExpression ae;
1562 if (arguments instanceof ArgumentListExpression) {
1563 ae = (ArgumentListExpression) arguments;
1564 } else if (arguments instanceof TupleExpression){
1565 TupleExpression te = (TupleExpression) arguments;
1566 ae = new ArgumentListExpression(te.getExpressions());
1567 } else {
1568 ae = new ArgumentListExpression();
1569 ae.addExpression(arguments);
1570 }
1571 if (containsSpreadExpression){
1572 despreadList(ae.getExpressions(),true);
1573 } else {
1574 ae.visit(this);
1575 }
1576 } else if (numberOfArguments > 0) {
1577 TupleExpression te = (TupleExpression) arguments;
1578 for (int i = 0; i < numberOfArguments; i++) {
1579 Expression argument = te.getExpression(i);
1580 visitAndAutoboxBoolean(argument);
1581 if (argument instanceof CastExpression) loadWrapper(argument);
1582 }
1583 }
1584
1585 adapter.call(cv,numberOfArguments,safe,spreadSafe);
1586
1587 leftHandExpression = lhs;
1588 }
1589
1590 private void despreadList(List expressions, boolean wrap) {
1591
1592 ArrayList spreadIndexes = new ArrayList();
1593 ArrayList spreadExpressions = new ArrayList();
1594 ArrayList normalArguments = new ArrayList();
1595 for (int i=0; i<expressions.size(); i++) {
1596 Object expr = expressions.get(i);
1597 if ( !(expr instanceof SpreadExpression) ) {
1598 normalArguments.add(expr);
1599 } else {
1600 spreadIndexes.add(new ConstantExpression(new Integer(i-spreadExpressions.size())));
1601 spreadExpressions.add(((SpreadExpression)expr).getExpression());
1602 }
1603 }
1604
1605 //load normal arguments as array
1606 visitTupleExpression(new ArgumentListExpression(normalArguments),wrap);
1607 //load spread expressions as array
1608 (new TupleExpression(spreadExpressions)).visit(this);
1609 //load insertion index
1610 (new ArrayExpression(ClassHelper.int_TYPE,spreadIndexes,null)).visit(this);
1611 despreadList.call(cv);
1612 }
1613
1614 public void visitMethodCallExpression(MethodCallExpression call) {
1615 onLineNumber(call, "visitMethodCallExpression: \"" + call.getMethod() + "\":");
1616
1617 Expression arguments = call.getArguments();
1618 String methodName = call.getMethodAsString();
1619 boolean isSuperMethodCall = usesSuper(call);
1620 boolean isThisExpression = isThisExpression(call.getObjectExpression());
1621
1622 // are we a local variable
1623 if (methodName!=null && isThisExpression && isFieldOrVariable(methodName) && ! classNode.hasPossibleMethod(methodName, arguments)) {
1624 // lets invoke the closure method
1625 visitVariableExpression(new VariableExpression(methodName));
1626 arguments.visit(this);
1627 invokeClosureMethod.call(cv);
1628 } else {
1629 MethodCallerMultiAdapter adapter = invokeMethod;
1630 if (isThisExpression) adapter = invokeMethodOnCurrent;
1631 if (isSuperMethodCall) adapter = invokeMethodOnSuper;
1632 if (isStaticInvocation(call)) adapter = invokeStaticMethod;
1633 makeInvokeMethodCall(call,isSuperMethodCall,adapter);
1634 }
1635 }
1636
1637 private boolean isStaticInvocation(MethodCallExpression call) {
1638 if (!isThisExpression(call.getObjectExpression())) return false;
1639 if (isStaticMethod()) return true;
1640 return isStaticContext() && !call.isImplicitThis();
1641 }
1642
1643 protected boolean emptyArguments(Expression arguments) {
1644 return argumentSize(arguments) == 0;
1645 }
1646
1647 protected static boolean containsSpreadExpression(Expression arguments) {
1648 List args = null;
1649 if (arguments instanceof TupleExpression) {
1650 TupleExpression tupleExpression = (TupleExpression) arguments;
1651 args = tupleExpression.getExpressions();
1652 } else if (arguments instanceof ListExpression) {
1653 ListExpression le = (ListExpression) arguments;
1654 args = le.getExpressions();
1655 } else {
1656 return arguments instanceof SpreadExpression;
1657 }
1658 for (Iterator iter = args.iterator(); iter.hasNext();) {
1659 if (iter.next() instanceof SpreadExpression) return true;
1660 }
1661 return false;
1662 }
1663
1664 protected static int argumentSize(Expression arguments) {
1665 if (arguments instanceof TupleExpression) {
1666 TupleExpression tupleExpression = (TupleExpression) arguments;
1667 int size = tupleExpression.getExpressions().size();
1668 return size;
1669 }
1670 return 1;
1671 }
1672
1673 public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
1674 onLineNumber(call, "visitStaticMethodCallExpression: \"" + call.getMethod() + "\":");
1675
1676 makeCall(
1677 new ClassExpression(call.getOwnerType()),
1678 new ConstantExpression(call.getMethod()),
1679 call.getArguments(),
1680 invokeStaticMethod,
1681 false,false,false);
1682 }
1683
1684 private void visitSpecialConstructorCall(ConstructorCallExpression call) {
1685 ClassNode callNode = classNode;
1686 if (call.isSuperCall()) callNode = callNode.getSuperClass();
1687 List constructors = sortConstructors(call, callNode);
1688 call.getArguments().visit(this);
1689 // keep Objet[] on stack
1690 cv.visitInsn(DUP);
1691 // to select the constructor we need also the number of
1692 // available constructors and the class we want to make
1693 // the call on
1694 helper.pushConstant(constructors.size());
1695 visitClassExpression(new ClassExpression(callNode));
1696 // removes one Object[] leaves the int containing the
1697 // call flags and the construtcor number
1698 selectConstructorAndTransformArguments.call(cv);
1699 // Object[],int -> int,Object[],int
1700 // we need to examine the flags and maybe change the
1701 // Object[] later, so this reordering will do the job
1702 cv.visitInsn(DUP_X1);
1703 // test if rewrap flag is set
1704 cv.visitInsn(ICONST_1);
1705 cv.visitInsn(IAND);
1706 Label afterIf = new Label();
1707 cv.visitJumpInsn(IFEQ, afterIf);
1708 // true part, so rewrap using the first argument
1709 cv.visitInsn(ICONST_0);
1710 cv.visitInsn(AALOAD);
1711 cv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
1712 cv.visitLabel(afterIf);
1713 // here the stack is int,Object[], but we need the
1714 // the int for our table, so swap it
1715 cv.visitInsn(SWAP);
1716 //load "this"
1717 cv.visitVarInsn(ALOAD, 0);
1718 cv.visitInsn(SWAP);
1719 //prepare switch with >>8
1720 cv.visitIntInsn(BIPUSH,8);
1721 cv.visitInsn(ISHR);
1722 Label[] targets = new Label[constructors.size()];
1723 int[] indices = new int[constructors.size()];
1724 for (int i=0; i<targets.length; i++) {
1725 targets[i] = new Label();
1726 indices[i] = i;
1727 }
1728 // create switch targets
1729 Label defaultLabel = new Label();
1730 Label afterSwitch = new Label();
1731 cv.visitLookupSwitchInsn(defaultLabel, indices, targets);
1732 for (int i=0; i<targets.length; i++) {
1733 cv.visitLabel(targets[i]);
1734 // to keep the stack height, we need to leave
1735 // one Object[] on the stack as last element. At the
1736 // same time, we need the Object[] on top of the stack
1737 // to extract the parameters. So a SWAP will exchange
1738 // "this" and Object[], a DUP_X1 will then copy the Object[]
1739 /// to the last place in the stack:
1740 // Object[],this -SWAP-> this,Object[]
1741 // this,Object[] -DUP_X1-> Object[],this,Object[]
1742 cv.visitInsn(SWAP);
1743 cv.visitInsn(DUP_X1);
1744
1745 ConstructorNode cn = (ConstructorNode) constructors.get(i);
1746 String descriptor = helper.getMethodDescriptor(ClassHelper.VOID_TYPE, cn.getParameters());
1747 // unwrap the Object[] and make transformations if needed
1748 // that means, to duplicate the Object[], make a cast with possible
1749 // unboxing and then swap it with the Object[] for each parameter
1750 Parameter[] parameters = cn.getParameters();
1751 for (int p=0; p<parameters.length; p++) {
1752 cv.visitInsn(DUP);
1753 helper.pushConstant(p);
1754 cv.visitInsn(AALOAD);
1755 ClassNode type = parameters[p].getType();
1756 if (ClassHelper.isPrimitiveType(type)) {
1757 helper.unbox(type);
1758 } else {
1759 helper.doCast(type);
1760 }
1761 helper.swapWithObject(type);
1762 }
1763 // at the end we remove the Object[]
1764 cv.visitInsn(POP);
1765 // make the constructor call
1766 cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(callNode), "<init>", descriptor);
1767 cv.visitJumpInsn(GOTO, afterSwitch);
1768 }
1769 cv.visitLabel(defaultLabel);
1770 // this part should never be reached!
1771 cv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
1772 cv.visitInsn(DUP);
1773 cv.visitLdcInsn("illegal constructor number");
1774 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V");
1775 cv.visitInsn(ATHROW);
1776 cv.visitLabel(afterSwitch);
1777 }
1778
1779 private List sortConstructors(ConstructorCallExpression call, ClassNode callNode) {
1780 // sort in a new list to prevent side effects
1781 List constructors = new ArrayList(callNode.getDeclaredConstructors());
1782 Comparator comp = new Comparator() {
1783 public int compare(Object arg0, Object arg1) {
1784 ConstructorNode c0 = (ConstructorNode) arg0;
1785 ConstructorNode c1 = (ConstructorNode) arg1;
1786 String descriptor0 = helper.getMethodDescriptor(ClassHelper.VOID_TYPE, c0.getParameters());
1787 String descriptor1 = helper.getMethodDescriptor(ClassHelper.VOID_TYPE, c1.getParameters());
1788 return descriptor0.compareTo(descriptor1);
1789 }
1790 };
1791 Collections.sort(constructors,comp);
1792 return constructors;
1793 }
1794
1795 public void visitConstructorCallExpression(ConstructorCallExpression call) {
1796 onLineNumber(call, "visitConstructorCallExpression: \"" + call.getType().getName() + "\":");
1797
1798 if (call.isSpecialCall()){
1799 visitSpecialConstructorCall(call);
1800 return;
1801 }
1802
1803 Expression arguments = call.getArguments();
1804 if (arguments instanceof TupleExpression) {
1805 TupleExpression tupleExpression = (TupleExpression) arguments;
1806 int size = tupleExpression.getExpressions().size();
1807 if (size == 0) {
1808 arguments = MethodCallExpression.NO_ARGUMENTS;
1809 }
1810 }
1811
1812 Expression receiverClass = new ClassExpression(call.getType());
1813 makeCall(
1814 receiverClass, null,
1815 arguments,
1816 invokeNew, false, false, false
1817 );
1818 }
1819
1820 private static String makeFieldClassName(ClassNode type) {
1821 String internalName = BytecodeHelper.getClassInternalName(type);
1822 StringBuffer ret = new StringBuffer(internalName.length());
1823 for (int i=0; i<internalName.length(); i++) {
1824 char c = internalName.charAt(i);
1825 if (c=='/') {
1826 ret.append('$');
1827 } else if (c==';') {
1828 //append nothing -> delete ';'
1829 } else {
1830 ret.append(c);
1831 }
1832 }
1833 return ret.toString();
1834 }
1835
1836 private static String getStaticFieldName(ClassNode type) {
1837 ClassNode componentType = type;
1838 String prefix = "";
1839 for (; componentType.isArray(); componentType=componentType.getComponentType()){
1840 prefix+="$";
1841 }
1842 if (prefix.length()!=0) prefix = "array"+prefix;
1843 String name = prefix+"class$" + makeFieldClassName(componentType);
1844 return name;
1845 }
1846
1847 private void visitAttributeOrProperty(PropertyExpression expression, MethodCallerMultiAdapter adapter) {
1848 Expression objectExpression = expression.getObjectExpression();
1849 if (isThisExpression(objectExpression)) {
1850 // lets use the field expression if its available
1851 String name = expression.getPropertyAsString();
1852 if (name!=null) {
1853 FieldNode field = classNode.getField(name);
1854 if (field != null) {
1855 visitFieldExpression(new FieldExpression(field));
1856 return;
1857 }
1858 }
1859 }
1860
1861 // arguments already on stack if any
1862 makeCall(
1863 objectExpression, // receiver
1864 new CastExpression(ClassHelper.STRING_TYPE, expression.getProperty()), // messageName
1865 MethodCallExpression.NO_ARGUMENTS,
1866 adapter,
1867 expression.isSafe(), expression.isSpreadSafe(), expression.isImplicitThis()
1868 );
1869 }
1870
1871 private boolean isStaticContext(){
1872 if (!isInClosure()) return false;
1873 if (constructorNode != null) return false;
1874 return classNode.isStaticClass() || methodNode.isStatic();
1875 }
1876
1877 public void visitPropertyExpression(PropertyExpression expression) {
1878 Expression objectExpression = expression.getObjectExpression();
1879 MethodCallerMultiAdapter adapter;
1880 if (leftHandExpression) {
1881 adapter = setProperty;
1882 if (isGroovyObject(objectExpression)) adapter = setGroovyObjectProperty;
1883 if (isStaticContext() && isThisOrSuper(objectExpression)) adapter = setProperty;
1884 } else {
1885 adapter = getProperty;
1886 if (isGroovyObject(objectExpression)) adapter = getGroovyObjectProperty;
1887 if (isStaticContext() && isThisOrSuper(objectExpression)) adapter = getProperty;
1888 }
1889 visitAttributeOrProperty(expression,adapter);
1890 }
1891
1892 public void visitAttributeExpression(AttributeExpression expression) {
1893 Expression objectExpression = expression.getObjectExpression();
1894 MethodCallerMultiAdapter adapter;
1895 if (leftHandExpression) {
1896 adapter = setField;
1897 if (isGroovyObject(objectExpression)) adapter = setGroovyObjectField;
1898 if (usesSuper(expression)) adapter = getFieldOnSuper;
1899 } else {
1900 adapter = getField;
1901 if (isGroovyObject(objectExpression)) adapter = getGroovyObjectField;
1902 if (usesSuper(expression)) adapter = getFieldOnSuper;
1903 }
1904 visitAttributeOrProperty(expression,adapter);
1905 }
1906
1907 protected boolean isGroovyObject(Expression objectExpression) {
1908 return isThisExpression(objectExpression);
1909 }
1910
1911 public void visitFieldExpression(FieldExpression expression) {
1912 FieldNode field = expression.getField();
1913
1914 if (field.isStatic()) {
1915 if (leftHandExpression) {
1916 storeStaticField(expression);
1917 }else {
1918 loadStaticField(expression);
1919 }
1920 } else {
1921 if (leftHandExpression) {
1922 storeThisInstanceField(expression);
1923 } else {
1924 loadInstanceField(expression);
1925 }
1926 }
1927 }
1928
1929 /**
1930 *
1931 * @param fldExp
1932 */
1933 public void loadStaticField(FieldExpression fldExp) {
1934 FieldNode field = fldExp.getField();
1935 boolean holder = field.isHolder() && !isInClosureConstructor();
1936 ClassNode type = field.getType();
1937
1938 String ownerName = (field.getOwner().equals(classNode))
1939 ? internalClassName
1940 : BytecodeHelper.getClassInternalName(field.getOwner());
1941 if (holder) {
1942 cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
1943 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
1944 }
1945 else {
1946 cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
1947 if (ClassHelper.isPrimitiveType(type)) {
1948 helper.box(type);
1949 } else {
1950 }
1951 }
1952 }
1953
1954 /**
1955 * RHS instance field. should move most of the code in the BytecodeHelper
1956 * @param fldExp
1957 */
1958 public void loadInstanceField(FieldExpression fldExp) {
1959 FieldNode field = fldExp.getField();
1960 boolean holder = field.isHolder() && !isInClosureConstructor();
1961 ClassNode type = field.getType();
1962 String ownerName = (field.getOwner().equals(classNode))
1963 ? internalClassName
1964 : helper.getClassInternalName(field.getOwner());
1965
1966 cv.visitVarInsn(ALOAD, 0);
1967 cv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
1968
1969 if (holder) {
1970 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
1971 } else {
1972 if (ClassHelper.isPrimitiveType(type)) {
1973 helper.box(type);
1974 } else {
1975 }
1976 }
1977 }
1978
1979 public void storeThisInstanceField(FieldExpression expression) {
1980 FieldNode field = expression.getField();
1981
1982 boolean holder = field.isHolder() && !isInClosureConstructor();
1983 ClassNode type = field.getType();
1984
1985 String ownerName = (field.getOwner().equals(classNode)) ?
1986 internalClassName : BytecodeHelper.getClassInternalName(field.getOwner());
1987 if (holder) {
1988 cv.visitVarInsn(ALOAD, 0);
1989 cv.visitFieldInsn(GETFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
1990 cv.visitInsn(SWAP);
1991 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
1992 }
1993 else {
1994 if (isInClosureConstructor()) {
1995 helper.doCast(type);
1996 } else if (!ClassHelper.isPrimitiveType(type)){
1997 doConvertAndCast(type);
1998 }
1999 cv.visitVarInsn(ALOAD, 0);
2000 //helper.swapObjectWith(type);
2001 cv.visitInsn(SWAP);
2002 helper.unbox(type);
2003 helper.putField(field, ownerName);
2004 }
2005 }
2006
2007
2008 public void storeStaticField(FieldExpression expression) {
2009 FieldNode field = expression.getField();
2010
2011 boolean holder = field.isHolder() && !isInClosureConstructor();
2012
2013 ClassNode type = field.getType();
2014
2015 String ownerName = (field.getOwner().equals(classNode))
2016 ? internalClassName
2017 : helper.getClassInternalName(field.getOwner());
2018 if (holder) {
2019 cv.visitFieldInsn(GETSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2020 cv.visitInsn(SWAP);
2021 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
2022 } else {
2023 helper.doCast(type);
2024 cv.visitFieldInsn(PUTSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
2025 }
2026 }
2027
2028 protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first ) {
2029 FieldNode field = expression.getField();
2030 boolean isStatic = field.isStatic();
2031
2032 int tempIdx = compileStack.defineTemporaryVariable(field, leftHandExpression && first);
2033
2034 if (steps > 1 || !isStatic) {
2035 cv.visitVarInsn(ALOAD, 0);
2036 cv.visitFieldInsn(
2037 GETFIELD,
2038 internalClassName,
2039 "owner",
2040 BytecodeHelper.getTypeDescription(outerClassNode));
2041 }
2042
2043 if( steps == 1 ) {
2044 int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
2045 String ownerName = BytecodeHelper.getClassInternalName(outerClassNode);
2046
2047 if (leftHandExpression) {
2048 cv.visitVarInsn(ALOAD, tempIdx);
2049 boolean holder = field.isHolder() && !isInClosureConstructor();
2050 if ( !holder) {
2051 doConvertAndCast(field.getType());
2052 }
2053 }
2054 cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType()));
2055 if (!leftHandExpression) {
2056 if (ClassHelper.isPrimitiveType(field.getType())) {
2057 helper.box(field.getType());
2058 }
2059 }
2060 }
2061
2062 else {
2063 visitOuterFieldExpression( expression, outerClassNode.getOuterClass(), steps - 1, false );
2064 }
2065 }
2066
2067
2068
2069 /**
2070 * Visits a bare (unqualified) variable expression.
2071 */
2072
2073 public void visitVariableExpression(VariableExpression expression) {
2074
2075 String variableName = expression.getName();
2076
2077 //-----------------------------------------------------------------------
2078 // SPECIAL CASES
2079
2080 //
2081 // "this" for static methods is the Class instance
2082
2083 ClassNode classNode = this.classNode;
2084 if (isInClosure()) classNode = getOutermostClass();
2085
2086 if (variableName.equals("this")) {
2087 if (isStaticMethod() || (!implicitThis && isStaticContext())) {
2088 visitClassExpression(new ClassExpression(classNode));
2089 } else {
2090 loadThis();
2091 }
2092 return;
2093 }
2094
2095 //
2096 // "super" also requires special handling
2097
2098 if (variableName.equals("super")) {
2099 if (isStaticMethod()) {
2100 visitClassExpression(new ClassExpression(classNode.getSuperClass()));
2101 } else {
2102 loadThis();
2103 }
2104 return; // <<< FLOW CONTROL <<<<<<<<<
2105 }
2106
2107 Variable variable = compileStack.getVariable(variableName, false);
2108
2109 VariableScope scope = compileStack.getScope();
2110 if (variable==null) {
2111 processClassVariable(variableName);
2112 } else {
2113 processStackVariable(variable);
2114 }
2115 }
2116
2117 private void loadThis() {
2118 cv.visitVarInsn(ALOAD, 0);
2119 if (!implicitThis && isInClosure()) {
2120 cv.visitMethodInsn(
2121 INVOKEVIRTUAL,
2122 "groovy/lang/Closure",
2123 "getThisObject",
2124 "()Ljava/lang/Object;"
2125 );
2126 }
2127 }
2128
2129 protected void processStackVariable(Variable variable) {
2130 if( leftHandExpression ) {
2131 helper.storeVar(variable);
2132 } else {
2133 helper.loadVar(variable);
2134 }
2135 if (ASM_DEBUG) {
2136 helper.mark("var: " + variable.getName());
2137 }
2138 }
2139
2140 protected void processClassVariable(String name) {
2141 if (passingClosureParams && isInScriptBody() ) {
2142 // lets create a ScriptReference to pass into the closure
2143 cv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference");
2144 cv.visitInsn(DUP);
2145
2146 loadThisOrOwner();
2147 cv.visitLdcInsn(name);
2148
2149 cv.visitMethodInsn(
2150 INVOKESPECIAL,
2151 "org/codehaus/groovy/runtime/ScriptReference",
2152 "<init>",
2153 "(Lgroovy/lang/Script;Ljava/lang/String;)V");
2154 }
2155 else {
2156 PropertyExpression pexp = new PropertyExpression(VariableExpression.THIS_EXPRESSION, name);
2157 pexp.setImplicitThis(true);
2158 visitPropertyExpression(pexp);
2159 }
2160 }
2161
2162
2163 protected void processFieldAccess( String name, FieldNode field, int steps ) {
2164 FieldExpression expression = new FieldExpression(field);
2165
2166 if( steps == 0 ) {
2167 visitFieldExpression( expression );
2168 }
2169 else {
2170 visitOuterFieldExpression( expression, classNode.getOuterClass(), steps, true );
2171 }
2172 }
2173
2174
2175
2176 /**
2177 * @return true if we are in a script body, where all variables declared are no longer
2178 * local variables but are properties
2179 */
2180 protected boolean isInScriptBody() {
2181 if (classNode.isScriptBody()) {
2182 return true;
2183 }
2184 else {
2185 return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
2186 }
2187 }
2188
2189 /**
2190 * @return true if this expression will have left a value on the stack
2191 * that must be popped
2192 */
2193 protected boolean isPopRequired(Expression expression) {
2194 if (expression instanceof MethodCallExpression) {
2195 if (expression.getType()==ClassHelper.VOID_TYPE) { // nothing on the stack
2196 return false;
2197 } else {
2198 return !usesSuper((MethodCallExpression) expression);
2199 }
2200 }
2201 if (expression instanceof DeclarationExpression) {
2202 return false;
2203 }
2204 if (expression instanceof BinaryExpression) {
2205 BinaryExpression binExp = (BinaryExpression) expression;
2206 switch (binExp.getOperation().getType()) { // br todo should leave a copy of the value on the stack for all the assignemnt.
2207 // 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
2208 // case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAsignment()
2209 // case Types.MINUS_EQUAL :
2210 // case Types.MULTIPLY_EQUAL :
2211 // case Types.DIVIDE_EQUAL :
2212 // case Types.INTDIV_EQUAL :
2213 // case Types.MOD_EQUAL :
2214 // return false;
2215 }
2216 }
2217 if (expression instanceof ConstructorCallExpression) {
2218 ConstructorCallExpression cce = (ConstructorCallExpression) expression;
2219 return !cce.isSpecialCall();
2220 }
2221 return true;
2222 }
2223
2224 protected void createInterfaceSyntheticStaticFields() {
2225 if (syntheticStaticFields.isEmpty()) return;
2226
2227 addInnerClass(interfaceClassLoadingClass);
2228
2229 for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
2230 String staticFieldName = (String) iter.next();
2231 // generate a field node
2232 interfaceClassLoadingClass.addField(staticFieldName,ACC_STATIC + ACC_SYNTHETIC,ClassHelper.CLASS_Type,null);
2233 }
2234 }
2235
2236 protected void createSyntheticStaticFields() {
2237 for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
2238 String staticFieldName = (String) iter.next();
2239 // generate a field node
2240 FieldNode fn = classNode.getField(staticFieldName);
2241 if (fn!=null) {
2242 boolean type = fn.getType()==ClassHelper.CLASS_Type;
2243 boolean modifiers = fn.getModifiers() == ACC_STATIC + ACC_SYNTHETIC;
2244 if (type && modifiers) continue;
2245 String text = "";
2246 if (!type) text = " with wrong type: "+fn.getType()+" (java.lang.Class needed)";
2247 if (!modifiers) text = " with wrong modifiers: "+fn.getModifiers()+" ("+(ACC_STATIC + ACC_SYNTHETIC)+" needed)";
2248 throwException(
2249 "tried to set a static syntethic field "+staticFieldName+" in "+classNode.getName()+
2250 " for class resolving, but found alreeady a node of that"+
2251 " name "+text);
2252 } else {
2253 cw.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null);
2254 }
2255 }
2256
2257 cv =
2258 cw.visitMethod(
2259 ACC_STATIC + ACC_SYNTHETIC,
2260 "class$",
2261 "(Ljava/lang/String;)Ljava/lang/Class;",
2262 null,
2263 null);
2264 Label l0 = new Label();
2265 cv.visitLabel(l0);
2266 cv.visitVarInsn(ALOAD, 0);
2267 cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
2268 Label l1 = new Label();
2269 cv.visitLabel(l1);
2270 cv.visitInsn(ARETURN);
2271 Label l2 = new Label();
2272 cv.visitLabel(l2);
2273 cv.visitVarInsn(ASTORE, 1);
2274 cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
2275 cv.visitInsn(DUP);
2276 cv.visitVarInsn(ALOAD, 1);
2277 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;");
2278 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V");
2279 cv.visitInsn(ATHROW);
2280 cv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
2281 cv.visitMaxs(3, 2);
2282 }
2283
2284 /** load class object on stack */
2285 public void visitClassExpression(ClassExpression expression) {
2286 ClassNode type = expression.getType();
2287
2288 if (ClassHelper.isPrimitiveType(type)) {
2289 ClassNode objectType = ClassHelper.getWrapper(type);
2290 cv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;");
2291 } else {
2292 String staticFieldName;
2293 if (type.equals(classNode)) {
2294 staticFieldName = "class$0";
2295 if (compileStack.getCurrentClassIndex()!=-1) {
2296 cv.visitVarInsn(ALOAD,compileStack.getCurrentClassIndex());
2297 return;
2298 }
2299 } else if (type.equals(ClassHelper.METACLASS_TYPE)) {
2300 staticFieldName = getStaticFieldName(type);
2301 if (compileStack.getCurrentMetaClassIndex()!=-1) {
2302 cv.visitVarInsn(ALOAD,compileStack.getCurrentMetaClassIndex());
2303 return;
2304 }
2305 } else {
2306 staticFieldName = getStaticFieldName(type);
2307 }
2308
2309 syntheticStaticFields.add(staticFieldName);
2310
2311 String internalClassName = this.internalClassName;
2312 if (classNode.isInterface()) {
2313 internalClassName = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass);
2314 }
2315
2316 cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
2317
2318 Label l0 = new Label();
2319 cv.visitJumpInsn(IFNONNULL, l0);
2320 cv.visitLdcInsn(BytecodeHelper.getClassLoadingTypeDescription(type));
2321 cv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
2322 cv.visitInsn(DUP);
2323 cv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
2324 Label l1 = new Label();
2325 cv.visitJumpInsn(GOTO, l1);
2326 cv.visitLabel(l0);
2327 cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
2328 cv.visitLabel(l1);
2329
2330 if (type.equals(classNode)) {
2331 cv.visitInsn(DUP);
2332 int index = compileStack.defineTemporaryVariable("class$0",ClassHelper.CLASS_Type,true);
2333 compileStack.setCurrentClassIndex(index);
2334 } else if (type.equals(ClassHelper.METACLASS_TYPE)) {
2335 cv.visitInsn(DUP);
2336 int index = compileStack.defineTemporaryVariable("meta$class$0",ClassHelper.CLASS_Type,true);
2337 compileStack.setCurrentMetaClassIndex(index);
2338 }
2339 }
2340 }
2341
2342 public void visitRangeExpression(RangeExpression expression) {
2343 expression.getFrom().visit(this);
2344 expression.getTo().visit(this);
2345
2346 helper.pushConstant(expression.isInclusive());
2347
2348 createRangeMethod.call(cv);
2349 }
2350
2351 public void visitMapEntryExpression(MapEntryExpression expression) {
2352 throw new GroovyBugError("MapEntryExpression should not be visited here");
2353 }
2354
2355 public void visitMapExpression(MapExpression expression) {
2356 List entries = expression.getMapEntryExpressions();
2357 int size = entries.size();
2358 helper.pushConstant(size * 2);
2359
2360 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2361
2362 int i = 0;
2363 for (Iterator iter = entries.iterator(); iter.hasNext();) {
2364 Object object = iter.next();
2365 MapEntryExpression entry = (MapEntryExpression) object;
2366
2367 cv.visitInsn(DUP);
2368 helper.pushConstant(i++);
2369 visitAndAutoboxBoolean(entry.getKeyExpression());
2370 cv.visitInsn(AASTORE);
2371
2372 cv.visitInsn(DUP);
2373 helper.pushConstant(i++);
2374 visitAndAutoboxBoolean(entry.getValueExpression());
2375 cv.visitInsn(AASTORE);
2376 }
2377 createMapMethod.call(cv);
2378 }
2379
2380 public void visitArgumentlistExpression(ArgumentListExpression ale) {
2381 if (containsSpreadExpression(ale)) {
2382 despreadList(ale.getExpressions(),true);
2383 } else {
2384 visitTupleExpression(ale,true);
2385 }
2386 }
2387
2388 public void visitTupleExpression(TupleExpression expression) {
2389 visitTupleExpression(expression,false);
2390 }
2391
2392 private void visitTupleExpression(TupleExpression expression,boolean useWrapper) {
2393 int size = expression.getExpressions().size();
2394
2395 helper.pushConstant(size);
2396
2397 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2398
2399 for (int i = 0; i < size; i++) {
2400 cv.visitInsn(DUP);
2401 helper.pushConstant(i);
2402 Expression argument = expression.getExpression(i);
2403 visitAndAutoboxBoolean(argument);
2404 if (useWrapper && argument instanceof CastExpression) loadWrapper(argument);
2405
2406 cv.visitInsn(AASTORE);
2407 }
2408 }
2409
2410 private void loadWrapper(Expression argument) {
2411 ClassNode goalClass = argument.getType();
2412 visitClassExpression(new ClassExpression(goalClass));
2413 if (goalClass.isDerivedFromGroovyObject()) {
2414 createGroovyObjectWrapperMethod.call(cv);
2415 } else {
2416 createPojoWrapperMethod.call(cv);
2417 }
2418 }
2419
2420 public void visitArrayExpression(ArrayExpression expression) {
2421 ClassNode elementType = expression.getElementType();
2422 String arrayTypeName = BytecodeHelper.getClassInternalName(elementType);
2423 List sizeExpression = expression.getSizeExpression();
2424
2425 int size=0;
2426 int dimensions=0;
2427 if (sizeExpression!=null) {
2428 for (Iterator iter = sizeExpression.iterator(); iter.hasNext();) {
2429 Expression element = (Expression) iter.next();
2430 if (element==ConstantExpression.EMTPY_EXPRESSION) break;
2431 dimensions++;
2432 // lets convert to an int
2433 visitAndAutoboxBoolean(element);
2434 helper.unbox(int.class);
2435 }
2436 } else {
2437 size = expression.getExpressions().size();
2438 helper.pushConstant(size);
2439 }
2440
2441 int storeIns=AASTORE;
2442 if (sizeExpression!=null) {
2443 arrayTypeName = BytecodeHelper.getTypeDescription(expression.getType());
2444 cv.visitMultiANewArrayInsn(arrayTypeName, dimensions);
2445 } else if (ClassHelper.isPrimitiveType(elementType)) {
2446 int primType=0;
2447 if (elementType==ClassHelper.boolean_TYPE) {
2448 primType = T_BOOLEAN;
2449 storeIns = BASTORE;
2450 } else if (elementType==ClassHelper.char_TYPE) {
2451 primType = T_CHAR;
2452 storeIns = CASTORE;
2453 } else if (elementType==ClassHelper.float_TYPE) {
2454 primType = T_FLOAT;
2455 storeIns = FASTORE;
2456 } else if (elementType==ClassHelper.double_TYPE) {
2457 primType = T_DOUBLE;
2458 storeIns = DASTORE;
2459 } else if (elementType==ClassHelper.byte_TYPE) {
2460 primType = T_BYTE;
2461 storeIns = BASTORE;
2462 } else if (elementType==ClassHelper.short_TYPE) {
2463 primType = T_SHORT;
2464 storeIns = SASTORE;
2465 } else if (elementType==ClassHelper.int_TYPE) {
2466 primType = T_INT;
2467 storeIns=IASTORE;
2468 } else if (elementType==ClassHelper.long_TYPE) {
2469 primType = T_LONG;
2470 storeIns = LASTORE;
2471 }
2472 cv.visitIntInsn(NEWARRAY, primType);
2473 } else {
2474 cv.visitTypeInsn(ANEWARRAY, arrayTypeName);
2475 }
2476
2477 for (int i = 0; i < size; i++) {
2478 cv.visitInsn(DUP);
2479 helper.pushConstant(i);
2480 Expression elementExpression = expression.getExpression(i);
2481 if (elementExpression == null) {
2482 ConstantExpression.NULL.visit(this);
2483 } else {
2484 if (!elementType.equals(elementExpression.getType())) {
2485 visitCastExpression(new CastExpression(elementType, elementExpression, true));
2486 } else {
2487 visitAndAutoboxBoolean(elementExpression);
2488 }
2489 }
2490 cv.visitInsn(storeIns);
2491 }
2492
2493 if (sizeExpression==null && ClassHelper.isPrimitiveType(elementType)) {
2494 int par = compileStack.defineTemporaryVariable("par",true);
2495 cv.visitVarInsn(ALOAD, par);
2496 }
2497 }
2498
2499 public void visitListExpression(ListExpression expression) {
2500 int size = expression.getExpressions().size();
2501 boolean containsSpreadExpression = containsSpreadExpression(expression);
2502 if (!containsSpreadExpression) {
2503 helper.pushConstant(size);
2504
2505 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2506
2507 for (int i = 0; i < size; i++) {
2508 cv.visitInsn(DUP);
2509 helper.pushConstant(i);
2510 visitAndAutoboxBoolean(expression.getExpression(i));
2511 cv.visitInsn(AASTORE);
2512 }
2513 } else {
2514 despreadList(expression.getExpressions(),false);
2515 }
2516 createListMethod.call(cv);
2517 }
2518
2519 public void visitGStringExpression(GStringExpression expression) {
2520 int size = expression.getValues().size();
2521 helper.pushConstant(size);
2522
2523 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2524
2525 for (int i = 0; i < size; i++) {
2526 cv.visitInsn(DUP);
2527 helper.pushConstant(i);
2528 visitAndAutoboxBoolean(expression.getValue(i));
2529 cv.visitInsn(AASTORE);
2530 }
2531
2532 int paramIdx = compileStack.defineTemporaryVariable("iterator",true);
2533
2534 ClassNode innerClass = createGStringClass(expression);
2535 addInnerClass(innerClass);
2536 String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass);
2537
2538 cv.visitTypeInsn(NEW, innerClassinternalName);
2539 cv.visitInsn(DUP);
2540 cv.visitVarInsn(ALOAD, paramIdx);
2541
2542 cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", "([Ljava/lang/Object;)V");
2543 compileStack.removeVar(paramIdx);
2544 }
2545
2546 public void visitAnnotations(AnnotatedNode node) {
2547 Map annotionMap = node.getAnnotations();
2548 if (annotionMap.isEmpty()) return;
2549 Iterator it = annotionMap.values().iterator();
2550 while (it.hasNext()) {
2551 AnnotationNode an = (AnnotationNode) it.next();
2552 //skip builtin properties
2553 if (an.isBuiltIn()) continue;
2554 ClassNode type = an.getClassNode();
2555
2556 String clazz = type.getName();
2557 AnnotationVisitor av = cw.visitAnnotation(BytecodeHelper.formatNameForClassLoading(clazz),false);
2558
2559 Iterator mIt = an.getMembers().keySet().iterator();
2560 while (mIt.hasNext()) {
2561 String name = (String) mIt.next();
2562 ConstantExpression exp = (ConstantExpression) an.getMember(name);
2563 av.visit(name,exp.getValue());
2564 }
2565 av.visitEnd();
2566 }
2567 }
2568
2569
2570 // Implementation methods
2571 //-------------------------------------------------------------------------
2572 protected boolean addInnerClass(ClassNode innerClass) {
2573 innerClass.setModule(classNode.getModule());
2574 return innerClasses.add(innerClass);
2575 }
2576
2577 protected ClassNode createClosureClass(ClosureExpression expression) {
2578 ClassNode outerClass = getOutermostClass();
2579 String name = outerClass.getName() + "$"
2580 + context.getNextClosureInnerName(outerClass, classNode, methodNode); // br added a more infomative name
2581 boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass();
2582
2583 Parameter[] parameters = expression.getParameters();
2584 if (parameters==null){
2585 parameters = new Parameter[0];
2586 } else if (parameters.length == 0) {
2587 // lets create a default 'it' parameter
2588 parameters = new Parameter[] { new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL)};
2589 }
2590
2591 Parameter[] localVariableParams = getClosureSharedVariables(expression);
2592
2593 InnerClassNode answer = new InnerClassNode(outerClass, name, 0, ClassHelper.CLOSURE_TYPE); // closures are local inners and not public
2594 answer.setEnclosingMethod(this.methodNode);
2595 answer.setSynthetic(true);
2596
2597 if (staticMethodOrInStaticClass) {
2598 answer.setStaticClass(true);
2599 }
2600 if (isInScriptBody()) {
2601 answer.setScriptBody(true);
2602 }
2603 MethodNode method =
2604 answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
2605 method.setSourcePosition(expression);
2606
2607 VariableScope varScope = expression.getVariableScope();
2608 if (varScope == null) {
2609 throw new RuntimeException(
2610 "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
2611 } else {
2612 method.setVariableScope(varScope.copy());
2613 }
2614 if (parameters.length > 1
2615 || (parameters.length == 1
2616 && parameters[0].getType() != null
2617 && parameters[0].getType() != ClassHelper.OBJECT_TYPE)) {
2618
2619 // lets add a typesafe call method
2620 MethodNode call = answer.addMethod(
2621 "call",
2622 ACC_PUBLIC,
2623 ClassHelper.OBJECT_TYPE,
2624 parameters,
2625 ClassNode.EMPTY_ARRAY,
2626 new ReturnStatement(
2627 new MethodCallExpression(
2628 VariableExpression.THIS_EXPRESSION,
2629 "doCall",
2630 new ArgumentListExpression(parameters))));
2631 call.setSourcePosition(expression);
2632 }
2633
2634 // lets make the constructor
2635 BlockStatement block = new BlockStatement();
2636 block.setSourcePosition(expression);
2637 VariableExpression outer = new VariableExpression("_outerInstance");
2638 outer.setSourcePosition(expression);
2639 block.getVariableScope().getReferencedLocalVariables().put("_outerInstance",outer);
2640 VariableExpression thisObject = new VariableExpression("_thisObject");
2641 thisObject.setSourcePosition(expression);
2642 block.getVariableScope().getReferencedLocalVariables().put("_thisObject",thisObject);
2643 TupleExpression conArgs = new TupleExpression();
2644 conArgs.addExpression(outer);
2645 conArgs.addExpression(thisObject);
2646 block.addStatement(
2647 new ExpressionStatement(
2648 new ConstructorCallExpression(
2649 ClassNode.SUPER,
2650 conArgs)));
2651
2652 // lets assign all the parameter fields from the outer context
2653 for (int i = 0; i < localVariableParams.length; i++) {
2654 Parameter param = localVariableParams[i];
2655 String paramName = param.getName();
2656 Expression initialValue = null;
2657 ClassNode type = param.getType();
2658 FieldNode paramField = null;
2659 if (true) {
2660 initialValue = new VariableExpression(paramName);
2661 ClassNode realType = type;
2662 type = ClassHelper.makeReference();
2663 param.setType(type);
2664 paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue);
2665 paramField.setHolder(true);
2666 String methodName = Verifier.capitalize(paramName);
2667
2668 // lets add a getter & setter
2669 Expression fieldExp = new FieldExpression(paramField);
2670 answer.addMethod(
2671 "get" + methodName,
2672 ACC_PUBLIC,
2673 realType,
2674 Parameter.EMPTY_ARRAY,
2675 ClassNode.EMPTY_ARRAY,
2676 new ReturnStatement(fieldExp));
2677
2678 /*
2679 answer.addMethod(
2680 "set" + methodName,
2681 ACC_PUBLIC,
2682 "void",
2683 new Parameter[] { new Parameter(realType, "__value") },
2684 new ExpressionStatement(
2685 new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
2686 */
2687 }
2688 }
2689
2690 Parameter[] params = new Parameter[2 + localVariableParams.length];
2691 params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance");
2692 params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject");
2693 System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
2694
2695 ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, block);
2696 sn.setSourcePosition(expression);
2697 return answer;
2698 }
2699
2700 protected Parameter[] getClosureSharedVariables(ClosureExpression ce){
2701 VariableScope scope = ce.getVariableScope();
2702 Map references = scope.getReferencedLocalVariables();
2703 Parameter[] ret = new Parameter[references.size()];
2704 int index = 0;
2705 for (Iterator iter = references.values().iterator(); iter.hasNext();) {
2706 org.codehaus.groovy.ast.Variable element = (org.codehaus.groovy.ast.Variable) iter.next();
2707 if (element instanceof Parameter) {
2708 ret[index] = (Parameter) element;
2709 } else {
2710 Parameter p = new Parameter(element.getType(),element.getName());
2711 ret[index] = p;
2712 }
2713 index++;
2714 }
2715 return ret;
2716 }
2717
2718 protected ClassNode getOutermostClass() {
2719 if (outermostClass == null) {
2720 outermostClass = classNode;
2721 while (outermostClass instanceof InnerClassNode) {
2722 outermostClass = outermostClass.getOuterClass();
2723 }
2724 }
2725 return outermostClass;
2726 }
2727
2728 protected ClassNode createGStringClass(GStringExpression expression) {
2729 ClassNode owner = classNode;
2730 if (owner instanceof InnerClassNode) {
2731 owner = owner.getOuterClass();
2732 }
2733 String outerClassName = owner.getName();
2734 String name = outerClassName + "$" + context.getNextInnerClassIdx();
2735 InnerClassNode answer = new InnerClassNode(owner, name, 0, ClassHelper.GSTRING_TYPE);
2736 answer.setEnclosingMethod(this.methodNode);
2737 FieldNode stringsField =
2738 answer.addField(
2739 "strings",
2740 ACC_PRIVATE /*| ACC_STATIC*/,
2741 ClassHelper.STRING_TYPE.makeArray(),
2742 new ArrayExpression(ClassHelper.STRING_TYPE, expression.getStrings()));
2743 answer.addMethod(
2744 "getStrings",
2745 ACC_PUBLIC,
2746 ClassHelper.STRING_TYPE.makeArray(),
2747 Parameter.EMPTY_ARRAY,
2748 ClassNode.EMPTY_ARRAY,
2749 new ReturnStatement(new FieldExpression(stringsField)));
2750 // lets make the constructor
2751 BlockStatement block = new BlockStatement();
2752 block.addStatement(
2753 new ExpressionStatement(
2754 new ConstructorCallExpression(ClassNode.SUPER, new VariableExpression("values"))));
2755 Parameter[] contructorParams = new Parameter[] { new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "values")};
2756 answer.addConstructor(ACC_PUBLIC, contructorParams, ClassNode.EMPTY_ARRAY, block);
2757 return answer;
2758 }
2759
2760 protected void doConvertAndCast(ClassNode type){
2761 doConvertAndCast(type,false);
2762 }
2763
2764 protected void doConvertAndCast(ClassNode type, boolean coerce) {
2765 if (type==ClassHelper.OBJECT_TYPE) return;
2766 if (isValidTypeForCast(type)) {
2767 visitClassExpression(new ClassExpression(type));
2768 if (coerce) {
2769 asTypeMethod.call(cv);
2770 } else {
2771 castToTypeMethod.call(cv);
2772 }
2773 }
2774 helper.doCast(type);
2775 }
2776
2777 protected void evaluateLogicalOrExpression(BinaryExpression expression) {
2778 visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
2779 Label l0 = new Label();
2780 Label l2 = new Label();
2781 cv.visitJumpInsn(IFEQ, l0);
2782
2783 cv.visitLabel(l2);
2784
2785 visitConstantExpression(ConstantExpression.TRUE);
2786
2787 Label l1 = new Label();
2788 cv.visitJumpInsn(GOTO, l1);
2789 cv.visitLabel(l0);
2790
2791 visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
2792
2793 cv.visitJumpInsn(IFNE, l2);
2794
2795 visitConstantExpression(ConstantExpression.FALSE);
2796 cv.visitLabel(l1);
2797 }
2798
2799 // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for
2800 // consistancy.
2801 protected void evaluateLogicalAndExpression(BinaryExpression expression) {
2802 visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
2803 Label l0 = new Label();
2804 cv.visitJumpInsn(IFEQ, l0);
2805
2806 visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
2807
2808 cv.visitJumpInsn(IFEQ, l0);
2809
2810 visitConstantExpression(ConstantExpression.TRUE);
2811
2812 Label l1 = new Label();
2813 cv.visitJumpInsn(GOTO, l1);
2814 cv.visitLabel(l0);
2815
2816 visitConstantExpression(ConstantExpression.FALSE);
2817
2818 cv.visitLabel(l1);
2819 }
2820
2821 protected void evaluateBinaryExpression(String method, BinaryExpression expression) {
2822 makeCall(
2823 expression.getLeftExpression(),
2824 new ConstantExpression(method),
2825 new ArgumentListExpression().addExpression(expression.getRightExpression()),
2826 invokeMethod, false, false, false
2827 );
2828 }
2829
2830 protected void evaluateCompareTo(BinaryExpression expression) {
2831 Expression leftExpression = expression.getLeftExpression();
2832 leftExpression.visit(this);
2833 if (isComparisonExpression(leftExpression)) {
2834 helper.boxBoolean();
2835 }
2836
2837 // if the right hand side is a boolean expression, we need to autobox
2838 Expression rightExpression = expression.getRightExpression();
2839 rightExpression.visit(this);
2840 if (isComparisonExpression(rightExpression)) {
2841 helper.boxBoolean();
2842 }
2843 compareToMethod.call(cv);
2844 }
2845
2846 protected void evaluateBinaryExpressionWithAsignment(String method, BinaryExpression expression) {
2847 Expression leftExpression = expression.getLeftExpression();
2848 if (leftExpression instanceof BinaryExpression) {
2849 BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
2850 if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
2851 // lets replace this assignment to a subscript operator with a
2852 // method call
2853 // e.g. x[5] += 10
2854 // -> (x, [], 5), =, x[5] + 10
2855 // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
2856
2857 MethodCallExpression methodCall =
2858 new MethodCallExpression(
2859 expression.getLeftExpression(),
2860 method,
2861 new ArgumentListExpression(new Expression[] { expression.getRightExpression()}));
2862
2863 Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression());
2864
2865 visitMethodCallExpression(
2866 new MethodCallExpression(
2867 leftBinExpr.getLeftExpression(),
2868 "putAt",
2869 new ArgumentListExpression(new Expression[] { safeIndexExpr, methodCall })));
2870 //cv.visitInsn(POP);
2871 return;
2872 }
2873 }
2874
2875 evaluateBinaryExpression(method, expression);
2876
2877 // br to leave a copy of rvalue on the stack. see also isPopRequired()
2878 cv.visitInsn(DUP);
2879
2880 leftHandExpression = true;
2881 evaluateExpression(leftExpression);
2882 leftHandExpression = false;
2883 }
2884
2885 private void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression expression) {
2886 Expression leftExp = expression.getLeftExpression();
2887 Expression rightExp = expression.getRightExpression();
2888 load(leftExp);
2889 load(rightExp);
2890 compareMethod.call(cv);
2891 }
2892
2893 protected void evaluateEqual(BinaryExpression expression) {
2894 Expression leftExpression = expression.getLeftExpression();
2895 if (leftExpression instanceof BinaryExpression) {
2896 BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
2897 if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
2898 // lets replace this assignment to a subscript operator with a
2899 // method call
2900 // e.g. x[5] = 10
2901 // -> (x, [], 5), =, 10
2902 // -> methodCall(x, "putAt", [5, 10])
2903
2904 visitMethodCallExpression(
2905 new MethodCallExpression(
2906 leftBinExpr.getLeftExpression(),
2907 "putAt",
2908 new ArgumentListExpression(
2909 new Expression[] { leftBinExpr.getRightExpression(), expression.getRightExpression()})));
2910 // cv.visitInsn(POP); //this is realted to isPopRequired()
2911 return;
2912 }
2913 }
2914
2915 // lets evaluate the RHS then hopefully the LHS will be a field
2916 Expression rightExpression = expression.getRightExpression();
2917 ClassNode type = getLHSType(leftExpression);
2918 // lets not cast for primitive types as we handle these in field setting etc
2919 if (ClassHelper.isPrimitiveType(type)) {
2920 visitAndAutoboxBoolean(rightExpression);
2921 } else if (type!=ClassHelper.OBJECT_TYPE){
2922 visitCastExpression(new CastExpression(type, rightExpression));
2923 } else {
2924 visitAndAutoboxBoolean(rightExpression);
2925 }
2926
2927 cv.visitInsn(DUP); // to leave a copy of the rightexpression value on the stack after the assignment.
2928 leftHandExpression = true;
2929 leftExpression.visit(this);
2930 leftHandExpression = false;
2931 }
2932
2933 /**
2934 * Deduces the type name required for some casting
2935 *
2936 * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
2937 */
2938 protected ClassNode getLHSType(Expression leftExpression) {
2939 if (leftExpression instanceof VariableExpression) {
2940 VariableExpression varExp = (VariableExpression) leftExpression;
2941 ClassNode type = varExp.getType();
2942 if (isValidTypeForCast(type)) {
2943 return type;
2944 }
2945 String variableName = varExp.getName();
2946 Variable variable = compileStack.getVariable(variableName,false);
2947 if (variable != null) {
2948 if (variable.isHolder()) {
2949 return type;
2950 }
2951 if (variable.isProperty()) return variable.getType();
2952 type = variable.getType();
2953 if (isValidTypeForCast(type)) {
2954 return type;
2955 }
2956 }
2957 else {
2958 FieldNode field = classNode.getField(variableName);
2959 if (field == null) {
2960 field = classNode.getOuterField(variableName);
2961 }
2962 if (field != null) {
2963 type = field.getType();
2964 if (!field.isHolder() && isValidTypeForCast(type)) {
2965 return type;
2966 }
2967 }
2968 }
2969 }
2970 else if (leftExpression instanceof FieldExpression) {
2971 FieldExpression fieldExp = (FieldExpression) leftExpression;
2972 ClassNode type = fieldExp.getType();
2973 if (isValidTypeForCast(type)) {
2974 return type;
2975 }
2976 }
2977 return ClassHelper.DYNAMIC_TYPE;
2978 }
2979
2980 protected boolean isValidTypeForCast(ClassNode type) {
2981 return type!=ClassHelper.DYNAMIC_TYPE &&
2982 type!=ClassHelper.REFERENCE_TYPE;
2983 }
2984
2985 protected void visitAndAutoboxBoolean(Expression expression) {
2986 expression.visit(this);
2987
2988 if (isComparisonExpression(expression)) {
2989 helper.boxBoolean(); // convert boolean to Boolean
2990 }
2991 }
2992
2993 protected void evaluatePrefixMethod(String method, Expression expression) {
2994 // execute method
2995 makeCall(
2996 expression,
2997 new ConstantExpression(method),
2998 MethodCallExpression.NO_ARGUMENTS,invokeMethod,
2999 false,false,false);
3000
3001 // store
3002 leftHandExpression = true;
3003 expression.visit(this);
3004
3005 // reload new value
3006 leftHandExpression = false;
3007 expression.visit(this);
3008 }
3009
3010 protected void evaluatePostfixMethod(String method, Expression expression) {
3011 // load
3012 expression.visit(this);
3013
3014 // save value for later
3015 int tempIdx = compileStack.defineTemporaryVariable("postfix_" + method, true);
3016
3017 //execute method
3018 makeCall(
3019 expression, new ConstantExpression(method),
3020 MethodCallExpression.NO_ARGUMENTS,
3021 invokeMethod,false,false, false);
3022
3023 // store
3024 leftHandExpression = true;
3025 expression.visit(this);
3026 leftHandExpression = false;
3027
3028 //reload saved value
3029 cv.visitVarInsn(ALOAD, tempIdx);
3030 compileStack.removeVar(tempIdx);
3031 }
3032
3033 protected void evaluateInstanceof(BinaryExpression expression) {
3034 visitAndAutoboxBoolean(expression.getLeftExpression());
3035 Expression rightExp = expression.getRightExpression();
3036 ClassNode classType = ClassHelper.DYNAMIC_TYPE;
3037 if (rightExp instanceof ClassExpression) {
3038 ClassExpression classExp = (ClassExpression) rightExp;
3039 classType = classExp.getType();
3040 }
3041 else {
3042 throw new RuntimeException(
3043 "Right hand side of the instanceof keyword must be a class name, not: " + rightExp);
3044 }
3045 String classInternalName = BytecodeHelper.getClassInternalName(classType);
3046 cv.visitTypeInsn(INSTANCEOF, classInternalName);
3047 }
3048
3049 /**
3050 * @return true if the given argument expression requires the stack, in
3051 * which case the arguments are evaluated first, stored in the
3052 * variable stack and then reloaded to make a method call
3053 */
3054 protected boolean argumentsUseStack(Expression arguments) {
3055 return arguments instanceof TupleExpression || arguments instanceof ClosureExpression;
3056 }
3057
3058 /**
3059 * @return true if the given expression represents a non-static field
3060 */
3061 protected boolean isNonStaticField(Expression expression) {
3062 FieldNode field = null;
3063 if (expression instanceof VariableExpression) {
3064 VariableExpression varExp = (VariableExpression) expression;
3065 field = classNode.getField(varExp.getName());
3066 }
3067 else if (expression instanceof FieldExpression) {
3068 FieldExpression fieldExp = (FieldExpression) expression;
3069 field = classNode.getField(fieldExp.getFieldName());
3070 }
3071 else if (expression.getClass()==PropertyExpression.class) {
3072 PropertyExpression fieldExp = (PropertyExpression) expression;
3073 String possibleField = fieldExp.getPropertyAsString();
3074 if (possibleField!=null) field = classNode.getField(possibleField);
3075 }
3076 if (field != null) {
3077 return !field.isStatic();
3078 }
3079 return false;
3080 }
3081
3082 private static boolean isThisExpression(Expression expression) {
3083 if (expression instanceof VariableExpression) {
3084 VariableExpression varExp = (VariableExpression) expression;
3085 return varExp.getName().equals("this");
3086 }
3087 return false;
3088 }
3089
3090 private static boolean isSuperExpression(Expression expression) {
3091 if (expression instanceof VariableExpression) {
3092 VariableExpression varExp = (VariableExpression) expression;
3093 return varExp.getName().equals("super");
3094 }
3095 return false;
3096 }
3097
3098 private static boolean isThisOrSuper(Expression expression) {
3099 return isThisExpression(expression) || isSuperExpression(expression);
3100 }
3101
3102
3103 /**
3104 * For assignment expressions, return a safe expression for the LHS we can use
3105 * to return the value
3106 */
3107 protected Expression createReturnLHSExpression(Expression expression) {
3108 if (expression instanceof BinaryExpression) {
3109 BinaryExpression binExpr = (BinaryExpression) expression;
3110 if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
3111 return createReusableExpression(binExpr.getLeftExpression());
3112 }
3113 }
3114 return null;
3115 }
3116
3117 protected Expression createReusableExpression(Expression expression) {
3118 ExpressionTransformer transformer = new ExpressionTransformer() {
3119 public Expression transform(Expression expression) {
3120 if (expression instanceof PostfixExpression) {
3121 PostfixExpression postfixExp = (PostfixExpression) expression;
3122 return postfixExp.getExpression();
3123 }
3124 else if (expression instanceof PrefixExpression) {
3125 PrefixExpression prefixExp = (PrefixExpression) expression;
3126 return prefixExp.getExpression();
3127 }
3128 return expression;
3129 }
3130 };
3131
3132 // could just be a postfix / prefix expression or nested inside some other expression
3133 return transformer.transform(expression.transformExpression(transformer));
3134 }
3135
3136 protected boolean isComparisonExpression(Expression expression) {
3137 if (expression instanceof BinaryExpression) {
3138 BinaryExpression binExpr = (BinaryExpression) expression;
3139 switch (binExpr.getOperation().getType()) {
3140 case Types.COMPARE_EQUAL :
3141 case Types.MATCH_REGEX :
3142 case Types.COMPARE_GREATER_THAN :
3143 case Types.COMPARE_GREATER_THAN_EQUAL :
3144 case Types.COMPARE_LESS_THAN :
3145 case Types.COMPARE_LESS_THAN_EQUAL :
3146 case Types.COMPARE_IDENTICAL :
3147 case Types.COMPARE_NOT_EQUAL :
3148 case Types.KEYWORD_INSTANCEOF :
3149 case Types.KEYWORD_IN :
3150 return true;
3151 }
3152 }
3153 else if (expression instanceof BooleanExpression) {
3154 return true;
3155 }
3156 return false;
3157 }
3158
3159 protected void onLineNumber(ASTNode statement, String message) {
3160 int line = statement.getLineNumber();
3161 int col = statement.getColumnNumber();
3162 this.currentASTNode = statement;
3163
3164 if (line >=0) {
3165 lineNumber = line;
3166 columnNumber = col;
3167 }
3168 if (CREATE_LINE_NUMBER_INFO && line >= 0 && cv != null) {
3169 Label l = new Label();
3170 cv.visitLabel(l);
3171 cv.visitLineNumber(line, l);
3172 if (ASM_DEBUG) {
3173 helper.mark(message + "[" + statement.getLineNumber() + ":" + statement.getColumnNumber() + "]");
3174 }
3175 }
3176 }
3177
3178 private boolean isInnerClass() {
3179 return classNode instanceof InnerClassNode;
3180 }
3181
3182 /** @return true if the given name is a local variable or a field */
3183 protected boolean isFieldOrVariable(String name) {
3184 return compileStack.containsVariable(name) || classNode.getField(name) != null;
3185 }
3186
3187 /**
3188 * @return if the type of the expression can be determined at compile time
3189 * then this method returns the type - otherwise null
3190 */
3191 protected ClassNode getExpressionType(Expression expression) {
3192 if (isComparisonExpression(expression)) {
3193 return ClassHelper.boolean_TYPE;
3194 }
3195 if (expression instanceof VariableExpression) {
3196 if (expression == VariableExpression.THIS_EXPRESSION) {
3197 return classNode;
3198 }else if (expression==VariableExpression.SUPER_EXPRESSION) {
3199 return classNode.getSuperClass();
3200 }
3201
3202 VariableExpression varExpr = (VariableExpression) expression;
3203 Variable variable = compileStack.getVariable(varExpr.getName(),false);
3204 if (variable != null && !variable.isHolder()) {
3205 ClassNode type = variable.getType();
3206 if (! variable.isDynamicTyped()) return type;
3207 }
3208 if (variable == null) {
3209 org.codehaus.groovy.ast.Variable var = (org.codehaus.groovy.ast.Variable) compileStack.getScope().getReferencedClassVariables().get(varExpr.getName());
3210 if (var!=null && !var.isDynamicTyped()) return var.getType();
3211 }
3212 }
3213 return expression.getType();
3214 }
3215
3216 protected boolean isInClosureConstructor() {
3217 return constructorNode != null
3218 && classNode.getOuterClass() != null
3219 && classNode.getSuperClass()==ClassHelper.CLOSURE_TYPE;
3220 }
3221
3222 protected boolean isInClosure() {
3223 return classNode.getOuterClass() != null
3224 && classNode.getSuperClass()==ClassHelper.CLOSURE_TYPE;
3225 }
3226
3227 protected boolean isStaticMethod() {
3228 if (methodNode == null) { // we're in a constructor
3229 return false;
3230 }
3231 return methodNode.isStatic();
3232 }
3233
3234 protected CompileUnit getCompileUnit() {
3235 CompileUnit answer = classNode.getCompileUnit();
3236 if (answer == null) {
3237 answer = context.getCompileUnit();
3238 }
3239 return answer;
3240 }
3241
3242 protected boolean isHolderVariable(Expression expression) {
3243 if (expression instanceof FieldExpression) {
3244 FieldExpression fieldExp = (FieldExpression) expression;
3245 return fieldExp.getField().isHolder();
3246 }
3247 if (expression instanceof VariableExpression) {
3248 VariableExpression varExp = (VariableExpression) expression;
3249 Variable variable = compileStack.getVariable(varExp.getName(),false);
3250 if (variable != null) {
3251 return variable.isHolder();
3252 }
3253 FieldNode field = classNode.getField(varExp.getName());
3254 if (field != null) {
3255 return field.isHolder();
3256 }
3257 }
3258 return false;
3259 }
3260
3261 public static boolean usesSuper(MethodCallExpression call) {
3262 Expression expression = call.getObjectExpression();
3263 if (expression instanceof VariableExpression) {
3264 VariableExpression varExp = (VariableExpression) expression;
3265 String variable = varExp.getName();
3266 return variable.equals("super");
3267 }
3268 return false;
3269 }
3270
3271 public static boolean usesSuper(PropertyExpression pe) {
3272 Expression expression = pe.getObjectExpression();
3273 if (expression instanceof VariableExpression) {
3274 VariableExpression varExp = (VariableExpression) expression;
3275 String variable = varExp.getName();
3276 return variable.equals("super");
3277 }
3278 return false;
3279 }
3280 }