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