1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.codehaus.groovy.classgen;
47
48 import groovy.lang.GroovyObject;
49 import groovy.lang.GroovyRuntimeException;
50
51 import java.util.ArrayList;
52 import java.util.Collections;
53 import java.util.Comparator;
54 import java.util.HashSet;
55 import java.util.Iterator;
56 import java.util.LinkedList;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Set;
60 import java.util.logging.Logger;
61
62 import org.codehaus.groovy.GroovyBugError;
63 import org.codehaus.groovy.ast.ASTNode;
64 import org.codehaus.groovy.ast.AnnotatedNode;
65 import org.codehaus.groovy.ast.AnnotationNode;
66 import org.codehaus.groovy.ast.ClassHelper;
67 import org.codehaus.groovy.ast.ClassNode;
68 import org.codehaus.groovy.ast.CompileUnit;
69 import org.codehaus.groovy.ast.ConstructorNode;
70 import org.codehaus.groovy.ast.FieldNode;
71 import org.codehaus.groovy.ast.InnerClassNode;
72 import org.codehaus.groovy.ast.MethodNode;
73 import org.codehaus.groovy.ast.Parameter;
74 import org.codehaus.groovy.ast.PropertyNode;
75 import org.codehaus.groovy.ast.VariableScope;
76 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
77 import org.codehaus.groovy.ast.expr.ArrayExpression;
78 import org.codehaus.groovy.ast.expr.AttributeExpression;
79 import org.codehaus.groovy.ast.expr.BinaryExpression;
80 import org.codehaus.groovy.ast.expr.BitwiseNegExpression;
81 import org.codehaus.groovy.ast.expr.BooleanExpression;
82 import org.codehaus.groovy.ast.expr.CastExpression;
83 import org.codehaus.groovy.ast.expr.ClassExpression;
84 import org.codehaus.groovy.ast.expr.ClosureExpression;
85 import org.codehaus.groovy.ast.expr.ConstantExpression;
86 import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
87 import org.codehaus.groovy.ast.expr.DeclarationExpression;
88 import org.codehaus.groovy.ast.expr.Expression;
89 import org.codehaus.groovy.ast.expr.ExpressionTransformer;
90 import org.codehaus.groovy.ast.expr.FieldExpression;
91 import org.codehaus.groovy.ast.expr.GStringExpression;
92 import org.codehaus.groovy.ast.expr.ListExpression;
93 import org.codehaus.groovy.ast.expr.MapEntryExpression;
94 import org.codehaus.groovy.ast.expr.MapExpression;
95 import org.codehaus.groovy.ast.expr.MethodCallExpression;
96 import org.codehaus.groovy.ast.expr.MethodPointerExpression;
97 import org.codehaus.groovy.ast.expr.NegationExpression;
98 import org.codehaus.groovy.ast.expr.NotExpression;
99 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
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
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
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
205 MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
206 MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
207
208 MethodCaller assertFailedMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "assertFailed");
209
210 MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");
211
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
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
225 MethodCaller spreadMap = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "spreadMap");
226 MethodCaller despreadList = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "despreadList");
227
228 MethodCaller getMethodPointer = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getMethodPointer");
229 MethodCaller invokeClosureMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeClosure");
230
231 MethodCaller negation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "negate");
232 MethodCaller bitNegation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "bitNegate");
233
234
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
243 MethodCaller createPojoWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createPojoWrapper");
244 MethodCaller createGroovyObjectWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createGroovyObjectWrapper");
245
246
247 MethodCaller selectConstructorAndTransformArguments = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "selectConstructorAndTransformArguments");
248
249
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;
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
295
296 public void visitClass(ClassNode classNode) {
297
298
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;
344 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
345 if (enclosingMethod != null) {
346
347 outerClassName = null;
348 innerClassName = null;
349 }
350 cw.visitInnerClass(
351 innerClassInternalName,
352 outerClassName,
353 innerClassName,
354 innerClass.getModifiers());
355 }
356
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
397
398
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
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
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
500 (new ClassExpression(classNode)).visit(this);
501 cv.visitInsn(POP);
502 (new ClassExpression(ClassHelper.METACLASS_TYPE)).visit(this);
503 cv.visitInsn(POP);
504
505
506 super.visitConstructorOrMethod(node, isConstructor);
507 if (!outputReturn || node.isVoidMethod()) {
508 cv.visitInsn(RETURN);
509 }
510 compileStack.clear();
511
512
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,
556 null);
557 visitAnnotations(fieldNode);
558 }
559
560 public void visitProperty(PropertyNode statement) {
561
562
563 onLineNumber(statement, "visitProperty:" + statement.getField().getName());
564 this.methodNode = null;
565 }
566
567
568
569
570
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
598 Variable variable = compileStack.defineVariable(loop.getVariable(),false);
599
600
601
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
614 cv.visitJumpInsn(IFEQ, breakLabel);
615
616 cv.visitVarInsn(ALOAD, iteratorIdx);
617 iteratorNextMethod.call(cv);
618 helper.storeVar(variable);
619
620
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
713
714 Label l1 = new Label();
715 cv.visitJumpInsn(GOTO, l1);
716 cv.visitLabel(l0);
717
718
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
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
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
810 final Label tryStart = new Label();
811 cv.visitLabel(tryStart);
812 tryStatement.visit(this);
813
814 final Label finallyStart = new Label();
815 cv.visitJumpInsn(GOTO, finallyStart);
816
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
824 final Label catchStart = new Label();
825 cv.visitLabel(catchStart);
826
827 compileStack.defineVariable(catchStatement.getVariable(),true);
828
829 catchStatement.visit(this);
830
831 cv.visitJumpInsn(GOTO, finallyStart);
832
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
842 final Label endOfAllCatches = new Label();
843 cv.visitLabel(endOfAllCatches);
844
845
846 if (!finallyStatement.isEmpty()) compileStack.popFinallyBlock();
847
848
849 cv.visitLabel(finallyStart);
850 finallyStatement.visit(this);
851
852 Label afterFinally = new Label();
853 cv.visitJumpInsn(GOTO, afterFinally);
854
855
856 final Label catchAny = new Label();
857 cv.visitLabel(catchAny);
858
859 cv.visitVarInsn(ASTORE, anyExceptionIndex);
860 finallyStatement.visit(this);
861
862 cv.visitVarInsn(ALOAD, anyExceptionIndex);
863 cv.visitInsn(ATHROW);
864
865
866 cv.visitLabel(afterFinally);
867
868
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
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
931
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
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);
1031 } else {
1032
1033
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
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
1087
1088
1089 public void visitDeclarationExpression(DeclarationExpression expression) {
1090 onLineNumber(expression, "visitDeclarationExpression: \""+expression.getVariableExpression().getName()+"\"");
1091
1092 Expression rightExpression = expression.getRightExpression();
1093
1094 VariableExpression vex = expression.getVariableExpression();
1095 ClassNode type = vex.getType();
1096
1097
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 :
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
1211
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
1279
1280
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
1300
1301
1302
1303 visitAndAutoboxBoolean(exp);
1304
1305
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 :
1