001 /* 002 * $Id: AsmClassGenerator.java,v 1.40 2005/07/16 20:00:26 phk Exp $ 003 * 004 * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 * 006 * Redistribution and use of this software and associated documentation 007 * ("Software"), with or without modification, are permitted provided that the 008 * following conditions are met: 1. Redistributions of source code must retain 009 * copyright statements and notices. Redistributions must also contain a copy 010 * of this document. 2. Redistributions in binary form must reproduce the above 011 * copyright notice, this list of conditions and the following disclaimer in 012 * the documentation and/or other materials provided with the distribution. 3. 013 * The name "groovy" must not be used to endorse or promote products derived 014 * from this Software without prior written permission of The Codehaus. For 015 * written permission, please contact info@codehaus.org. 4. Products derived 016 * from this Software may not be called "groovy" nor may "groovy" appear in 017 * their names without prior written permission of The Codehaus. "groovy" is a 018 * registered trademark of The Codehaus. 5. Due credit should be given to The 019 * Codehaus - http://groovy.codehaus.org/ 020 * 021 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 022 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 024 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 025 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 026 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 027 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 028 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 029 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 030 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 031 * DAMAGE. 032 * 033 */ 034 package org.codehaus.groovy.classgen; 035 036 import groovy.lang.*; 037 038 import java.lang.reflect.Constructor; 039 import java.lang.reflect.Field; 040 import java.lang.reflect.Method; 041 import java.lang.reflect.Modifier; 042 import java.util.*; 043 import java.util.regex.Matcher; 044 import java.util.logging.Logger; 045 import java.security.AccessController; 046 import java.security.PrivilegedAction; 047 048 import org.codehaus.groovy.ast.ASTNode; 049 import org.codehaus.groovy.ast.ClassNode; 050 import org.codehaus.groovy.ast.CompileUnit; 051 import org.codehaus.groovy.ast.ConstructorNode; 052 import org.codehaus.groovy.ast.FieldNode; 053 import org.codehaus.groovy.ast.GroovyCodeVisitor; 054 import org.codehaus.groovy.ast.InnerClassNode; 055 import org.codehaus.groovy.ast.MethodNode; 056 import org.codehaus.groovy.ast.Parameter; 057 import org.codehaus.groovy.ast.PropertyNode; 058 import org.codehaus.groovy.ast.Type; 059 import org.codehaus.groovy.ast.VariableScope; 060 import org.codehaus.groovy.ast.expr.*; 061 import org.codehaus.groovy.ast.stmt.AssertStatement; 062 import org.codehaus.groovy.ast.stmt.BlockStatement; 063 import org.codehaus.groovy.ast.stmt.BreakStatement; 064 import org.codehaus.groovy.ast.stmt.CaseStatement; 065 import org.codehaus.groovy.ast.stmt.CatchStatement; 066 import org.codehaus.groovy.ast.stmt.ContinueStatement; 067 import org.codehaus.groovy.ast.stmt.DoWhileStatement; 068 import org.codehaus.groovy.ast.stmt.ExpressionStatement; 069 import org.codehaus.groovy.ast.stmt.ForStatement; 070 import org.codehaus.groovy.ast.stmt.IfStatement; 071 import org.codehaus.groovy.ast.stmt.ReturnStatement; 072 import org.codehaus.groovy.ast.stmt.Statement; 073 import org.codehaus.groovy.ast.stmt.SwitchStatement; 074 import org.codehaus.groovy.ast.stmt.SynchronizedStatement; 075 import org.codehaus.groovy.ast.stmt.ThrowStatement; 076 import org.codehaus.groovy.ast.stmt.TryCatchStatement; 077 import org.codehaus.groovy.ast.stmt.WhileStatement; 078 import org.codehaus.groovy.runtime.DefaultGroovyMethods; 079 import org.codehaus.groovy.runtime.RegexSupport; 080 import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; 081 import org.codehaus.groovy.syntax.Token; 082 import org.codehaus.groovy.syntax.Types; 083 import org.codehaus.groovy.syntax.RuntimeParserException; 084 import org.objectweb.asm.ClassVisitor; 085 import org.objectweb.asm.MethodVisitor; 086 import org.objectweb.asm.Label; 087 import org.objectweb.asm.ClassWriter; 088 089 /** 090 * Generates Java class versions of Groovy classes using ASM. 091 * 092 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 093 * @author <a href="mailto:b55r@sina.com">Bing Ran</a> 094 * 095 * @version $Revision: 1.40 $ 096 */ 097 public class AsmClassGenerator extends ClassGenerator { 098 099 private Logger log = Logger.getLogger(getClass().getName()); 100 101 private ClassVisitor cw; 102 private MethodVisitor cv; 103 private GeneratorContext context; 104 105 private String sourceFile; 106 107 // current class details 108 private ClassNode classNode; 109 private ClassNode outermostClass; 110 private String internalClassName; 111 private String internalBaseClassName; 112 113 /** maps the variable names to the JVM indices */ 114 private Map variableStack = new HashMap(); 115 116 /** have we output a return statement yet */ 117 private boolean outputReturn; 118 119 /** are we on the left or right of an expression */ 120 private boolean leftHandExpression; 121 122 // cached values 123 MethodCaller invokeMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethod"); 124 MethodCaller invokeMethodSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethodSafe"); 125 MethodCaller invokeMethodSpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeMethodSpreadSafe"); 126 MethodCaller invokeStaticMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeStaticMethod"); 127 MethodCaller invokeConstructorMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeConstructor"); 128 MethodCaller invokeConstructorOfMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeConstructorOf"); 129 MethodCaller invokeNoArgumentsConstructorOf = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsConstructorOf"); 130 MethodCaller invokeClosureMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeClosure"); 131 MethodCaller invokeSuperMethodMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeSuperMethod"); 132 MethodCaller invokeNoArgumentsMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsMethod"); 133 MethodCaller invokeNoArgumentsSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsSafeMethod"); 134 MethodCaller invokeNoArgumentsSpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeNoArgumentsSpreadSafeMethod"); 135 MethodCaller invokeStaticNoArgumentsMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeStaticNoArgumentsMethod"); 136 137 MethodCaller asIntMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asInt"); 138 MethodCaller asTypeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asType"); 139 140 MethodCaller getAttributeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttribute"); 141 MethodCaller getAttributeSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttributeSafe"); 142 MethodCaller getAttributeSpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getAttributeSpreadSafe"); 143 MethodCaller setAttributeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setAttribute2"); 144 MethodCaller setAttributeSafeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setAttributeSafe2"); 145 146 MethodCaller getPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getProperty"); 147 MethodCaller getPropertySafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getPropertySafe"); 148 MethodCaller getPropertySpreadSafeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getPropertySpreadSafe"); 149 MethodCaller setPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setProperty"); 150 MethodCaller setPropertyMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setProperty2"); 151 MethodCaller setPropertySafeMethod2 = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setPropertySafe2"); 152 153 MethodCaller getGroovyObjectPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getGroovyObjectProperty"); 154 MethodCaller setGroovyObjectPropertyMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "setGroovyObjectProperty"); 155 MethodCaller asIteratorMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asIterator"); 156 MethodCaller asBool = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asBool"); 157 MethodCaller notBoolean = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "notBoolean"); 158 MethodCaller notObject = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "notObject"); 159 MethodCaller regexPattern = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "regexPattern"); 160 MethodCaller spreadList = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "spreadList"); 161 MethodCaller spreadMap = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "spreadMap"); 162 MethodCaller getMethodPointer = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getMethodPointer"); 163 MethodCaller negation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "negate"); 164 MethodCaller bitNegation = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "bitNegate"); 165 MethodCaller convertPrimitiveArray = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "convertPrimitiveArray"); 166 MethodCaller convertToPrimitiveArray = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "convertToPrimitiveArray"); 167 168 MethodCaller compareIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareIdentical"); 169 MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual"); 170 MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual"); 171 MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo"); 172 MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex"); 173 MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex"); 174 MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan"); 175 MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual"); 176 MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan"); 177 MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual"); 178 MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase"); 179 180 MethodCaller createListMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createList"); 181 MethodCaller createTupleMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createTuple"); 182 MethodCaller createMapMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createMap"); 183 MethodCaller createRangeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createRange"); 184 185 MethodCaller assertFailedMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "assertFailed"); 186 187 MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next"); 188 MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext"); 189 190 191 // current stack index 192 private int lastVariableIndex; 193 private static int tempVariableNameCounter; 194 195 // exception blocks list 196 private List exceptionBlocks = new ArrayList(); 197 198 private boolean definingParameters; 199 private Set syntheticStaticFields = new HashSet(); 200 private Set mutableVars = new HashSet(); 201 private boolean passingClosureParams; 202 203 private ConstructorNode constructorNode; 204 private MethodNode methodNode; 205 //private PropertyNode propertyNode; 206 private BlockScope scope; 207 private BytecodeHelper helper = new BytecodeHelper(null); 208 209 private VariableScope variableScope; 210 public static final boolean CREATE_DEBUG_INFO = false; 211 public static final boolean CREATE_LINE_NUMBER_INFO = true; 212 private static final boolean MARK_START = true; 213 214 public static final String EB_SWITCH_NAME = "static.dispatching"; 215 public boolean ENABLE_EARLY_BINDING; 216 { // 217 String ebSwitch = (String) AccessController.doPrivileged(new PrivilegedAction() { 218 public Object run() { 219 return System.getProperty(EB_SWITCH_NAME, "false"); // set default to true if early binding is on by default. 220 } 221 }); 222 //System.out.println("ebSwitch = " + ebSwitch); 223 if (ebSwitch.equals("true")) { 224 ENABLE_EARLY_BINDING = true; 225 } 226 else if (ebSwitch.equals("false")) { 227 ENABLE_EARLY_BINDING = false; 228 } 229 else { 230 ENABLE_EARLY_BINDING = false; 231 log.warning("The value of system property " + EB_SWITCH_NAME + " is not recognized. Late dispatching is assumed. "); 232 } 233 } 234 public static final boolean ASM_DEBUG = false; // add marker in the bytecode to show source-byecode relationship 235 private int lineNumber = -1; 236 private int columnNumber = -1; 237 private ASTNode currentASTNode = null; 238 239 private DummyClassGenerator dummyGen = null; 240 private ClassWriter dummyClassWriter = null; 241 242 public AsmClassGenerator( 243 GeneratorContext context, 244 ClassVisitor classVisitor, 245 ClassLoader classLoader, 246 String sourceFile) { 247 super(classLoader); 248 this.context = context; 249 this.cw = classVisitor; 250 this.sourceFile = sourceFile; 251 252 this.dummyClassWriter = new ClassWriter(true); 253 dummyGen = new DummyClassGenerator(context, dummyClassWriter, classLoader, sourceFile); 254 255 } 256 257 // GroovyClassVisitor interface 258 //------------------------------------------------------------------------- 259 public void visitClass(ClassNode classNode) { 260 // todo to be tested 261 // createDummyClass(classNode); 262 263 try { 264 syntheticStaticFields.clear(); 265 this.classNode = classNode; 266 this.outermostClass = null; 267 this.internalClassName = BytecodeHelper.getClassInternalName(classNode.getName()); 268 269 //System.out.println("Generating class: " + classNode.getName()); 270 271 // lets check that the classes are all valid 272 classNode.setSuperClass(checkValidType(classNode.getSuperClass(), classNode, "Must be a valid base class")); 273 String[] interfaces = classNode.getInterfaces(); 274 for (int i = 0; i < interfaces.length; i++ ) { 275 interfaces[i] = checkValidType(interfaces[i], classNode, "Must be a valid interface name"); 276 } 277 278 this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass()); 279 280 cw.visit( 281 asmJDKVersion, 282 classNode.getModifiers(), 283 internalClassName, 284 null, 285 internalBaseClassName, 286 BytecodeHelper.getClassInternalNames(classNode.getInterfaces()) 287 ); 288 289 // set the optional enclosing method attribute of the current inner class 290 // br comment out once Groovy uses the latest CVS HEAD of ASM 291 // MethodNode enclosingMethod = classNode.getEnclosingMethod(); 292 // String ownerName = BytecodeHelper.getClassInternalName(enclosingMethod.getDeclaringClass().getName()); 293 // String descriptor = BytecodeHelper.getMethodDescriptor(enclosingMethod.getReturnType(), enclosingMethod.getParameters()); 294 // EnclosingMethodAttribute attr = new EnclosingMethodAttribute(ownerName,enclosingMethod.getName(),descriptor); 295 // cw.visitAttribute(attr); 296 297 classNode.visitContents(this); 298 299 createSyntheticStaticFields(); 300 301 for (Iterator iter = innerClasses.iterator(); iter.hasNext();) { 302 ClassNode innerClass = (ClassNode) iter.next(); 303 String innerClassName = innerClass.getName(); 304 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName); 305 { 306 int index = innerClassName.lastIndexOf('$'); 307 if (index>=0) innerClassName = innerClassName.substring(index+1); 308 } 309 String outerClassName = internalClassName; // default for inner classes 310 MethodNode enclosingMethod = innerClass.getEnclosingMethod(); 311 if (enclosingMethod != null) { 312 // local inner classes do not specify the outer class name 313 outerClassName = null; 314 innerClassName = null; 315 } 316 cw.visitInnerClass( 317 innerClassInternalName, 318 outerClassName, 319 innerClassName, 320 innerClass.getModifiers()); 321 } 322 // br TODO an inner class should have an entry of itself 323 cw.visitEnd(); 324 } 325 catch (GroovyRuntimeException e) { 326 e.setModule(classNode.getModule()); 327 throw e; 328 } 329 } 330 331 // create a surrogate class that represents the classNode 332 // the surrogate has the "face" of the real class. It's used for 333 // type resolving "this" 334 private void createDummyClass(ClassNode classNode) { 335 dummyGen.visitClass(classNode); 336 byte[] code = dummyClassWriter.toByteArray(); 337 338 ClassLoader parentLoader = getClass().getClassLoader(); 339 GroovyClassLoader groovyLoader = new GroovyClassLoader(parentLoader); 340 Class theClass = groovyLoader.defineClass(classNode.getName(), code); 341 342 if (theClass != null) { 343 classCache.put(classNode.getName(), theClass); 344 } 345 } 346 347 public void visitConstructor(ConstructorNode node) { 348 // creates a MethodWriter for the (implicit) constructor 349 //String methodType = Type.getMethodDescriptor(VOID_TYPE, ) 350 351 this.constructorNode = node; 352 this.methodNode = null; 353 this.variableScope = null; 354 355 visitParameters(node, node.getParameters()); 356 357 String methodType = BytecodeHelper.getMethodDescriptor("void", node.getParameters()); 358 cv = cw.visitMethod(node.getModifiers(), "<init>", methodType, null, null); 359 helper = new BytecodeHelper(cv); 360 361 findMutableVariables(); 362 resetVariableStack(node.getParameters()); 363 364 Statement code = node.getCode(); 365 if (code == null || !firstStatementIsSuperInit(code)) { 366 // invokes the super class constructor 367 cv.visitVarInsn(ALOAD, 0); 368 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "()V"); 369 } 370 if (code != null) { 371 code.visit(this); 372 } 373 374 cv.visitInsn(RETURN); 375 cv.visitMaxs(0, 0); 376 } 377 378 public void visitMethod(MethodNode node) { 379 //System.out.println("Visiting method: " + node.getName() + " with 380 // return type: " + node.getReturnType()); 381 this.constructorNode = null; 382 this.methodNode = node; 383 this.variableScope = null; 384 385 visitParameters(node, node.getParameters()); 386 node.setReturnType(checkValidType(node.getReturnType(), node, "Must be a valid return type")); 387 388 String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters()); 389 cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, null); 390 if (node.getCode()!=null) { 391 Label labelStart = new Label(); 392 cv.visitLabel(labelStart); 393 helper = new BytecodeHelper(cv); 394 395 findMutableVariables(); 396 resetVariableStack(node.getParameters()); 397 398 399 outputReturn = false; 400 401 node.getCode().visit(this); 402 403 if (!outputReturn) { 404 cv.visitInsn(RETURN); 405 } 406 407 // lets do all the exception blocks 408 for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) { 409 Runnable runnable = (Runnable) iter.next(); 410 runnable.run(); 411 } 412 exceptionBlocks.clear(); 413 414 Label labelEnd = new Label(); 415 cv.visitLabel(labelEnd); 416 417 // br experiment with local var table so debuggers can retrieve variable names 418 if (CREATE_DEBUG_INFO) { 419 Set vars = this.variableStack.keySet(); 420 for (Iterator iterator = vars.iterator(); iterator.hasNext();) { 421 String varName = (String) iterator.next(); 422 Variable v = (Variable)variableStack.get(varName); 423 String type = v.getTypeName(); 424 type = BytecodeHelper.getTypeDescription(type); 425 Label start = v.getStartLabel() != null ? v.getStartLabel() : labelStart; 426 Label end = v.getEndLabel() != null ? v.getEndLabel() : labelEnd; 427 cv.visitLocalVariable(varName, type, null, start, end, v.getIndex()); 428 } 429 } 430 cv.visitMaxs(0, 0); 431 } 432 } 433 434 protected void visitParameters(ASTNode node, Parameter[] parameters) { 435 for (int i = 0, size = parameters.length; i < size; i++ ) { 436 visitParameter(node, parameters[i]); 437 } 438 } 439 440 protected void visitParameter(ASTNode node, Parameter parameter) { 441 if (! parameter.isDynamicType()) { 442 parameter.setType(checkValidType(parameter.getType(), node, "Must be a valid parameter class")); 443 } 444 } 445 446 public void visitField(FieldNode fieldNode) { 447 onLineNumber(fieldNode, "visitField: " + fieldNode.getName()); 448 449 // lets check that the classes are all valid 450 fieldNode.setType(checkValidType(fieldNode.getType(), fieldNode, "Must be a valid field class for field: " + fieldNode.getName())); 451 452 //System.out.println("Visiting field: " + fieldNode.getName() + " on 453 // class: " + classNode.getName()); 454 455 Object fieldValue = null; 456 Expression expression = fieldNode.getInitialValueExpression(); 457 if (expression instanceof ConstantExpression) { 458 ConstantExpression constantExp = (ConstantExpression) expression; 459 Object value = constantExp.getValue(); 460 if (isPrimitiveFieldType(fieldNode.getType())) { 461 // lets convert any primitive types 462 Class type = null; 463 try { 464 type = loadClass(fieldNode.getType()); 465 fieldValue = /*ScriptBytecodeAdapter.*/asType(value, type); 466 } 467 catch (Exception e) { 468 log.warning("Caught unexpected: " + e); 469 } 470 } 471 } 472 cw.visitField( 473 fieldNode.getModifiers(), 474 fieldNode.getName(), 475 BytecodeHelper.getTypeDescription(fieldNode.getType()), 476 null, //fieldValue, //br all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer. 477 null); 478 } 479 480 /** 481 * Creates a getter, setter and field 482 */ 483 public void visitProperty(PropertyNode statement) { 484 onLineNumber(statement, "visitProperty:" + statement.getField().getName()); 485 //this.propertyNode = statement; 486 this.methodNode = null; 487 } 488 489 // GroovyCodeVisitor interface 490 //------------------------------------------------------------------------- 491 492 // Statements 493 //------------------------------------------------------------------------- 494 495 public void visitForLoop(ForStatement loop) { 496 onLineNumber(loop, "visitForLoop"); 497 Class elemType = null; 498 if (ENABLE_EARLY_BINDING) { 499 Expression collectionExp = loop.getCollectionExpression(); 500 collectionExp.resolve(this); 501 Class cls = collectionExp.getTypeClass(); 502 if (cls != null) { 503 if (cls.isArray()) { 504 elemType = cls.getComponentType(); 505 if (elemType != null) { 506 Type varType = new Type(elemType.getName()); 507 loop.setVariableType(varType); 508 } 509 } 510 else if (collectionExp instanceof ListExpression) { 511 elemType = ((ListExpression)collectionExp).getComponentTypeClass(); 512 if (elemType != null) { 513 Type varType = new Type(elemType.getName()); 514 loop.setVariableType(varType); 515 } 516 } 517 else if (collectionExp instanceof RangeExpression) { 518 // use the from type class. assuming both from and to are of the same type 519 elemType = ((RangeExpression)collectionExp).getFrom().getTypeClass(); 520 if (elemType != null) { 521 Type varType = new Type(elemType.getName()); 522 loop.setVariableType(varType); 523 } 524 } 525 } 526 } 527 528 529 // 530 // Declare the loop counter. 531 Type variableType = checkValidType(loop.getVariableType(), loop, "for loop variable"); 532 Variable variable = defineVariable(loop.getVariable(), variableType, true); 533 534 if( isInScriptBody() ) { 535 variable.setProperty( true ); 536 } 537 538 539 // 540 // Then initialize the iterator and generate the loop control 541 542 loop.getCollectionExpression().visit(this); 543 544 asIteratorMethod.call(cv); 545 546 final Variable iterTemp = storeInTemp("iterator", "java.util.Iterator"); 547 final int iteratorIdx = iterTemp.getIndex(); 548 549 // to push scope here allows the iterator available after the loop, such as the i in: for (i in 1..5) 550 // move it to the top will make the iterator a local var in the for loop. 551 pushBlockScope(); 552 553 Label continueLabel = scope.getContinueLabel(); 554 cv.visitJumpInsn(GOTO, continueLabel); 555 Label label2 = new Label(); 556 cv.visitLabel(label2); 557 558 final Class elemClass = elemType; 559 BytecodeExpression expression = new BytecodeExpression() { 560 public void visit(GroovyCodeVisitor visitor) { 561 cv.visitVarInsn(ALOAD, iteratorIdx); 562 iteratorNextMethod.call(cv); 563 } 564 565 protected void resolveType(AsmClassGenerator resolver) { 566 setTypeClass(elemClass); 567 } 568 }; 569 570 evaluateEqual( BinaryExpression.newAssignmentExpression(loop.getVariable(), expression) ); 571 cv.visitInsn(POP); // br now the evaluateEqual() will leave a value on the stack. pop it. 572 573 // 574 // Generate the loop body 575 576 loop.getLoopBlock().visit(this); 577 578 579 // 580 // Generate the loop tail 581 582 cv.visitLabel(continueLabel); 583 cv.visitVarInsn(ALOAD, iteratorIdx); 584 585 iteratorHasNextMethod.call(cv); 586 587 cv.visitJumpInsn(IFNE, label2); 588 589 cv.visitLabel(scope.getBreakLabel()); 590 popScope(); 591 } 592 593 public void visitWhileLoop(WhileStatement loop) { 594 onLineNumber(loop, "visitWhileLoop"); 595 596 pushBlockScope(); 597 598 Label continueLabel = scope.getContinueLabel(); 599 600 cv.visitJumpInsn(GOTO, continueLabel); 601 Label l1 = new Label(); 602 cv.visitLabel(l1); 603 604 loop.getLoopBlock().visit(this); 605 606 cv.visitLabel(continueLabel); 607 608 loop.getBooleanExpression().visit(this); 609 610 cv.visitJumpInsn(IFNE, l1); 611 612 cv.visitLabel(scope.getBreakLabel()); 613 popScope(); 614 } 615 616 public void visitDoWhileLoop(DoWhileStatement loop) { 617 onLineNumber(loop, "visitDoWhileLoop"); 618 619 pushBlockScope(); 620 621 Label breakLabel = scope.getBreakLabel(); 622 623 Label continueLabel = scope.getContinueLabel(); 624 cv.visitLabel(continueLabel); 625 Label l1 = new Label(); 626 627 loop.getLoopBlock().visit(this); 628 629 cv.visitLabel(l1); 630 631 loop.getBooleanExpression().visit(this); 632 633 cv.visitJumpInsn(IFNE, continueLabel); 634 635 cv.visitLabel(breakLabel); 636 popScope(); 637 } 638 639 public void visitIfElse(IfStatement ifElse) { 640 onLineNumber(ifElse, "visitIfElse"); 641 642 ifElse.getBooleanExpression().visit(this); 643 644 Label l0 = new Label(); 645 cv.visitJumpInsn(IFEQ, l0); 646 pushBlockScope(false, false); 647 ifElse.getIfBlock().visit(this); 648 popScope(); 649 650 Label l1 = new Label(); 651 cv.visitJumpInsn(GOTO, l1); 652 cv.visitLabel(l0); 653 654 pushBlockScope(false, false); 655 ifElse.getElseBlock().visit(this); 656 cv.visitLabel(l1); 657 popScope(); 658 } 659 660 public void visitTernaryExpression(TernaryExpression expression) { 661 onLineNumber(expression, "visitTernaryExpression"); 662 663 expression.getBooleanExpression().visit(this); 664 665 Label l0 = new Label(); 666 cv.visitJumpInsn(IFEQ, l0); 667 expression.getTrueExpression().visit(this); 668 669 Label l1 = new Label(); 670 cv.visitJumpInsn(GOTO, l1); 671 cv.visitLabel(l0); 672 673 expression.getFalseExpression().visit(this); 674 cv.visitLabel(l1); 675 } 676 677 public void visitAssertStatement(AssertStatement statement) { 678 onLineNumber(statement, "visitAssertStatement"); 679 680 //System.out.println("Assert: " + statement.getLineNumber() + " for: " 681 // + statement.getText()); 682 683 BooleanExpression booleanExpression = statement.getBooleanExpression(); 684 booleanExpression.visit(this); 685 686 Label l0 = new Label(); 687 cv.visitJumpInsn(IFEQ, l0); 688 689 // do nothing 690 691 Label l1 = new Label(); 692 cv.visitJumpInsn(GOTO, l1); 693 cv.visitLabel(l0); 694 695 // push expression string onto stack 696 String expressionText = booleanExpression.getText(); 697 List list = new ArrayList(); 698 addVariableNames(booleanExpression, list); 699 if (list.isEmpty()) { 700 cv.visitLdcInsn(expressionText); 701 } 702 else { 703 boolean first = true; 704 705 // lets create a new expression 706 cv.visitTypeInsn(NEW, "java/lang/StringBuffer"); 707 cv.visitInsn(DUP); 708 cv.visitLdcInsn(expressionText + ". Values: "); 709 710 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V"); 711 712 Variable assertTemp = visitASTOREInTemp("assert"); 713 int tempIndex = assertTemp.getIndex(); 714 715 for (Iterator iter = list.iterator(); iter.hasNext();) { 716 String name = (String) iter.next(); 717 String text = name + " = "; 718 if (first) { 719 first = false; 720 } 721 else { 722 text = ", " + text; 723 } 724 725 cv.visitVarInsn(ALOAD, tempIndex); 726 cv.visitLdcInsn(text); 727 cv.visitMethodInsn( 728 INVOKEVIRTUAL, 729 "java/lang/StringBuffer", 730 "append", 731 "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); 732 cv.visitInsn(POP); 733 734 cv.visitVarInsn(ALOAD, tempIndex); 735 new VariableExpression(name).visit(this); 736 cv.visitMethodInsn( 737 INVOKEVIRTUAL, 738 "java/lang/StringBuffer", 739 "append", 740 "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); 741 cv.visitInsn(POP); 742 743 } 744 cv.visitVarInsn(ALOAD, tempIndex); 745 removeVar(assertTemp); 746 } 747 // now the optional exception expression 748 statement.getMessageExpression().visit(this); 749 750 assertFailedMethod.call(cv); 751 cv.visitLabel(l1); 752 } 753 754 private void addVariableNames(Expression expression, List list) { 755 if (expression instanceof BooleanExpression) { 756 BooleanExpression boolExp = (BooleanExpression) expression; 757 addVariableNames(boolExp.getExpression(), list); 758 } 759 else if (expression instanceof BinaryExpression) { 760 BinaryExpression binExp = (BinaryExpression) expression; 761 addVariableNames(binExp.getLeftExpression(), list); 762 addVariableNames(binExp.getRightExpression(), list); 763 } 764 else if (expression instanceof VariableExpression) { 765 VariableExpression varExp = (VariableExpression) expression; 766 list.add(varExp.getVariable()); 767 } 768 } 769 770 public void visitTryCatchFinally(TryCatchStatement statement) { 771 onLineNumber(statement, "visitTryCatchFinally"); 772 // todo need to add blockscope handling 773 CatchStatement catchStatement = statement.getCatchStatement(0); 774 775 Statement tryStatement = statement.getTryStatement(); 776 777 if (tryStatement.isEmpty() || catchStatement == null) { 778 final Label l0 = new Label(); 779 cv.visitLabel(l0); 780 781 tryStatement.visit(this); 782 783 784 int index1 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex(); 785 int index2 = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex(); 786 787 final Label l1 = new Label(); 788 cv.visitJumpInsn(JSR, l1); 789 final Label l2 = new Label(); 790 cv.visitLabel(l2); 791 final Label l3 = new Label(); 792 cv.visitJumpInsn(GOTO, l3); 793 final Label l4 = new Label(); 794 cv.visitLabel(l4); 795 cv.visitVarInsn(ASTORE, index1); 796 cv.visitJumpInsn(JSR, l1); 797 final Label l5 = new Label(); 798 cv.visitLabel(l5); 799 cv.visitVarInsn(ALOAD, index1); 800 cv.visitInsn(ATHROW); 801 cv.visitLabel(l1); 802 cv.visitVarInsn(ASTORE, index2); 803 804 statement.getFinallyStatement().visit(this); 805 806 cv.visitVarInsn(RET, index2); 807 cv.visitLabel(l3); 808 809 exceptionBlocks.add(new Runnable() { 810 public void run() { 811 cv.visitTryCatchBlock(l0, l2, l4, null); 812 cv.visitTryCatchBlock(l4, l5, l4, null); 813 } 814 }); 815 816 } 817 else { 818 int finallySubAddress = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex(); 819 int anyExceptionIndex = defineVariable(this.createVariableName("exception"), "java.lang.Object").getIndex(); 820 821 // start try block, label needed for exception table 822 final Label tryStart = new Label(); 823 cv.visitLabel(tryStart); 824 tryStatement.visit(this); 825 // goto finally part 826 final Label finallyStart = new Label(); 827 cv.visitJumpInsn(GOTO, finallyStart); 828 // marker needed for Exception table 829 final Label tryEnd = new Label(); 830 cv.visitLabel(tryEnd); 831 832 for (Iterator it=statement.getCatchStatements().iterator(); it.hasNext();) { 833 catchStatement = (CatchStatement) it.next(); 834 String exceptionType = 835 checkValidType(catchStatement.getExceptionType(), catchStatement, "in catch statement"); 836 int exceptionIndex = defineVariable(catchStatement.getVariable(), exceptionType, false).getIndex(); 837 838 // start catch block, label needed for exception table 839 final Label catchStart = new Label(); 840 cv.visitLabel(catchStart); 841 // store the exception 842 cv.visitVarInsn(ASTORE, exceptionIndex); 843 catchStatement.visit(this); 844 // goto finally start 845 cv.visitJumpInsn(GOTO, finallyStart); 846 // add exception to table 847 final String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType); 848 exceptionBlocks.add(new Runnable() { 849 public void run() { 850 cv.visitTryCatchBlock(tryStart, tryEnd, catchStart, exceptionTypeInternalName); 851 } 852 }); 853 } 854 855 // marker needed for the exception table 856 final Label endOfAllCatches = new Label(); 857 cv.visitLabel(endOfAllCatches); 858 859 // start finally 860 cv.visitLabel(finallyStart); 861 Label finallySub = new Label(); 862 // run finally sub 863 cv.visitJumpInsn(JSR, finallySub); 864 // goto end of finally 865 Label afterFinally = new Label(); 866 cv.visitJumpInsn(GOTO, afterFinally); 867 868 // start a block catching any Exception 869 final Label catchAny = new Label(); 870 cv.visitLabel(catchAny); 871 //store exception 872 cv.visitVarInsn(ASTORE, anyExceptionIndex); 873 // run finally subroutine 874 cv.visitJumpInsn(JSR, finallySub); 875 // load the exception and rethrow it 876 cv.visitVarInsn(ALOAD, anyExceptionIndex); 877 cv.visitInsn(ATHROW); 878 879 // start the finally subroutine 880 cv.visitLabel(finallySub); 881 // store jump address 882 cv.visitVarInsn(ASTORE, finallySubAddress); 883 if (!statement.getFinallyStatement().isEmpty()) 884 statement.getFinallyStatement().visit(this); 885 // return from subroutine 886 cv.visitVarInsn(RET, finallySubAddress); 887 888 // end of all catches and finally parts 889 cv.visitLabel(afterFinally); 890 891 // add catch any block to exception table 892 exceptionBlocks.add(new Runnable() { 893 public void run() { 894 cv.visitTryCatchBlock(tryStart, endOfAllCatches, catchAny, null); 895 } 896 }); 897 } 898 } 899 900 private Variable storeInTemp(String name, String type) { 901 Variable var = defineVariable(createVariableName(name), type, false); 902 int varIdx = var.getIndex(); 903 cv.visitVarInsn(ASTORE, varIdx); 904 if (CREATE_DEBUG_INFO) cv.visitLabel(var.getStartLabel()); 905 return var; 906 } 907 908 public void visitSwitch(SwitchStatement statement) { 909 onLineNumber(statement, "visitSwitch"); 910 911 statement.getExpression().visit(this); 912 913 // switch does not have a continue label. use its parent's for continue 914 pushBlockScope(false, true); 915 //scope.setContinueLabel(scope.getParent().getContinueLabel()); 916 917 918 int switchVariableIndex = defineVariable(createVariableName("switch"), "java.lang.Object").getIndex(); 919 cv.visitVarInsn(ASTORE, switchVariableIndex); 920 921 List caseStatements = statement.getCaseStatements(); 922 int caseCount = caseStatements.size(); 923 Label[] labels = new Label[caseCount + 1]; 924 for (int i = 0; i < caseCount; i++) { 925 labels[i] = new Label(); 926 } 927 928 int i = 0; 929 for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) { 930 CaseStatement caseStatement = (CaseStatement) iter.next(); 931 visitCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]); 932 } 933 934 statement.getDefaultStatement().visit(this); 935 936 cv.visitLabel(scope.getBreakLabel()); 937 938 popScope(); 939 } 940 941 public void visitCaseStatement(CaseStatement statement) { 942 } 943 944 public void visitCaseStatement( 945 CaseStatement statement, 946 int switchVariableIndex, 947 Label thisLabel, 948 Label nextLabel) { 949 950 onLineNumber(statement, "visitCaseStatement"); 951 952 cv.visitVarInsn(ALOAD, switchVariableIndex); 953 statement.getExpression().visit(this); 954 955 isCaseMethod.call(cv); 956 957 Label l0 = new Label(); 958 cv.visitJumpInsn(IFEQ, l0); 959 960 cv.visitLabel(thisLabel); 961 962 statement.getCode().visit(this); 963 964 // now if we don't finish with a break we need to jump past 965 // the next comparison 966 if (nextLabel != null) { 967 cv.visitJumpInsn(GOTO, nextLabel); 968 } 969 970 cv.visitLabel(l0); 971 } 972 973 public void visitBreakStatement(BreakStatement statement) { 974 onLineNumber(statement, "visitBreakStatement"); 975 976 Label breakLabel = scope.getBreakLabel(); 977 if (breakLabel != null ) { 978 cv.visitJumpInsn(GOTO, breakLabel); 979 } else { 980 // should warn that break is not allowed in the context. 981 } 982 } 983 984 public void visitContinueStatement(ContinueStatement statement) { 985 onLineNumber(statement, "visitContinueStatement"); 986 987 Label continueLabel = scope.getContinueLabel(); 988 if (continueLabel != null ) { 989 cv.visitJumpInsn(GOTO, continueLabel); 990 } else { 991 // should warn that continue is not allowed in the context. 992 } 993 } 994 995 public void visitSynchronizedStatement(SynchronizedStatement statement) { 996 onLineNumber(statement, "visitSynchronizedStatement"); 997 998 statement.getExpression().visit(this); 999 1000 int index = defineVariable(createVariableName("synchronized"), "java.lang.Integer").getIndex(); 1001 1002 cv.visitVarInsn(ASTORE, index); 1003 cv.visitInsn(MONITORENTER); 1004 final Label l0 = new Label(); 1005 cv.visitLabel(l0); 1006 1007 statement.getCode().visit(this); 1008 1009 cv.visitVarInsn(ALOAD, index); 1010 cv.visitInsn(MONITOREXIT); 1011 final Label l1 = new Label(); 1012 cv.visitJumpInsn(GOTO, l1); 1013 final Label l2 = new Label(); 1014 cv.visitLabel(l2); 1015 cv.visitVarInsn(ALOAD, index); 1016 cv.visitInsn(MONITOREXIT); 1017 cv.visitInsn(ATHROW); 1018 cv.visitLabel(l1); 1019 1020 exceptionBlocks.add(new Runnable() { 1021 public void run() { 1022 cv.visitTryCatchBlock(l0, l2, l2, null); 1023 } 1024 }); 1025 } 1026 1027 public void visitThrowStatement(ThrowStatement statement) { 1028 statement.getExpression().visit(this); 1029 1030 // we should infer the type of the exception from the expression 1031 cv.visitTypeInsn(CHECKCAST, "java/lang/Throwable"); 1032 1033 cv.visitInsn(ATHROW); 1034 } 1035 1036 public void visitReturnStatement(ReturnStatement statement) { 1037 onLineNumber(statement, "visitReturnStatement"); 1038 String returnType = methodNode.getReturnType(); 1039 if (returnType.equals("void")) { 1040 if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) { 1041 throwException("Cannot use return statement with an expression on a method that returns void"); 1042 } 1043 cv.visitInsn(RETURN); 1044 outputReturn = true; 1045 return; 1046 } 1047 1048 Expression expression = statement.getExpression(); 1049 evaluateExpression(expression); 1050 if (returnType.equals("java.lang.Object") && expression.getType() != null && expression.getType().equals("void")) { 1051 cv.visitInsn(ACONST_NULL); // cheat the caller 1052 cv.visitInsn(ARETURN); 1053 } else { 1054 //return is based on class type 1055 //TODO: make work with arrays 1056 // we may need to cast 1057 helper.unbox(returnType); 1058 if (returnType.equals("double")) { 1059 cv.visitInsn(DRETURN); 1060 } 1061 else if (returnType.equals("float")) { 1062 cv.visitInsn(FRETURN); 1063 } 1064 else if (returnType.equals("long")) { 1065 cv.visitInsn(LRETURN); 1066 } 1067 else if (returnType.equals("boolean")) { 1068 cv.visitInsn(IRETURN); 1069 } 1070 else if ( 1071 returnType.equals("char") 1072 || returnType.equals("byte") 1073 || returnType.equals("int") 1074 || returnType.equals("short")) { //byte,short,boolean,int are 1075 // all IRETURN 1076 cv.visitInsn(IRETURN); 1077 } 1078 else { 1079 doConvertAndCast(returnType, expression, false); 1080 cv.visitInsn(ARETURN); 1081 1082 /* 1083 if (c == Boolean.class) { 1084 Label l0 = new Label(); 1085 cv.visitJumpInsn(IFEQ, l0); 1086 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); 1087 cv.visitInsn(ARETURN); 1088 cv.visitLabel(l0); 1089 cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); 1090 cv.visitInsn(ARETURN); 1091 } 1092 else { 1093 if (isValidTypeForCast(returnType) && !returnType.equals(c.getName())) { 1094 doConvertAndCast(returnType, expression); 1095 } 1096 cv.visitInsn(ARETURN); 1097 } 1098 */ 1099 } 1100 } 1101 outputReturn = true; 1102 } 1103 1104 /** 1105 * Casts to the given type unless it can be determined that the cast is unnecessary 1106 */ 1107 protected void doConvertAndCast(String type, Expression expression, boolean ignoreAutoboxing) { 1108 String expType = getExpressionType(expression); 1109 // temp resolution: convert all primitive casting to corresponsing Object type 1110 if (!ignoreAutoboxing && BytecodeHelper.isPrimitiveType(type)) { 1111 type = BytecodeHelper.getObjectTypeForPrimitive(type); 1112 } 1113 if (expType == null || !type.equals(expType)) { 1114 doConvertAndCast(type); 1115 } 1116 } 1117 1118 /** 1119 * @param expression 1120 */ 1121 protected void evaluateExpression(Expression expression) { 1122 visitAndAutoboxBoolean(expression); 1123 //expression.visit(this); 1124 1125 Expression assignExpr = createReturnLHSExpression(expression); 1126 if (assignExpr != null) { 1127 leftHandExpression = false; 1128 assignExpr.visit(this); 1129 } 1130 } 1131 1132 public void visitExpressionStatement(ExpressionStatement statement) { 1133 onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName()); 1134 1135 Expression expression = statement.getExpression(); 1136 // disabled in favor of JIT resolving 1137 // if (ENABLE_EARLY_BINDING) 1138 // expression.resolve(this); 1139 1140 visitAndAutoboxBoolean(expression); 1141 1142 if (isPopRequired(expression)) { 1143 cv.visitInsn(POP); 1144 } 1145 } 1146 1147 // Expressions 1148 //------------------------------------------------------------------------- 1149 1150 public void visitBinaryExpression(BinaryExpression expression) { 1151 onLineNumber(expression, "visitBinaryExpression: \"" + expression.getOperation().getText() + "\" "); 1152 switch (expression.getOperation().getType()) { 1153 case Types.EQUAL : // = assignment 1154 evaluateEqual(expression); 1155 break; 1156 1157 case Types.COMPARE_IDENTICAL : // === 1158 evaluateBinaryExpression(compareIdenticalMethod, expression); 1159 break; 1160 1161 case Types.COMPARE_EQUAL : // == 1162 evaluateBinaryExpression(compareEqualMethod, expression); 1163 break; 1164 1165 case Types.COMPARE_NOT_EQUAL : 1166 evaluateBinaryExpression(compareNotEqualMethod, expression); 1167 break; 1168 1169 case Types.COMPARE_TO : 1170 evaluateCompareTo(expression); 1171 break; 1172 1173 case Types.COMPARE_GREATER_THAN : 1174 evaluateBinaryExpression(compareGreaterThanMethod, expression); 1175 break; 1176 1177 case Types.COMPARE_GREATER_THAN_EQUAL : 1178 evaluateBinaryExpression(compareGreaterThanEqualMethod, expression); 1179 break; 1180 1181 case Types.COMPARE_LESS_THAN : 1182 evaluateBinaryExpression(compareLessThanMethod, expression); 1183 break; 1184 1185 case Types.COMPARE_LESS_THAN_EQUAL : 1186 evaluateBinaryExpression(compareLessThanEqualMethod, expression); 1187 break; 1188 1189 case Types.LOGICAL_AND : 1190 evaluateLogicalAndExpression(expression); 1191 break; 1192 1193 case Types.LOGICAL_OR : 1194 evaluateLogicalOrExpression(expression); 1195 break; 1196 1197 case Types.BITWISE_AND : 1198 evaluateBinaryExpression("and", expression); 1199 break; 1200 1201 case Types.BITWISE_AND_EQUAL : 1202 evaluateBinaryExpressionWithAsignment("and", expression); 1203 break; 1204 1205 case Types.BITWISE_OR : 1206 evaluateBinaryExpression("or", expression); 1207 break; 1208 1209 case Types.BITWISE_OR_EQUAL : 1210 evaluateBinaryExpressionWithAsignment("or", expression); 1211 break; 1212 1213 case Types.BITWISE_XOR : 1214 evaluateBinaryExpression("xor", expression); 1215 break; 1216 1217 case Types.BITWISE_XOR_EQUAL : 1218 evaluateBinaryExpressionWithAsignment("xor", expression); 1219 break; 1220 1221 case Types.PLUS : 1222 { 1223 if (ENABLE_EARLY_BINDING) { 1224 expression.resolve(this); 1225 if (expression.isResolveFailed() || !expression.isTypeResolved()) { 1226 evaluateBinaryExpression("plus", expression); 1227 break; 1228 } 1229 Expression leftExpression = expression.getLeftExpression(); 1230 Expression rightExpression = expression.getRightExpression(); 1231 Class lclass = leftExpression.getTypeClass(); 1232 Class rclass = rightExpression.getTypeClass(); 1233 if (lclass == null || rclass == null) { 1234 evaluateBinaryExpression("plus", expression); 1235 break; 1236 } 1237 if (lclass == String.class && rclass == String.class) { 1238 // MethodCallExpression call = new MethodCallExpression( 1239 // leftExpression, 1240 // "concat", 1241 // new ArgumentListExpression(new Expression[] {rightExpression})); 1242 // call.setTypeClass(String.class); // must do to avoid excessive resolving 1243 // visitMethodCallExpression(call); 1244 cv.visitTypeInsn(NEW, "java/lang/StringBuffer"); 1245 cv.visitInsn(DUP); 1246 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V"); 1247 load(leftExpression); 1248 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); 1249 load(rightExpression); 1250 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); 1251 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;"); 1252 } 1253 else if (lclass == String.class && Number.class.isAssignableFrom(rclass) ) { 1254 cv.visitTypeInsn(NEW, "java/lang/StringBuffer"); 1255 cv.visitInsn(DUP); 1256 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V"); 1257 load(leftExpression); 1258 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); 1259 load(rightExpression); 1260 // will Object.toString() work here? 1261 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;"); 1262 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); 1263 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;"); 1264 } 1265 else if (rclass == String.class && Number.class.isAssignableFrom(lclass) ) { 1266 cv.visitTypeInsn(NEW, "java/lang/StringBuffer"); 1267 cv.visitInsn(DUP); 1268 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V"); 1269 load(leftExpression); 1270 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;"); 1271 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;"); 1272 load(rightExpression); 1273 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;"); // note the arg is object type for safety 1274 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;"); 1275 } 1276 else if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) { 1277 // assuming all return boxed version for primitives 1278 load(leftExpression); 1279 helper.quickUnboxIfNecessary(int.class); 1280 load(rightExpression); 1281 helper.quickUnboxIfNecessary(int.class); 1282 cv.visitInsn(IADD); 1283 helper.quickBoxIfNecessary(int.class); 1284 } 1285 else if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) { 1286 // let's use groovy utilities in the DefaultGroovyMethods 1287 load(leftExpression); 1288 load(rightExpression); 1289 cv.visitMethodInsn( 1290 INVOKESTATIC, 1291 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1292 "plus", 1293 "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;"); 1294 } 1295 else { // todo add more more number optimiztion 1296 evaluateBinaryExpression("plus", expression); 1297 } 1298 1299 } else { 1300 evaluateBinaryExpression("plus", expression); 1301 } 1302 } 1303 break; 1304 1305 case Types.PLUS_EQUAL : 1306 evaluateBinaryExpressionWithAsignment("plus", expression); 1307 break; 1308 case Types.MINUS : 1309 { 1310 if (ENABLE_EARLY_BINDING) { 1311 expression.resolve(this); 1312 if (expression.isResolveFailed() || !expression.isTypeResolved()) { 1313 evaluateBinaryExpression("minus", expression); 1314 break; 1315 } 1316 Expression leftExpression = expression.getLeftExpression(); 1317 Expression rightExpression = expression.getRightExpression(); 1318 Class lclass = leftExpression.getTypeClass(); 1319 Class rclass = rightExpression.getTypeClass(); 1320 if (lclass == null || rclass == null) { 1321 evaluateBinaryExpression("minus", expression); 1322 break; 1323 } 1324 if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) { 1325 // assuming all return boxed version for primitives 1326 load(leftExpression); 1327 helper.quickUnboxIfNecessary(int.class); 1328 load(rightExpression); 1329 helper.quickUnboxIfNecessary(int.class); 1330 cv.visitInsn(ISUB); 1331 helper.quickBoxIfNecessary(int.class); 1332 } 1333 else 1334 if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) { 1335 // let's use groovy utilities in the DefaultGroovyMethods 1336 load(leftExpression); 1337 load(rightExpression); 1338 cv.visitMethodInsn( 1339 INVOKESTATIC, 1340 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1341 "minus", 1342 "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;"); 1343 } 1344 else { // todo add more more number optimiztion 1345 evaluateBinaryExpression("minus", expression); 1346 } 1347 } else { 1348 evaluateBinaryExpression("minus", expression); 1349 } 1350 } 1351 break; 1352 case Types.MINUS_EQUAL : 1353 evaluateBinaryExpressionWithAsignment("minus", expression); 1354 break; 1355 1356 case Types.MULTIPLY : 1357 { 1358 if (ENABLE_EARLY_BINDING) { 1359 expression.resolve(this); 1360 if (expression.isResolveFailed() || !expression.isTypeResolved()) { 1361 evaluateBinaryExpression("multiply", expression); 1362 break; 1363 } 1364 Expression leftExpression = expression.getLeftExpression(); 1365 Expression rightExpression = expression.getRightExpression(); 1366 Class lclass = leftExpression.getTypeClass(); 1367 Class rclass = rightExpression.getTypeClass(); 1368 if (lclass == null || rclass == null) { 1369 evaluateBinaryExpression("multiply", expression); 1370 break; 1371 } 1372 if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) { 1373 // assuming all return boxed version for primitives 1374 load(leftExpression); 1375 helper.quickUnboxIfNecessary(int.class); 1376 load(rightExpression); 1377 helper.quickUnboxIfNecessary(int.class); 1378 cv.visitInsn(IMUL); 1379 helper.quickBoxIfNecessary(int.class); 1380 } 1381 else if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) { 1382 // let's use groovy utilities in the DefaultGroovyMethods 1383 load(leftExpression); 1384 load(rightExpression); 1385 cv.visitMethodInsn( 1386 INVOKESTATIC, 1387 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1388 "multiply", 1389 "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;"); 1390 } 1391 else { // todo add more more number optimiztion 1392 evaluateBinaryExpression("multiply", expression); 1393 } 1394 } else { 1395 evaluateBinaryExpression("multiply", expression); 1396 } 1397 } 1398 1399 break; 1400 1401 case Types.MULTIPLY_EQUAL : 1402 evaluateBinaryExpressionWithAsignment("multiply", expression); 1403 break; 1404 1405 case Types.DIVIDE : 1406 //SPG don't use divide since BigInteger implements directly 1407 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result 1408 { 1409 if (ENABLE_EARLY_BINDING) { 1410 expression.resolve(this); 1411 if (expression.isResolveFailed() || !expression.isTypeResolved()) { 1412 evaluateBinaryExpression("div", expression); 1413 break; 1414 } 1415 Expression leftExpression = expression.getLeftExpression(); 1416 Expression rightExpression = expression.getRightExpression(); 1417 Class lclass = leftExpression.getTypeClass(); 1418 Class rclass = rightExpression.getTypeClass(); 1419 if (lclass == null || rclass == null) { 1420 evaluateBinaryExpression("div", expression); 1421 break; 1422 } 1423 // 1424 // if ((lclass == Integer.class || lclass == int.class) && (rclass == Integer.class || rclass == int.class)) { 1425 // // assuming all return boxed version for primitives 1426 // load(leftExpression); 1427 // helper.quickUnboxIfNecessary(int.class); 1428 // cv.visitInsn(I2D); 1429 // load(rightExpression); 1430 // helper.quickUnboxIfNecessary(int.class); 1431 // cv.visitInsn(I2D); 1432 // cv.visitInsn(DDIV); 1433 // helper.quickBoxIfNecessary(double.class); 1434 // } 1435 // else 1436 if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) { 1437 // let's use groovy utilities in the DefaultGroovyMethods 1438 load(leftExpression); 1439 load(rightExpression); 1440 cv.visitMethodInsn( 1441 INVOKESTATIC, 1442 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1443 "div", 1444 "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;"); 1445 } 1446 else { // todo add more more number optimiztion 1447 evaluateBinaryExpression("div", expression); 1448 } 1449 } else { 1450 evaluateBinaryExpression("div", expression); 1451 } 1452 } 1453 1454 break; 1455 1456 case Types.DIVIDE_EQUAL : 1457 //SPG don't use divide since BigInteger implements directly 1458 //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result 1459 evaluateBinaryExpressionWithAsignment("div", expression); 1460 break; 1461 1462 case Types.INTDIV : 1463 { 1464 if (ENABLE_EARLY_BINDING) { 1465 expression.resolve(this); 1466 if (expression.isResolveFailed() || !expression.isTypeResolved()) { 1467 evaluateBinaryExpression("intdiv", expression); 1468 break; 1469 } 1470 Expression leftExpression = expression.getLeftExpression(); 1471 Expression rightExpression = expression.getRightExpression(); 1472 Class lclass = leftExpression.getTypeClass(); 1473 Class rclass = rightExpression.getTypeClass(); 1474 if (lclass == null || rclass == null) { 1475 evaluateBinaryExpression("intdiv", expression); 1476 break; 1477 } 1478 if (Number.class.isAssignableFrom(lclass) && Number.class.isAssignableFrom(rclass)) { 1479 // let's use groovy utilities in the DefaultGroovyMethods 1480 load(leftExpression); 1481 load(rightExpression); 1482 cv.visitMethodInsn( 1483 INVOKESTATIC, 1484 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1485 "intdiv", 1486 "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;"); 1487 } 1488 else { // todo add more more number optimiztion 1489 evaluateBinaryExpression("intdiv", expression); 1490 } 1491 } else { 1492 evaluateBinaryExpression("intdiv", expression); 1493 } 1494 } 1495 break; 1496 1497 case Types.INTDIV_EQUAL : 1498 evaluateBinaryExpressionWithAsignment("intdiv", expression); 1499 break; 1500 1501 case Types.MOD : 1502 evaluateBinaryExpression("mod", expression); 1503 break; 1504 1505 case Types.MOD_EQUAL : 1506 evaluateBinaryExpressionWithAsignment("mod", expression); 1507 break; 1508 1509 case Types.POWER : 1510 evaluateBinaryExpression("power", expression); 1511 break; 1512 1513 case Types.POWER_EQUAL : 1514 evaluateBinaryExpressionWithAsignment("power", expression); 1515 break; 1516 1517 case Types.LEFT_SHIFT : 1518 evaluateBinaryExpression("leftShift", expression); 1519 break; 1520 1521 case Types.LEFT_SHIFT_EQUAL : 1522 evaluateBinaryExpressionWithAsignment("leftShift", expression); 1523 break; 1524 1525 case Types.RIGHT_SHIFT : 1526 evaluateBinaryExpression("rightShift", expression); 1527 break; 1528 1529 case Types.RIGHT_SHIFT_EQUAL : 1530 evaluateBinaryExpressionWithAsignment("rightShift", expression); 1531 break; 1532 1533 case Types.RIGHT_SHIFT_UNSIGNED : 1534 evaluateBinaryExpression("rightShiftUnsigned", expression); 1535 break; 1536 1537 case Types.RIGHT_SHIFT_UNSIGNED_EQUAL : 1538 evaluateBinaryExpressionWithAsignment("rightShiftUnsigned", expression); 1539 break; 1540 1541 case Types.KEYWORD_INSTANCEOF : 1542 evaluateInstanceof(expression); 1543 break; 1544 1545 case Types.FIND_REGEX : 1546 evaluateBinaryExpression(findRegexMethod, expression); 1547 break; 1548 1549 case Types.MATCH_REGEX : 1550 evaluateBinaryExpression(matchRegexMethod, expression); 1551 break; 1552 1553 case Types.LEFT_SQUARE_BRACKET : 1554 if (leftHandExpression) { 1555 throwException("Should not be called here. Possible reason: postfix operation on array."); 1556 // This is handled right now in the evaluateEqual() 1557 // should support this here later 1558 //evaluateBinaryExpression("putAt", expression); 1559 } 1560 else if (ENABLE_EARLY_BINDING) { 1561 expression.resolve(this); 1562 if (expression.isResolveFailed() || !expression.isTypeResolved()) { 1563 evaluateBinaryExpression("getAt", expression); 1564 break; 1565 } 1566 Expression leftExpression = expression.getLeftExpression(); 1567 Expression rightExpression = expression.getRightExpression(); 1568 Class lclass = leftExpression.getTypeClass(); 1569 Class rclass = rightExpression.getTypeClass(); 1570 if (lclass == null || rclass == null) { 1571 evaluateBinaryExpression("getAt", expression); 1572 break; 1573 } 1574 if (lclass == String.class && rclass == Integer.class) { 1575 load(leftExpression); cast(String.class); 1576 load(rightExpression); helper.quickUnboxIfNecessary(int.class); 1577 cv.visitMethodInsn( 1578 INVOKESTATIC, 1579 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1580 "getAt", 1581 "([Ljava/lang/String;I)Ljava/lang/String;"); 1582 break; 1583 } 1584 else if (lclass.isArray() && rclass == Integer.class) { 1585 load(leftExpression); // cast it? 1586 load(rightExpression); helper.quickUnboxIfNecessary(int.class); 1587 Class elemType = lclass.getComponentType(); 1588 if (!elemType.isPrimitive()) { 1589 cv.visitMethodInsn( 1590 INVOKESTATIC, 1591 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1592 "getAt", 1593 "([Ljava/lang/Object;I)Ljava/lang/Object;"); 1594 cast(elemType); 1595 } 1596 else { 1597 evaluateBinaryExpression("getAt", expression); // todo more optim 1598 } 1599 break; 1600 } 1601 else if (List.class == lclass && rclass == Integer.class){ 1602 // there is special logic in treating list subscript 1603 load(leftExpression); cast(List.class); 1604 load(rightExpression); helper.quickUnboxIfNecessary(int.class); 1605 //INVOKESTATIC org/codehaus/groovy/runtime/DefaultGroovyMethods getAt (Ljava/util/List;I)Ljava/lang/Object; 1606 cv.visitMethodInsn( 1607 INVOKESTATIC, 1608 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1609 "getAt", 1610 "(Ljava/util/List;I)Ljava/lang/Object;"); 1611 break; 1612 } 1613 else if (Map.class.isAssignableFrom(lclass)){ // todo test this 1614 visitMethodCallExpression( 1615 new MethodCallExpression( 1616 leftExpression, 1617 "get", 1618 new ArgumentListExpression( 1619 new Expression[] { rightExpression}))); 1620 break; 1621 } 1622 else { 1623 evaluateBinaryExpression("getAt", expression); // todo more optim 1624 break; 1625 } 1626 } 1627 else { 1628 evaluateBinaryExpression("getAt", expression); 1629 } 1630 break; 1631 1632 default : 1633 throwException("Operation: " + expression.getOperation() + " not supported"); 1634 } 1635 } 1636 1637 private void load(Expression exp) { 1638 1639 boolean wasLeft = leftHandExpression; 1640 leftHandExpression = false; 1641 // if (CREATE_DEBUG_INFO) 1642 // helper.mark("-- loading expression: " + exp.getClass().getName() + 1643 // " at [" + exp.getLineNumber() + ":" + exp.getColumnNumber() + "]"); 1644 //exp.visit(this); 1645 visitAndAutoboxBoolean(exp); 1646 // if (CREATE_DEBUG_INFO) 1647 // helper.mark(" -- end of loading --"); 1648 1649 1650 if (ENABLE_EARLY_BINDING){ 1651 // casting might be expensive. should do JIT casting 1652 1653 // Class cls = exp.getTypeClass(); 1654 // if (cls != null && !cls.isPrimitive() && cls != Object.class) { 1655 // cast(cls); 1656 // } 1657 } 1658 //evaluateExpression(exp); 1659 leftHandExpression = wasLeft; 1660 } 1661 1662 public void visitPostfixExpression(PostfixExpression expression) { 1663 if (ENABLE_EARLY_BINDING) { 1664 int type = expression.getOperation().getType(); 1665 expression.resolve(this); 1666 if (expression.isResolveFailed() || !expression.isTypeResolved()) { 1667 evaluatePostfixMethod("next", expression.getExpression()); 1668 return; 1669 } 1670 Class lclass = expression.getTypeClass(); 1671 Expression exp = expression.getExpression(); 1672 String func = type == Types.PLUS_PLUS ? "next" : "previous"; 1673 int op = type == Types.PLUS_PLUS ? IADD : ISUB; 1674 1675 if (lclass == Integer.class) { 1676 load(exp); 1677 cv.visitInsn(DUP); // leave the old value on the stack; 1678 helper.quickUnboxIfNecessary(int.class); 1679 cv.visitInsn(ICONST_1); 1680 cv.visitInsn(op); 1681 helper.quickBoxIfNecessary(int.class); 1682 store(exp); 1683 } 1684 else if (Number.class.isAssignableFrom(lclass)) { 1685 // let's use groovy utilities in the DefaultGroovyMethods 1686 load(exp); 1687 cv.visitInsn(DUP); // leave the old value on the stack; 1688 cv.visitMethodInsn( 1689 INVOKESTATIC, 1690 BytecodeHelper.getClassInternalName(DefaultGroovyMethods.class.getName()), 1691 func, 1692 "(Ljava/lang/Number;)Ljava/lang/Number;"); 1693 store(exp); 1694 } 1695 else { // todo add more more number optimiztion 1696 evaluatePostfixMethod(func, exp); 1697 } 1698 1699 } else { 1700 switch (expression.getOperation().getType()) { 1701 case Types.PLUS_PLUS : 1702 evaluatePostfixMethod("next", expression.getExpression()); 1703 break; 1704 case Types.MINUS_MINUS : 1705 evaluatePostfixMethod("previous", expression.getExpression()); 1706 break; 1707 } 1708 } 1709 } 1710 1711 // store the data on the stack to the expression (variablem, property, field, etc. 1712 private void store(Expression expression) { 1713 if (expression instanceof BinaryExpression) { 1714 throwException("BinaryExpression appeared on LHS. "); 1715 } 1716 if (ASM_DEBUG) { 1717 if (expression instanceof VariableExpression) { 1718 helper.mark(((VariableExpression)expression).getVariable()); 1719 } 1720 } 1721 boolean wasLeft = leftHandExpression; 1722 leftHandExpression = true; 1723 expression.visit(this); 1724 //evaluateExpression(expression); 1725 leftHandExpression = wasLeft; 1726 return; 1727 } 1728 1729 private void throwException(String s) { 1730 //throw new ClassGeneratorException(s + ". Source: " + classNode.getName() + ":[" + this.lineNumber + ":" + this.columnNumber + "]"); 1731 throw new RuntimeParserException(s, currentASTNode); 1732 } 1733 1734 public void visitPrefixExpression(PrefixExpression expression) { 1735 switch (expression.getOperation().getType()) { 1736 case Types.PLUS_PLUS : 1737 evaluatePrefixMethod("next", expression.getExpression()); 1738 break; 1739 case Types.MINUS_MINUS : 1740 evaluatePrefixMethod("previous", expression.getExpression()); 1741 break; 1742 } 1743 } 1744 1745 public void visitClosureExpression(ClosureExpression expression) { 1746 ClassNode innerClass = createClosureClass(expression); 1747 addInnerClass(innerClass); 1748 String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName()); 1749 1750 ClassNode owner = innerClass.getOuterClass(); 1751 String ownerTypeName = owner.getName(); 1752 /* 1753 if (classNode.isStaticClass() || isStaticMethod()) { 1754 ownerTypeName = "java.lang.Class"; 1755 } 1756 */ 1757 passingClosureParams = true; 1758 List constructors = innerClass.getDeclaredConstructors(); 1759 ConstructorNode node = (ConstructorNode) constructors.get(0); 1760 Parameter[] localVariableParams = node.getParameters(); 1761 1762 1763 // 1764 // Define in the context any variables that will be 1765 // created inside the closure. Note that the first two 1766 // parameters are always _outerInstance and _delegate, 1767 // so we don't worry about them. 1768 1769 for (int i = 2; i < localVariableParams.length; i++) { 1770 Parameter param = localVariableParams[i]; 1771 String name = param.getName(); 1772 1773 if (variableStack.get(name) == null && classNode.getField(name) == null) { 1774 defineVariable(name, "java.lang.Object"); // todo should use param type is available 1775 } 1776 } 1777 1778 cv.visitTypeInsn(NEW, innerClassinternalName); 1779 cv.visitInsn(DUP); 1780 if (isStaticMethod() || classNode.isStaticClass()) { 1781 visitClassExpression(new ClassExpression(ownerTypeName)); 1782 } 1783 else { 1784 loadThisOrOwner(); 1785 } 1786 1787 if (innerClass.getSuperClass().equals("groovy.lang.Closure")) { 1788 if (isStaticMethod()) { 1789 /** 1790 * todo could maybe stash this expression in a JVM variable 1791 * from previous statement above 1792 */ 1793 visitClassExpression(new ClassExpression(ownerTypeName)); 1794 } 1795 else { 1796 loadThisOrOwner(); 1797 } 1798 } 1799 1800 //String prototype = "(L" + BytecodeHelper.getClassInternalName(ownerTypeName) + ";Ljava/lang/Object;"; 1801 1802 // now lets load the various parameters we're passing 1803 for (int i = 2; i < localVariableParams.length; i++) { 1804 Parameter param = localVariableParams[i]; 1805 String name = param.getName(); 1806 1807 if (variableStack.get(name) == null) { 1808 visitFieldExpression(new FieldExpression(classNode.getField(name))); 1809 } 1810 else { 1811 visitVariableExpression(new VariableExpression(name)); 1812 } 1813 //prototype = prototype + "L" + BytecodeHelper.getClassInternalName(param.getType()) + ";"; 1814 } 1815 passingClosureParams = false; 1816 1817 // we may need to pass in some other constructors 1818 //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V"); 1819 cv.visitMethodInsn( 1820 INVOKESPECIAL, 1821 innerClassinternalName, 1822 "<init>", 1823 BytecodeHelper.getMethodDescriptor("void", localVariableParams)); 1824 } 1825 1826 /** 1827 * Loads either this object or if we're inside a closure then load the top level owner 1828 */ 1829 protected void loadThisOrOwner() { 1830 if (isInnerClass()) { 1831 visitFieldExpression(new FieldExpression(classNode.getField("owner"))); 1832 } 1833 else { 1834 cv.visitVarInsn(ALOAD, 0); 1835 } 1836 } 1837 1838 public void visitRegexExpression(RegexExpression expression) { 1839 expression.getRegex().visit(this); 1840 regexPattern.call(cv); 1841 } 1842 1843 /** 1844 * Generate byte code for constants 1845 * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a> 1846 */ 1847 public void visitConstantExpression(ConstantExpression expression) { 1848 Object value = expression.getValue(); 1849 helper.loadConstant(value); 1850 } 1851 1852 public void visitSpreadExpression(SpreadExpression expression) { 1853 Expression subExpression = expression.getExpression(); 1854 subExpression.visit(this); 1855 spreadList.call(cv); 1856 } 1857 1858 public void visitSpreadMapExpression(SpreadMapExpression expression) { 1859 Expression subExpression = expression.getExpression(); 1860 subExpression.visit(this); 1861 spreadMap.call(cv); 1862 } 1863 1864 public void visitMethodPointerExpression(MethodPointerExpression expression) { 1865 Expression subExpression = expression.getExpression(); 1866 subExpression.visit(this); 1867 helper.loadConstant(expression.getMethodName()); 1868 getMethodPointer.call(cv); 1869 } 1870 1871 public void visitNegationExpression(NegationExpression expression) { 1872 Expression subExpression = expression.getExpression(); 1873 subExpression.visit(this); 1874 negation.call(cv); 1875 } 1876 1877 public void visitBitwiseNegExpression(BitwiseNegExpression expression) { 1878 Expression subExpression = expression.getExpression(); 1879 subExpression.visit(this); 1880 bitNegation.call(cv); 1881 } 1882 1883 public void visitCastExpression(CastExpression expression) { 1884 String type = expression.getType(); 1885 type = checkValidType(type, expression, "in cast"); 1886 1887 visitAndAutoboxBoolean(expression.getExpression()); 1888 1889 doConvertAndCast(type, expression.getExpression(), expression.isIgnoringAutoboxing()); 1890 } 1891 1892 public void visitNotExpression(NotExpression expression) { 1893 Expression subExpression = expression.getExpression(); 1894 subExpression.visit(this); 1895 1896 // This is not the best way to do this. Javac does it by reversing the 1897 // underlying expressions but that proved 1898 // fairly complicated for not much gain. Instead we'll just use a 1899 // utility function for now. 1900 if (isComparisonExpression(expression.getExpression())) { 1901 notBoolean.call(cv); 1902 } 1903 else { 1904 notObject.call(cv); 1905 } 1906 } 1907 1908 /** 1909 * return a primitive boolean value of the BooleanExpresion. 1910 * @param expression 1911 */ 1912 public void visitBooleanExpression(BooleanExpression expression) { 1913 expression.getExpression().visit(this); 1914 1915 if (!isComparisonExpression(expression.getExpression())) { 1916 // comment out for optimization when boolean values are not autoboxed for eg. function calls. 1917 // Class typeClass = expression.getExpression().getTypeClass(); 1918 // if (typeClass != null && typeClass != boolean.class) { 1919 asBool.call(cv); // to return a primitive boolean 1920 // } 1921 } 1922 } 1923 1924 public void visitMethodCallExpression(MethodCallExpression call) { 1925 onLineNumber(call, "visitMethodCallExpression: \"" + call.getMethod() + "\":"); 1926 if (ENABLE_EARLY_BINDING) 1927 call.resolve(this); 1928 1929 this.leftHandExpression = false; 1930 1931 Expression arguments = call.getArguments(); 1932 /* 1933 * if (arguments instanceof TupleExpression) { TupleExpression 1934 * tupleExpression = (TupleExpression) arguments; int size = 1935 * tupleExpression.getExpressions().size(); if (size == 0) { arguments = 1936 * ConstantExpression.EMPTY_ARRAY; } } 1937 */ 1938 boolean superMethodCall = MethodCallExpression.isSuperMethodCall(call); 1939 String method = call.getMethod(); 1940 if (superMethodCall && method.equals("<init>")) { 1941 /** todo handle method types! */ 1942 cv.visitVarInsn(ALOAD, 0); 1943 if (isInClosureConstructor()) { // br use the second param to init the super class (Closure) 1944 cv.visitVarInsn(ALOAD, 2); 1945 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V"); 1946 } 1947 else { 1948 cv.visitVarInsn(ALOAD, 1); 1949 cv.visitMethodInsn(INVOKESPECIAL, internalBaseClassName, "<init>", "(Ljava/lang/Object;)V"); 1950 } 1951 } 1952 else { 1953 // are we a local variable 1954 if (isThisExpression(call.getObjectExpression()) && isFieldOrVariable(method) && ! classNode.hasPossibleMethod(method, arguments)) { 1955 /* 1956 * if (arguments instanceof TupleExpression) { TupleExpression 1957 * tupleExpression = (TupleExpression) arguments; int size = 1958 * tupleExpression.getExpressions().size(); if (size == 1) { 1959 * arguments = (Expression) 1960 * tupleExpression.getExpressions().get(0); } } 1961 */ 1962 1963 // lets invoke the closure method 1964 visitVariableExpression(new VariableExpression(method)); 1965 arguments.visit(this); 1966 invokeClosureMethod.call(cv); 1967 } 1968 else { 1969 if (superMethodCall) { 1970 if (method.equals("super") || method.equals("<init>")) { 1971 ConstructorNode superConstructorNode = findSuperConstructor(call); 1972 1973 cv.visitVarInsn(ALOAD, 0); 1974 1975 loadArguments(superConstructorNode.getParameters(), arguments); 1976 1977 String descriptor = BytecodeHelper.getMethodDescriptor("void", superConstructorNode.getParameters()); 1978 cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", descriptor); 1979 } 1980 else { 1981 MethodNode superMethodNode = findSuperMethod(call); 1982 1983 cv.visitVarInsn(ALOAD, 0); 1984 1985 loadArguments(superMethodNode.getParameters(), arguments); 1986 1987 String descriptor = BytecodeHelper.getMethodDescriptor(superMethodNode.getReturnType(), superMethodNode.getParameters()); 1988 cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(superMethodNode.getDeclaringClass().getName()), method, descriptor); 1989 } 1990 } 1991 else { 1992 // let's try early binding 1993 if (ENABLE_EARLY_BINDING) { 1994 try { 1995 MetaMethod metamethod = call.getMetaMethod(); // todo change it to resolveMethodCallExpression 1996 if (metamethod != null) { 1997 Class decClass = metamethod.getDeclaringClass(); 1998 String ownerClassName = null; 1999 if (decClass == null) { 2000 // meaning the class is the current class 2001 ownerClassName = BytecodeHelper.getClassInternalName(classNode.getName()); 2002 } 2003 else { 2004 ownerClassName = BytecodeHelper.getClassInternalName(decClass.getName()); 2005 } 2006 2007 String methodName = call.getMethod(); 2008 String descr = BytecodeHelper.getMethodDescriptor(metamethod); 2009 Class[] params = metamethod.getParameterTypes(); 2010 // 2011 Label l2 = new Label(); 2012 2013 if (metamethod.isStatic()) { 2014 } else { 2015 boolean wasLeft = leftHandExpression; 2016 leftHandExpression = false; 2017 call.getObjectExpression().visit(this); 2018 2019 if (call.isSafe()) { 2020 helper.dup(); 2021 cv.visitJumpInsn(IFNULL, l2); 2022 } 2023 2024 cv.visitTypeInsn(CHECKCAST, ownerClassName); 2025 leftHandExpression = wasLeft; 2026 } 2027 // 2028 if (arguments instanceof TupleExpression) { 2029 TupleExpression tupleExpression = (TupleExpression) arguments; 2030 List argexps = tupleExpression.getExpressions(); 2031 for (int i = 0; i < argexps.size(); i++) { 2032 Expression expression = (Expression) argexps.get(i); 2033 load(expression); 2034 2035 if (params[i].isPrimitive() /*&& !expression.getTypeClass().isPrimitive()*/) { // data always boxed 2036 cast(params[i]); 2037 helper.quickUnboxIfNecessary(params[i]); 2038 } 2039 else if (params[i].isArray() && params[i].getComponentType().isPrimitive() ) { 2040 new ClassExpression(params[i].getComponentType()).visit(this); 2041 convertToPrimitiveArray.call(cv); 2042 cast(params[i]); 2043 } 2044 else { 2045 if (expression.getTypeClass() == GString.class && params[i] == String.class){ 2046 cast(GString.class); 2047 cv.visitMethodInsn( 2048 INVOKEVIRTUAL, 2049 "java/lang/Object", 2050 "toString", 2051 "()Ljava/lang/String;" 2052 ); 2053 } 2054 else { 2055 cast(params[i]); 2056 } 2057 } 2058 } 2059 if (metamethod.isStatic()) { 2060 cv.visitMethodInsn(INVOKESTATIC, ownerClassName, methodName, descr); 2061 } 2062 else if (decClass != null && decClass.isInterface()){ 2063 cv.visitMethodInsn(INVOKEINTERFACE, ownerClassName, methodName, descr); 2064 } 2065 else { 2066 cv.visitMethodInsn(INVOKEVIRTUAL, ownerClassName, methodName, descr); 2067 } 2068 call.setTypeClass(metamethod.getReturnType()); 2069 if (metamethod.getReturnType().isPrimitive() 2070 && metamethod.getReturnType() != void.class 2071 //&& metamethod.getReturnType() != boolean.class 2072 ) { 2073 helper.quickBoxIfNecessary(metamethod.getReturnType()); 2074 } 2075 if (call.isSafe()) { 2076 Label l3 = new Label(); 2077 cv.visitJumpInsn(GOTO, l3); 2078 cv.visitLabel(l2); 2079 cv.visitInsn(POP); 2080 cv.visitInsn(ACONST_NULL); 2081 cv.visitLabel(l3); 2082 } 2083 return; 2084 } else { 2085 throw new GroovyRuntimeException("arguments type not handled. fall through to late binding"); 2086 } 2087 } 2088 } catch (Exception e) { 2089 // System.out.println(this.classNode.getName() + ":" + this.methodNode.getName()); 2090 // //e.printStackTrace(); //System.out.println(e.getMessage()); 2091 // log.info("ignore: attempt early binding: " + e.getMessage()); 2092 // fall through 2093 } 2094 } // end of early binding trial 2095 2096 if (emptyArguments(arguments) && !call.isSafe() && !call.isSpreadSafe()) { 2097 call.getObjectExpression().visit(this); 2098 cv.visitLdcInsn(method); 2099 invokeNoArgumentsMethod.call(cv); // todo try if we can do early binding 2100 } 2101 else { 2102 if (argumentsUseStack(arguments)) { 2103 2104 arguments.visit(this); 2105 2106 Variable tv = visitASTOREInTemp(method + "_arg"); 2107 int paramIdx = tv.getIndex(); 2108 2109 call.getObjectExpression().visit(this); // xxx 2110 2111 cv.visitLdcInsn(method); 2112 2113 cv.visitVarInsn(ALOAD, paramIdx); 2114 removeVar(tv); 2115 } 2116 else { 2117 call.getObjectExpression().visit(this); 2118 cv.visitLdcInsn(method); 2119 arguments.visit(this); 2120 } 2121 2122 if (call.isSpreadSafe()) { 2123 invokeMethodSpreadSafeMethod.call(cv); 2124 } 2125 else if (call.isSafe()) { 2126 invokeMethodSafeMethod.call(cv); 2127 } 2128 else { 2129 invokeMethodMethod.call(cv); 2130 } 2131 } 2132 } 2133 } 2134 } 2135 } 2136 2137 /** 2138 * Loads and coerces the argument values for the given method call 2139 */ 2140 protected void loadArguments(Parameter[] parameters, Expression expression) { 2141 TupleExpression argListExp = (TupleExpression) expression; 2142 List arguments = argListExp.getExpressions(); 2143 for (int i = 0, size = arguments.size(); i < size; i++) { 2144 Expression argExp = argListExp.getExpression(i); 2145 Parameter param = parameters[i]; 2146 visitAndAutoboxBoolean(argExp); 2147 2148 String type = param.getType(); 2149 if (BytecodeHelper.isPrimitiveType(type)) { 2150 helper.unbox(type); 2151 } 2152 2153 String expType = getExpressionType(argExp); 2154 if (isValidTypeForCast(type) && (expType == null || !type.equals(expType))) { 2155 doConvertAndCast(type); 2156 } 2157 // doConvertAndCast(type, argExp); 2158 } 2159 } 2160 2161 /** 2162 * Attempts to find the method of the given name in a super class 2163 */ 2164 protected MethodNode findSuperMethod(MethodCallExpression call) { 2165 String methodName = call.getMethod(); 2166 TupleExpression argExpr = (TupleExpression) call.getArguments(); 2167 int argCount = argExpr.getExpressions().size(); 2168 ClassNode superClassNode = classNode.getSuperClassNode(); 2169 if (superClassNode != null) { 2170 List methods = superClassNode.getMethods(methodName); 2171 for (Iterator iter = methods.iterator(); iter.hasNext(); ) { 2172 MethodNode method = (MethodNode) iter.next(); 2173 if (method.getParameters().length == argCount) { 2174 return method; 2175 } 2176 } 2177 } 2178 throwException("No such method: " + methodName + " for class: " + classNode.getName()); 2179 return null; // should not come here 2180 } 2181 2182 /** 2183 * Attempts to find the constructor in a super class 2184 */ 2185 protected ConstructorNode findSuperConstructor(MethodCallExpression call) { 2186 TupleExpression argExpr = (TupleExpression) call.getArguments(); 2187 int argCount = argExpr.getExpressions().size(); 2188 ClassNode superClassNode = classNode.getSuperClassNode(); 2189 if (superClassNode != null) { 2190 List constructors = superClassNode.getDeclaredConstructors(); 2191 for (Iterator iter = constructors.iterator(); iter.hasNext(); ) { 2192 ConstructorNode constructor = (ConstructorNode) iter.next(); 2193 if (constructor.getParameters().length == argCount) { 2194 return constructor; 2195 } 2196 } 2197 } 2198 throwException("No such constructor for class: " + classNode.getName()); 2199 return null; // should not come here 2200 } 2201 2202 protected boolean emptyArguments(Expression arguments) { 2203 if (arguments instanceof TupleExpression) { 2204 TupleExpression tupleExpression = (TupleExpression) arguments; 2205 int size = tupleExpression.getExpressions().size(); 2206 return size == 0; 2207 } 2208 return false; 2209 } 2210 2211 public void visitStaticMethodCallExpression(StaticMethodCallExpression call) { 2212 this.leftHandExpression = false; 2213 2214 Expression arguments = call.getArguments(); 2215 if (emptyArguments(arguments)) { 2216 cv.visitLdcInsn(call.getType()); 2217 cv.visitLdcInsn(call.getMethod()); 2218 2219 invokeStaticNoArgumentsMethod.call(cv); 2220 } 2221 else { 2222 if (arguments instanceof TupleExpression) { 2223 TupleExpression tupleExpression = (TupleExpression) arguments; 2224 int size = tupleExpression.getExpressions().size(); 2225 if (size == 1) { 2226 arguments = (Expression) tupleExpression.getExpressions().get(0); 2227 } 2228 } 2229 2230 cv.visitLdcInsn(call.getOwnerType()); 2231 cv.visitLdcInsn(call.getMethod()); 2232 arguments.visit(this); 2233 2234 invokeStaticMethodMethod.call(cv); 2235 } 2236 } 2237 2238 public void visitConstructorCallExpression(ConstructorCallExpression call) { 2239 onLineNumber(call, "visitConstructorCallExpression: \"" + call.getTypeToSet() + "\":"); 2240 do { 2241 if (ENABLE_EARLY_BINDING) { 2242 call.resolve(this); 2243 if (call.isResolveFailed() || call.getTypeClass() == null) { 2244 break; 2245 } 2246 else { 2247 try { 2248 Constructor ctor = call.getConstructor(); // todo change it to resolveMethodCallExpression 2249 if (ctor != null) { 2250 Class decClass = ctor.getDeclaringClass(); 2251 String ownerClassName = null; 2252 if (decClass == null) { 2253 // meaning the class is the current class 2254 ownerClassName = BytecodeHelper.getClassInternalName(classNode.getName()); 2255 } 2256 else { 2257 ownerClassName = BytecodeHelper.getClassInternalName(decClass.getName()); 2258 } 2259 2260 Class[] params = ctor.getParameterTypes(); 2261 StringBuffer argbuf = new StringBuffer("("); 2262 for (int i = 0; i < params.length; i++) { 2263 Class arg = params[i]; 2264 String descr = BytecodeHelper.getTypeDescription(arg); 2265 argbuf.append(descr); 2266 } 2267 argbuf.append(")V"); 2268 // 2269 cv.visitTypeInsn(NEW, ownerClassName); 2270 cv.visitInsn(DUP); 2271 2272 // 2273 Expression arguments = call.getArguments(); 2274 if (arguments instanceof TupleExpression) { 2275 TupleExpression tupleExpression = (TupleExpression) arguments; 2276 List argexps = tupleExpression.getExpressions(); 2277 for (int i = 0; i < argexps.size(); i++) { 2278 Expression expression = (Expression) argexps.get(i); 2279 load(expression); 2280 if (params[i].isPrimitive() /*&& !expression.getTypeClass().isPrimitive()*/) { // data always boxed 2281 cast(params[i]); 2282 helper.quickUnboxIfNecessary(params[i]); 2283 } 2284 else if (params[i].isArray() && params[i].getComponentType().isPrimitive() ) { 2285 new ClassExpression(params[i].getComponentType()).visit(this); 2286 convertToPrimitiveArray.call(cv); 2287 cast(params[i]); 2288 } 2289 else { 2290 //? if the target is String , I might as well call Object.toString() regardless 2291 if (expression.getTypeClass() == GString.class && params[i] == String.class){ 2292 cast(GString.class); 2293 cv.visitMethodInsn( 2294 INVOKEVIRTUAL, 2295 "java/lang/Object", 2296 "toString", 2297 "()Ljava/lang/String;" 2298 ); 2299 } 2300 else { 2301 cast(params[i]); 2302 } 2303 } 2304 } 2305 2306 cv.visitMethodInsn(INVOKESPECIAL, ownerClassName, "<init>", argbuf.toString()); 2307 return; 2308 } else { 2309 throw new GroovyRuntimeException("arguments type not handled. fall through to late binding"); 2310 } 2311 } 2312 } catch (Exception e) { 2313 // System.out.println(this.classNode.getName() + ":" + this.methodNode.getName()); 2314 //e.printStackTrace(); //System.out.println(e.getMessage()); 2315 // log.info("ignore: attempt early binding: " + e.getMessage()); 2316 break;// fall through 2317 } 2318 } 2319 } 2320 } while(false); 2321 2322 this.leftHandExpression = false; 2323 2324 Expression arguments = call.getArguments(); 2325 if (arguments instanceof TupleExpression) { 2326 TupleExpression tupleExpression = (TupleExpression) arguments; 2327 int size = tupleExpression.getExpressions().size(); 2328 if (size == 0) { 2329 arguments = null; 2330 } 2331 // else if (size == 1) { // why unpack the tuple of 1 component? 2332 // arguments = (Expression) tupleExpression.getExpressions().get(0); 2333 // } 2334 } 2335 2336 // lets check that the type exists 2337 String type = checkValidType(call.getType(), call, "in constructor call"); 2338 2339 //System.out.println("Constructing: " + type); 2340 2341 visitClassExpression(new ClassExpression(type)); 2342 if (arguments !=null) { 2343 arguments.visit(this); 2344 invokeConstructorOfMethod.call(cv); // todo subject to opti 2345 } else { 2346 invokeNoArgumentsConstructorOf.call(cv); // todo subject to opti 2347 } 2348 /* 2349 * cv.visitLdcInsn(type); 2350 * 2351 * arguments.visit(this); 2352 * 2353 * invokeConstructorMethod.call(cv); 2354 */ 2355 } 2356 2357 public void visitPropertyExpression(PropertyExpression expression) { 2358 2359 do { 2360 if (true && ENABLE_EARLY_BINDING) { 2361 expression.resolve(this); 2362 2363 if (!expression.isTypeResolved()) { 2364 break; 2365 } 2366 Expression ownerExp = expression.getObjectExpression(); 2367 String propName = expression.getProperty(); 2368 if (expression.getProperty().equals("class")) { 2369 break; // the default does the right thing. let it do. 2370 } 2371 2372 2373 String ownerType = ownerExp.getType(); 2374 Class ownerClass = ownerExp.getTypeClass(); 2375 if (ownerType == null || ownerType.length() == 0) { 2376 break; 2377 } 2378 2379 Label l3 = new Label(); 2380 // handle arraylength 2381 if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) { 2382 load(ownerExp); 2383 if (expression.isSafe()) { 2384 helper.dup(); 2385 cv.visitJumpInsn(IFNULL, l3); 2386 } 2387 cast(ownerClass); 2388 cv.visitInsn(ARRAYLENGTH); 2389 helper.quickBoxIfNecessary(int.class); 2390 cv.visitLabel(l3); 2391 return; 2392 } 2393 2394 2395 String propertyType = expression.getType(); 2396 if (propertyType == null || propertyType.length() == 0) { 2397 break; 2398 } 2399 boolean isStatic = expression.isStatic(); 2400 if (!isThisExpression(ownerExp) && GroovyObject.class.isAssignableFrom(ownerExp.getTypeClass())) { 2401 // call other groovy object property via getProperty()/setProperty() 2402 if (!isStatic && ownerExp instanceof ClassExpression) { 2403 if (leftHandExpression) { 2404 cv.visitMethodInsn( 2405 INVOKEVIRTUAL, 2406 BytecodeHelper.getClassInternalName(ownerType), 2407 "setProperty", 2408 BytecodeHelper.getTypeDescription(propertyType)); 2409 } else { 2410 cv.visitMethodInsn( 2411 INVOKEVIRTUAL, 2412 BytecodeHelper.getClassInternalName(ownerType), 2413 "getProperty", 2414 BytecodeHelper.getTypeDescription(propertyType)); 2415 } 2416 return; 2417 } else { 2418 break; 2419 } 2420 } 2421 // else if (isThisExpression(ownerExp)){ 2422 // if (leftHandExpression) { 2423 // helper.loadThis(); 2424 // cv.visitFieldInsn( 2425 // PUTFIELD, 2426 // BytecodeHelper.getClassInternalName(ownerType), 2427 // expression.getProperty(), 2428 // BytecodeHelper.getClassInternalName(propertyType)); 2429 // } else { 2430 // cv.visitMethodInsn( 2431 // INVOKEVIRTUAL, 2432 // BytecodeHelper.getClassInternalName(ownerType), 2433 // "getProperty", 2434 // BytecodeHelper.getClassInternalName(propertyType)); 2435 // } 2436 // return; 2437 // } 2438 2439 // the following logic is used for this.<prop> acess too. 2440 else { // none direct local access 2441 Field fld = expression.getField(); 2442 Method setter = expression.getSetter(); 2443 Method getter = expression.getGetter(); 2444 2445 // gate keeping 2446 if (leftHandExpression) { 2447 if (fld == null && setter == null) { 2448 break; 2449 } 2450 } 2451 else { 2452 if (fld == null && getter == null) { 2453 break; 2454 } 2455 } 2456 2457 if (ownerClass == null && !isThisExpression(ownerExp)) { 2458 break; // ownerClass is null only when the ownerExp is "this" 2459 } 2460 // now looking for public fields before accessors 2461 2462 2463 if (expression.isStatic()) { 2464 if (leftHandExpression) { 2465 if (fld != null) { 2466 helper.quickUnboxIfNecessary(expression.getTypeClass()); 2467 cv.visitFieldInsn( 2468 PUTSTATIC, 2469 BytecodeHelper.getClassInternalName(ownerType), 2470 expression.getProperty(), 2471 BytecodeHelper.getTypeDescription(propertyType) 2472 ); 2473 } 2474 else if (setter != null) { 2475 helper.quickUnboxIfNecessary(setter.getParameterTypes()[0]); 2476 cast(setter.getParameterTypes()[0]); 2477 helper.invoke(setter); 2478 } 2479 else { 2480 throwException("no method or field is found for a resolved property access"); 2481 } 2482 } 2483 else { // get the property 2484 if (fld != null){ 2485 cv.visitFieldInsn( 2486 GETSTATIC, 2487 BytecodeHelper.getClassInternalName(ownerType), 2488 propName, 2489 BytecodeHelper.getTypeDescription(propertyType) 2490 ); 2491 helper.quickBoxIfNecessary(expression.getTypeClass()); 2492 } 2493 else if (getter != null) { 2494 helper.invoke(getter); 2495 helper.quickBoxIfNecessary(expression.getTypeClass()); 2496 } 2497 else { 2498 throwException("no method or field is found for a resolved property access"); 2499 } 2500 } 2501 } else { // non-static access 2502 if (leftHandExpression) { // set the property 2503 // assumption: the data on the stack are boxed if it's a number 2504 helper.quickUnboxIfNecessary(expression.getTypeClass()); 2505 load(ownerExp); 2506 if (expression.isSafe()) { 2507 helper.dup(); 2508 cv.visitJumpInsn(IFNULL, l3); 2509 } 2510 2511 if (ownerClass != null) 2512 cast(ownerClass); 2513 Class cls = expression.getTypeClass(); 2514 if (cls == double.class || cls == long.class) { 2515 cv.visitInsn(DUP_X2); 2516 cv.visitInsn(POP); 2517 } else { 2518 cv.visitInsn(SWAP); 2519 } 2520 2521 if (fld != null) { 2522 cv.visitFieldInsn( 2523 PUTFIELD, 2524 BytecodeHelper.getClassInternalName(ownerType), 2525 propName, 2526 BytecodeHelper.getTypeDescription(propertyType) 2527 ); 2528 } 2529 else if (setter != null) { 2530 Method m = setter; 2531 Class[] paramTypes = m.getParameterTypes(); 2532 if (paramTypes.length != 1) { 2533 throw new RuntimeException("setter should take a single parameter"); 2534 } 2535 Class paramType = paramTypes[0]; 2536 cast(paramType); 2537 helper.invoke(setter); 2538 } 2539 else { 2540 throwException("no method or field is found for a resolved property access"); 2541 } 2542 } 2543 else { // get property 2544 load(ownerExp); 2545 if (expression.isSafe()) { 2546 helper.dup(); 2547 cv.visitJumpInsn(IFNULL, l3); 2548 } 2549 if (ownerClass != null) 2550 cast(ownerClass); 2551 if (fld != null) { 2552 cv.visitFieldInsn( 2553 GETFIELD, 2554 BytecodeHelper.getClassInternalName(ownerType), 2555 propName, 2556 BytecodeHelper.getTypeDescription(propertyType) 2557 ); 2558 helper.quickBoxIfNecessary(expression.getTypeClass()); 2559 } 2560 else if (getter != null) { 2561 helper.invoke(getter); 2562 helper.quickBoxIfNecessary(expression.getTypeClass()); 2563 } 2564 else { 2565 throwException("no method or field is found for a resolved property access"); 2566 } 2567 } 2568 } 2569 cv.visitLabel(l3); 2570 return; 2571 } 2572 } 2573 } while (false); 2574 2575 // lets check if we're a fully qualified class name 2576 String className = null; 2577 Expression objectExpression = expression.getObjectExpression(); 2578 if (!isThisExpression(objectExpression)) { 2579 className = checkForQualifiedClass(expression); 2580 if (className != null) { 2581 visitClassExpression(new ClassExpression(className)); 2582 return; 2583 } 2584 } 2585 if (expression.getProperty().equals("class")) { 2586 if ((objectExpression instanceof ClassExpression)) { 2587 visitClassExpression((ClassExpression) objectExpression); 2588 return; 2589 } 2590 else if (objectExpression instanceof VariableExpression) { 2591 VariableExpression varExp = (VariableExpression) objectExpression; 2592 className = varExp.getVariable(); 2593 try { 2594 className = resolveClassName(className); 2595 visitClassExpression(new ClassExpression(className)); 2596 return; 2597 } 2598 catch (Exception e) { 2599 // ignore 2600 } 2601 } 2602 } 2603 2604 if (isThisExpression(objectExpression)) { 2605 // lets use the field expression if its available 2606 String name = expression.getProperty(); 2607 FieldNode field = classNode.getField(name); 2608 if (field != null) { 2609 visitFieldExpression(new FieldExpression(field)); 2610 return; 2611 } 2612 } 2613 2614 // we need to clear the LHS flag to avoid "this." evaluating as ASTORE 2615 // rather than ALOAD 2616 boolean left = leftHandExpression; 2617 leftHandExpression = false; 2618 objectExpression.visit(this); 2619 leftHandExpression = left; 2620 2621 cv.visitLdcInsn(expression.getProperty()); 2622 2623 if (isGroovyObject(objectExpression) && ! expression.isSafe()) { 2624 if (left) { 2625 setGroovyObjectPropertyMethod.call(cv); 2626 } 2627 else { 2628 getGroovyObjectPropertyMethod.call(cv); 2629 } 2630 } 2631 else { 2632 if (expression.isSafe()) { 2633 if (left) { 2634 setPropertySafeMethod2.call(cv); 2635 } 2636 else { 2637 if (expression.isSpreadSafe()) { 2638 getPropertySpreadSafeMethod.call(cv); 2639 } 2640 else { 2641 getPropertySafeMethod.call(cv); 2642 } 2643 } 2644 } 2645 else { 2646 if (left) { 2647 setPropertyMethod2.call(cv); 2648 } 2649 else { 2650 getPropertyMethod.call(cv); 2651 } 2652 } 2653 } 2654 } 2655 2656 public void visitAttributeExpression(AttributeExpression expression) { 2657 Expression objectExpression = expression.getObjectExpression(); 2658 if (isThisExpression(objectExpression)) { 2659 // lets use the field expression if its available 2660 String name = expression.getProperty(); 2661 FieldNode field = classNode.getField(name); 2662 if (field != null) { 2663 visitFieldExpression(new FieldExpression(field)); 2664 return; 2665 } 2666 } 2667 2668 // we need to clear the LHS flag to avoid "this." evaluating as ASTORE 2669 // rather than ALOAD 2670 boolean left = leftHandExpression; 2671 leftHandExpression = false; 2672 objectExpression.visit(this); 2673 leftHandExpression = left; 2674 2675 cv.visitLdcInsn(expression.getProperty()); 2676 2677 if (expression.isSafe()) { 2678 if (left) { 2679 setAttributeSafeMethod2.call(cv); 2680 } 2681 else { 2682 if (expression.isSpreadSafe()) { 2683 getAttributeSpreadSafeMethod.call(cv); 2684 } 2685 else { 2686 getAttributeSafeMethod.call(cv); 2687 } 2688 } 2689 } 2690 else { 2691 if (left) { 2692 setAttributeMethod2.call(cv); 2693 } 2694 else { 2695 getAttributeMethod.call(cv); 2696 } 2697 } 2698 } 2699 2700 protected boolean isGroovyObject(Expression objectExpression) { 2701 return isThisExpression(objectExpression); 2702 } 2703 2704 /** 2705 * Checks if the given property expression represents a fully qualified class name 2706 * @return the class name or null if the property is not a valid class name 2707 */ 2708 protected String checkForQualifiedClass(PropertyExpression expression) { 2709 String text = expression.getText(); 2710 if (text != null && text.endsWith(".class")) { 2711 text = text.substring(0, text.length() - 6); 2712 } 2713 try { 2714 return resolveClassName(text); 2715 } 2716 catch (Exception e) { 2717 return null; 2718 } 2719 } 2720 2721 public void visitFieldExpression(FieldExpression expression) { 2722 FieldNode field = expression.getField(); 2723 2724 2725 if (field.isStatic()) { 2726 if (leftHandExpression) { 2727 storeStaticField(expression); 2728 } 2729 else { 2730 loadStaticField(expression); 2731 } 2732 } else { 2733 if (leftHandExpression) { 2734 storeThisInstanceField(expression); 2735 } 2736 else { 2737 loadInstanceField(expression); 2738 } 2739 } 2740 } 2741 2742 /** 2743 * 2744 * @param fldExp 2745 */ 2746 public void loadStaticField(FieldExpression fldExp) { 2747 FieldNode field = fldExp.getField(); 2748 boolean holder = field.isHolder() && !isInClosureConstructor(); 2749 String type = field.getType(); 2750 2751 String ownerName = (field.getOwner().equals(classNode.getName())) 2752 ? internalClassName 2753 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner())); 2754 if (holder) { 2755 cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type)); 2756 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;"); 2757 } 2758 else { 2759 cv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type)); 2760 if (BytecodeHelper.isPrimitiveType(type)) { 2761 helper.box(type); 2762 } else { 2763 } 2764 } 2765 } 2766 2767 /** 2768 * RHS instance field. should move most of the code in the BytecodeHelper 2769 * @param fldExp 2770 */ 2771 public void loadInstanceField(FieldExpression fldExp) { 2772 FieldNode field = fldExp.getField(); 2773 boolean holder = field.isHolder() && !isInClosureConstructor(); 2774 String type = field.getType(); 2775 String ownerName = (field.getOwner().equals(classNode.getName())) 2776 ? internalClassName 2777 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner())); 2778 2779 cv.visitVarInsn(ALOAD, 0); 2780 cv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type)); 2781 2782 if (holder) { 2783 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;"); 2784 } else { 2785 if (BytecodeHelper.isPrimitiveType(type)) { 2786 helper.box(type); 2787 } else { 2788 } 2789 } 2790 } 2791 2792 public void storeThisInstanceField(FieldExpression expression) { 2793 FieldNode field = expression.getField(); 2794 2795 boolean holder = field.isHolder() && !isInClosureConstructor(); 2796 String type = field.getType(); 2797 2798 String ownerName = (field.getOwner().equals(classNode.getName())) ? 2799 internalClassName : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner())); 2800 if (holder) { 2801 Variable tv = visitASTOREInTemp(field.getName()); 2802 int tempIndex = tv.getIndex(); 2803 cv.visitVarInsn(ALOAD, 0); 2804 cv.visitFieldInsn(GETFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type)); 2805 cv.visitVarInsn(ALOAD, tempIndex); 2806 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V"); 2807 removeVar(tv); 2808 } 2809 else { 2810 if (isInClosureConstructor()) { 2811 helper.doCast(type); 2812 } 2813 else { 2814 if (ENABLE_EARLY_BINDING) { 2815 helper.doCast(type); 2816 } 2817 else { 2818 // this may be superfluous 2819 doConvertAndCast(type); 2820 } 2821 } 2822 //Variable tmpVar = defineVariable(createVariableName(field.getName()), "java.lang.Object", false); 2823 Variable tmpVar = defineVariable(createVariableName(field.getName()), field.getType(), false); 2824 //int tempIndex = tmpVar.getIndex(); 2825 //helper.store(field.getType(), tempIndex); 2826 helper.store(tmpVar, MARK_START); 2827 helper.loadThis(); //cv.visitVarInsn(ALOAD, 0); 2828 helper.load(tmpVar); 2829 helper.putField(field, ownerName); 2830 //cv.visitFieldInsn(PUTFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type)); 2831 // let's remove the temp var 2832 removeVar(tmpVar); 2833 } 2834 } 2835 2836 2837 public void storeStaticField(FieldExpression expression) { 2838 FieldNode field = expression.getField(); 2839 2840 boolean holder = field.isHolder() && !isInClosureConstructor(); 2841 2842 String type = field.getType(); 2843 2844 String ownerName = (field.getOwner().equals(classNode.getName())) 2845 ? internalClassName 2846 : org.objectweb.asm.Type.getInternalName(loadClass(field.getOwner())); 2847 if (holder) { 2848 Variable tv = visitASTOREInTemp(field.getName()); 2849 int tempIndex = tv.getIndex(); 2850 cv.visitFieldInsn(GETSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type)); 2851 cv.visitVarInsn(ALOAD, tempIndex); 2852 cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V"); 2853 removeVar(tv); 2854 } 2855 else { 2856 if (isInClosureConstructor()) { 2857 helper.doCast(type); 2858 } 2859 else { 2860 if (ENABLE_EARLY_BINDING) { 2861 helper.doCast(type); 2862 } 2863 else { 2864 // this may be superfluous 2865 //doConvertAndCast(type); 2866 // use weaker cast 2867 helper.doCast(type); 2868 } 2869 } 2870 cv.visitFieldInsn(PUTSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type)); 2871 } 2872 } 2873 2874 protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first ) { 2875 FieldNode field = expression.getField(); 2876 boolean isStatic = field.isStatic(); 2877 2878 Variable fieldTemp = defineVariable(createVariableName(field.getName()), "java.lang.Object", false); 2879 int valueIdx = fieldTemp.getIndex(); 2880 2881 if (leftHandExpression && first) { 2882 cv.visitVarInsn(ASTORE, valueIdx); 2883 visitVariableStartLabel(fieldTemp); 2884 } 2885 2886 if (steps > 1 || !isStatic) { 2887 cv.visitVarInsn(ALOAD, 0); 2888 cv.visitFieldInsn( 2889 GETFIELD, 2890 internalClassName, 2891 "owner", 2892 BytecodeHelper.getTypeDescription(outerClassNode.getName())); 2893 } 2894 2895 if( steps == 1 ) { 2896 int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD); 2897 String ownerName = BytecodeHelper.getClassInternalName(outerClassNode.getName()); 2898 2899 if (leftHandExpression) { 2900 cv.visitVarInsn(ALOAD, valueIdx); 2901 boolean holder = field.isHolder() && !isInClosureConstructor(); 2902 if ( !holder) { 2903 doConvertAndCast(field.getType()); 2904 } 2905 } 2906 cv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType())); 2907 if (!leftHandExpression) { 2908 if (BytecodeHelper.isPrimitiveType(field.getType())) { 2909 helper.box(field.getType()); 2910 } 2911 } 2912 } 2913 2914 else { 2915 visitOuterFieldExpression( expression, outerClassNode.getOuterClass(), steps - 1, false ); 2916 } 2917 } 2918 2919 2920 2921 /** 2922 * Visits a bare (unqualified) variable expression. 2923 */ 2924 2925 public void visitVariableExpression(VariableExpression expression) { 2926 2927 String variableName = expression.getVariable(); 2928 2929 //----------------------------------------------------------------------- 2930 // SPECIAL CASES 2931 2932 // 2933 // "this" for static methods is the Class instance 2934 2935 if (isStaticMethod() && variableName.equals("this")) { 2936 visitClassExpression(new ClassExpression(classNode.getName())); 2937 return; // <<< FLOW CONTROL <<<<<<<<< 2938 } 2939 2940 // 2941 // "super" also requires special handling 2942 2943 if (variableName.equals("super")) { 2944 visitClassExpression(new ClassExpression(classNode.getSuperClass())); 2945 return; // <<< FLOW CONTROL <<<<<<<<< 2946 } 2947 2948 2949 // 2950 // class names return a Class instance, too 2951 2952 // if (!variableName.equals("this")) { 2953 // String className = resolveClassName(variableName); 2954 // if (className != null) { 2955 // if (leftHandExpression) { 2956 // throw new RuntimeParserException( 2957 // "Cannot use a class expression on the left hand side of an assignment", 2958 // expression); 2959 // } 2960 // visitClassExpression(new ClassExpression(className)); 2961 // return; // <<< FLOW CONTROL <<<<<<<<< 2962 // } 2963 // } 2964 2965 2966 //----------------------------------------------------------------------- 2967 // GENERAL VARIABLE LOOKUP 2968 2969 2970 // 2971 // We are handling only unqualified variables here. Therefore, 2972 // we do not care about accessors, because local access doesn't 2973 // go through them. Therefore, precedence is as follows: 2974 // 1) local variables, nearest block first 2975 // 2) class fields 2976 // 3) repeat search from 2) in next outer class 2977 2978 boolean handled = false; 2979 Variable variable = (Variable)variableStack.get( variableName ); 2980 2981 if( variable != null ) { 2982 2983 if( variable.isProperty() ) { 2984 processPropertyVariable(variable ); 2985 } 2986 else { 2987 if (ENABLE_EARLY_BINDING && expression.isTypeResolved() && leftHandExpression) { 2988 // let's pass the type back to the variable 2989 String typeName = expression.getType(); 2990 Type varOldType = variable.getType(); 2991 if (varOldType.isDynamic()) { 2992 variable.setType(new Type(typeName, true)); 2993 } 2994 else if (!varOldType.getName().equals(typeName)){ 2995 throw new GroovyRuntimeException("VariableExpression data type conflicts with the existing variable. " 2996 + "[" + expression.getLineNumber() + ":" + expression.getColumnNumber() + "]"); 2997 } 2998 } 2999 processStackVariable(variable ); 3000 } 3001 3002 handled = true; 3003 } else { 3004 // 3005 // Loop through outer classes for fields 3006 3007 int steps = 0; 3008 ClassNode currentClassNode = classNode; 3009 FieldNode field = null; 3010 3011 do { 3012 if( (field = currentClassNode.getField(variableName)) != null ) { 3013 if (methodNode == null || !methodNode.isStatic() || field.isStatic() ) 3014 break; //this is a match. break out. todo to be tested 3015 } 3016 steps++; 3017 3018 } while( (currentClassNode = currentClassNode.getOuterClass()) != null ); 3019 3020 if( field != null ) { 3021 processFieldAccess( variableName, field, steps ); 3022 handled = true; 3023 } 3024 } 3025 3026 // 3027 // class names return a Class instance, too 3028 if (!handled && !variableName.equals("this")) { 3029 String className = resolveClassName(variableName); 3030 if (className != null) { 3031 if (leftHandExpression) { 3032 throwException("The variable name '"+variableName+"' conflicts with the class name '"+className+"'. Please use another variable name"); 3033 } 3034 visitClassExpression(new ClassExpression(className)); 3035 return; // <<< FLOW CONTROL <<<<<<<<< 3036 } 3037 } 3038 3039 // 3040 // Finally, if unhandled, create a variable for it. 3041 // Except there a stack variable should be created, 3042 // we define the variable as a property accessor and 3043 // let other parts of the classgen report the error 3044 // if the property doesn't exist. 3045 3046 if( !handled ) { 3047 String variableType = expression.getType(); 3048 variable = defineVariable( variableName, variableType ); 3049 3050 if (leftHandExpression && expression.isDynamic()) { 3051 variable.setDynamic(true); // false by default 3052 } 3053 else { 3054 variable.setDynamic(false); 3055 } 3056 3057 if( isInScriptBody() || !leftHandExpression ) { // todo problematic: if on right hand not defined, should I report undefined var error? 3058 variable.setProperty( true ); 3059 processPropertyVariable(variable ); 3060 } 3061 else { 3062 processStackVariable(variable ); 3063 } 3064 } 3065 } 3066 3067 3068 protected void processStackVariable(Variable variable ) { 3069 boolean holder = variable.isHolder() && !passingClosureParams; 3070 3071 if( leftHandExpression ) { 3072 helper.storeVar(variable, holder); 3073 } 3074 else { 3075 helper.loadVar(variable, holder); 3076 } 3077 if (ASM_DEBUG) { 3078 helper.mark("var: " + variable.getName()); 3079 } 3080 } 3081 3082 private void visitVariableStartLabel(Variable variable) { 3083 if (CREATE_DEBUG_INFO) { 3084 Label l = variable.getStartLabel(); 3085 if (l != null) { 3086 cv.visitLabel(l); 3087 } else { 3088 System.out.println("start label == null! what to do about this?"); 3089 } 3090 } 3091 } 3092 3093 protected void processPropertyVariable(Variable variable ) { 3094 String name = variable.getName(); 3095 if (variable.isHolder() && passingClosureParams && isInScriptBody() ) { 3096 // lets create a ScriptReference to pass into the closure 3097 cv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference"); 3098 cv.visitInsn(DUP); 3099 3100 loadThisOrOwner(); 3101 cv.visitLdcInsn(name); 3102 3103 cv.visitMethodInsn( 3104 INVOKESPECIAL, 3105 "org/codehaus/groovy/runtime/ScriptReference", 3106 "<init>", 3107 "(Lgroovy/lang/Script;Ljava/lang/String;)V"); 3108 } 3109 else { 3110 visitPropertyExpression(new PropertyExpression(VariableExpression.THIS_EXPRESSION, name)); 3111 } 3112 } 3113 3114 3115 protected void processFieldAccess( String name, FieldNode field, int steps ) { 3116 FieldExpression expression = new FieldExpression(field); 3117 3118 if( steps == 0 ) { 3119 visitFieldExpression( expression ); 3120 } 3121 else { 3122 visitOuterFieldExpression( expression, classNode.getOuterClass(), steps, true ); 3123 } 3124 } 3125 3126 3127 3128 /** 3129 * @return true if we are in a script body, where all variables declared are no longer 3130 * local variables but are properties 3131 */ 3132 protected boolean isInScriptBody() { 3133 if (classNode.isScriptBody()) { 3134 return true; 3135 } 3136 else { 3137 return classNode.isScript() && methodNode != null && methodNode.getName().equals("run"); 3138 } 3139 } 3140 3141 /** 3142 * @return true if this expression will have left a value on the stack 3143 * that must be popped 3144 */ 3145 protected boolean isPopRequired(Expression expression) { 3146 if (expression instanceof MethodCallExpression) { 3147 if (expression.getType() != null && expression.getType().equals("void")) { // nothing on the stack 3148 return false; 3149 } else { 3150 return !MethodCallExpression.isSuperMethodCall((MethodCallExpression) expression); 3151 } 3152 } 3153 if (expression instanceof BinaryExpression) { 3154 BinaryExpression binExp = (BinaryExpression) expression; 3155 switch (binExp.getOperation().getType()) { // br todo should leave a copy of the value on the stack for all the assignemnt. 3156 // case Types.EQUAL : // br a copy of the right value is left on the stack (see evaluateEqual()) so a pop is required for a standalone assignment 3157 // case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAsignment() 3158 // case Types.MINUS_EQUAL : 3159 // case Types.MULTIPLY_EQUAL : 3160 // case Types.DIVIDE_EQUAL : 3161 // case Types.INTDIV_EQUAL : 3162 // case Types.MOD_EQUAL : 3163 // return false; 3164 } 3165 } 3166 return true; 3167 } 3168 3169 protected boolean firstStatementIsSuperInit(Statement code) { 3170 ExpressionStatement expStmt = null; 3171 if (code instanceof ExpressionStatement) { 3172 expStmt = (ExpressionStatement) code; 3173 } 3174 else if (code instanceof BlockStatement) { 3175 BlockStatement block = (BlockStatement) code; 3176 if (!block.getStatements().isEmpty()) { 3177 Object expr = block.getStatements().get(0); 3178 if (expr instanceof ExpressionStatement) { 3179 expStmt = (ExpressionStatement) expr; 3180 } 3181 } 3182 } 3183 if (expStmt != null) { 3184 Expression expr = expStmt.getExpression(); 3185 if (expr instanceof MethodCallExpression) { 3186 MethodCallExpression call = (MethodCallExpression) expr; 3187 if (MethodCallExpression.isSuperMethodCall(call)) { 3188 // not sure which one is constantly used as the super class ctor call. To cover both for now 3189 return call.getMethod().equals("<init>") || call.getMethod().equals("super"); 3190 } 3191 } 3192 } 3193 return false; 3194 } 3195 3196 protected void createSyntheticStaticFields() { 3197 for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) { 3198 String staticFieldName = (String) iter.next(); 3199 // generate a field node 3200 cw.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null); 3201 } 3202 3203 if (!syntheticStaticFields.isEmpty()) { 3204 cv = 3205 cw.visitMethod( 3206 ACC_STATIC + ACC_SYNTHETIC, 3207 "class$", 3208 "(Ljava/lang/String;)Ljava/lang/Class;", 3209 null, 3210 null); 3211 helper = new BytecodeHelper(cv); 3212 3213 Label l0 = new Label(); 3214 cv.visitLabel(l0); 3215 cv.visitVarInsn(ALOAD, 0); 3216 cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); 3217 Label l1 = new Label(); 3218 cv.visitLabel(l1); 3219 cv.visitInsn(ARETURN); 3220 Label l2 = new Label(); 3221 cv.visitLabel(l2); 3222 cv.visitVarInsn(ASTORE, 1); 3223 cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError"); 3224 cv.visitInsn(DUP); 3225 cv.visitVarInsn(ALOAD, 1); 3226 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;"); 3227 cv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V"); 3228 cv.visitInsn(ATHROW); 3229 cv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry 3230 cv.visitMaxs(3, 2); 3231 3232 cw.visitEnd(); 3233 } 3234 } 3235 /** load class object on stack */ 3236 public void visitClassExpression(ClassExpression expression) { 3237 String type = expression.getText(); 3238 //type = checkValidType(type, expression, "Must be a valid type name for a constructor call"); 3239 3240 3241 if (BytecodeHelper.isPrimitiveType(type)) { 3242 String objectType = BytecodeHelper.getObjectTypeForPrimitive(type); 3243 cv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;"); 3244 } 3245 else { 3246 final String staticFieldName = 3247 (type.equals(classNode.getName())) ? "class$0" : "class$" + type.replace('.', '$').replace('[', '_').replace(';', '_'); 3248 3249 syntheticStaticFields.add(staticFieldName); 3250 3251 cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;"); 3252 Label l0 = new Label(); 3253 cv.visitJumpInsn(IFNONNULL, l0); 3254 cv.visitLdcInsn(type); 3255 cv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;"); 3256 cv.visitInsn(DUP); 3257 cv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;"); 3258 Label l1 = new Label(); 3259 cv.visitJumpInsn(GOTO, l1); 3260 cv.visitLabel(l0); 3261 cv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;"); 3262 cv.visitLabel(l1); 3263 } 3264 } 3265 3266 public void visitRangeExpression(RangeExpression expression) { 3267 leftHandExpression = false; 3268 expression.getFrom().visit(this); 3269 3270 leftHandExpression = false; 3271 expression.getTo().visit(this); 3272 3273 helper.pushConstant(expression.isInclusive()); 3274 3275 createRangeMethod.call(cv); 3276 } 3277 3278 public void visitMapEntryExpression(MapEntryExpression expression) { 3279 } 3280 3281 public void visitMapExpression(MapExpression expression) { 3282 List entries = expression.getMapEntryExpressions(); 3283 int size = entries.size(); 3284 helper.pushConstant(size * 2); 3285 3286 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 3287 3288 int i = 0; 3289 for (Iterator iter = entries.iterator(); iter.hasNext();) { 3290 Object object = iter.next(); 3291 MapEntryExpression entry = (MapEntryExpression) object; 3292 3293 cv.visitInsn(DUP); 3294 helper.pushConstant(i++); 3295 visitAndAutoboxBoolean(entry.getKeyExpression()); 3296 cv.visitInsn(AASTORE); 3297 3298 cv.visitInsn(DUP); 3299 helper.pushConstant(i++); 3300 visitAndAutoboxBoolean(entry.getValueExpression()); 3301 cv.visitInsn(AASTORE); 3302 } 3303 createMapMethod.call(cv); 3304 } 3305 3306 public void visitTupleExpression(TupleExpression expression) { 3307 int size = expression.getExpressions().size(); 3308 3309 helper.pushConstant(size); 3310 3311 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 3312 3313 for (int i = 0; i < size; i++) { 3314 cv.visitInsn(DUP); 3315 helper.pushConstant(i); 3316 visitAndAutoboxBoolean(expression.getExpression(i)); 3317 cv.visitInsn(AASTORE); 3318 } 3319 //createTupleMethod.call(cv); 3320 } 3321 3322 public void visitArrayExpression(ArrayExpression expression) { 3323 String type = expression.getElementType(); 3324 if (type!=null && type.endsWith("[]")) type = type.substring(0,type.length()-2); 3325 String typeName = BytecodeHelper.getClassInternalName(type); 3326 Expression sizeExpression = expression.getSizeExpression(); 3327 3328 int size=0; 3329 if (sizeExpression != null) { 3330 // lets convert to an int 3331 visitAndAutoboxBoolean(sizeExpression); 3332 asIntMethod.call(cv); 3333 } else { 3334 size = expression.getExpressions().size(); 3335 helper.pushConstant(size); 3336 } 3337 3338 int storeIns=AASTORE; 3339 if (BytecodeHelper.isPrimitiveType(type)) { 3340 int primType=0; 3341 if (type.equals("boolean")) { 3342 primType = T_BOOLEAN; 3343 storeIns = BASTORE; 3344 } else if (type.equals("char")) { 3345 primType = T_CHAR; 3346 storeIns = CASTORE; 3347 } else if (type.equals("float")) { 3348 primType = T_FLOAT; 3349 storeIns = FASTORE; 3350 } else if (type.equals("double")) { 3351 primType = T_DOUBLE; 3352 storeIns = DASTORE; 3353 } else if (type.equals("byte")) { 3354 primType = T_BYTE; 3355 storeIns = BASTORE; 3356 } else if (type.equals("short")) { 3357 primType = T_SHORT; 3358 storeIns = SASTORE; 3359 } else if (type.equals("int")) { 3360 primType = T_INT; 3361 storeIns=IASTORE; 3362 } else if (type.equals("long")) { 3363 primType = T_LONG; 3364 storeIns = LASTORE; 3365 } 3366 cv.visitIntInsn(NEWARRAY, primType); 3367 } else { 3368 cv.visitTypeInsn(ANEWARRAY, typeName); 3369 } 3370 3371 for (int i = 0; i < size; i++) { 3372 cv.visitInsn(DUP); 3373 helper.pushConstant(i); 3374 Expression elementExpression = expression.getExpression(i); 3375 if (elementExpression == null) { 3376 ConstantExpression.NULL.visit(this); 3377 } else { 3378 if (!type.equals(elementExpression.getClass().getName())) { 3379 visitCastExpression(new CastExpression(type, elementExpression, true)); 3380 } else { 3381 visitAndAutoboxBoolean(elementExpression); 3382 } 3383 } 3384 cv.visitInsn(storeIns); 3385 } 3386 3387 if (BytecodeHelper.isPrimitiveType(type)) { 3388 int par = defineVariable("par","java.lang.Object").getIndex(); 3389 cv.visitVarInsn(ASTORE, par); 3390 cv.visitVarInsn(ALOAD, par); 3391 } 3392 } 3393 3394 public void visitListExpression(ListExpression expression) { 3395 int size = expression.getExpressions().size(); 3396 helper.pushConstant(size); 3397 3398 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 3399 3400 for (int i = 0; i < size; i++) { 3401 cv.visitInsn(DUP); 3402 helper.pushConstant(i); 3403 visitAndAutoboxBoolean(expression.getExpression(i)); 3404 cv.visitInsn(AASTORE); 3405 } 3406 createListMethod.call(cv); 3407 } 3408 3409 public void visitGStringExpression(GStringExpression expression) { 3410 int size = expression.getValues().size(); 3411 helper.pushConstant(size); 3412 3413 cv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 3414 3415 for (int i = 0; i < size; i++) { 3416 cv.visitInsn(DUP); 3417 helper.pushConstant(i); 3418 visitAndAutoboxBoolean(expression.getValue(i)); 3419 cv.visitInsn(AASTORE); 3420 } 3421 3422 Variable tv = visitASTOREInTemp("iterator"); 3423 int paramIdx = tv.getIndex(); 3424 3425 ClassNode innerClass = createGStringClass(expression); 3426 addInnerClass(innerClass); 3427 String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass.getName()); 3428 3429 cv.visitTypeInsn(NEW, innerClassinternalName); 3430 cv.visitInsn(DUP); 3431 cv.visitVarInsn(ALOAD, paramIdx); 3432 3433 cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", "([Ljava/lang/Object;)V"); 3434 removeVar(tv); 3435 } 3436 3437 private Variable visitASTOREInTemp(String s) { 3438 return storeInTemp(s, "java.lang.Object"); 3439 } 3440 3441 // Implementation methods 3442 //------------------------------------------------------------------------- 3443 protected boolean addInnerClass(ClassNode innerClass) { 3444 innerClass.setModule(classNode.getModule()); 3445 return innerClasses.add(innerClass); 3446 } 3447 3448 protected ClassNode createClosureClass(ClosureExpression expression) { 3449 ClassNode owner = getOutermostClass(); 3450 boolean parentIsInnerClass = owner instanceof InnerClassNode; 3451 String outerClassName = owner.getName(); 3452 String name = outerClassName + "$" 3453 + context.getNextClosureInnerName(owner, classNode, methodNode); // br added a more infomative name 3454 boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass(); 3455 if (staticMethodOrInStaticClass) { 3456 outerClassName = "java.lang.Class"; 3457 } 3458 Parameter[] parameters = expression.getParameters(); 3459 if (parameters == null || parameters.length == 0) { 3460 // lets create a default 'it' parameter 3461 parameters = new Parameter[] { new Parameter("java.lang.Object", "it", ConstantExpression.NULL)}; 3462 } 3463 3464 Parameter[] localVariableParams = getClosureSharedVariables(expression); 3465 3466 InnerClassNode answer = new InnerClassNode(owner, name, 0, "groovy.lang.Closure"); // clsures are local inners and not public 3467 answer.setEnclosingMethod(this.methodNode); 3468 answer.setSynthetic(true); 3469 3470 if (staticMethodOrInStaticClass) { 3471 answer.setStaticClass(true); 3472 } 3473 if (isInScriptBody()) { 3474 answer.setScriptBody(true); 3475 } 3476 MethodNode method = 3477 answer.addMethod("doCall", ACC_PUBLIC, "java.lang.Object", parameters, expression.getCode()); 3478 3479 method.setLineNumber(expression.getLineNumber()); 3480 method.setColumnNumber(expression.getColumnNumber()); 3481 3482 VariableScope varScope = expression.getVariableScope(); 3483 if (varScope == null) { 3484 throw new RuntimeException( 3485 "Must have a VariableScope by now! for expression: " + expression + " class: " + name); 3486 } 3487 else { 3488 method.setVariableScope(varScope); 3489 } 3490 if (parameters.length > 1 3491 || (parameters.length == 1 3492 && parameters[0].getType() != null 3493 && !parameters[0].getType().equals("java.lang.Object"))) { 3494 3495 // lets add a typesafe call method 3496 answer.addMethod( 3497 "call", 3498 ACC_PUBLIC, 3499 "java.lang.Object", 3500 parameters, 3501 new ReturnStatement( 3502 new MethodCallExpression( 3503 VariableExpression.THIS_EXPRESSION, 3504 "doCall", 3505 new ArgumentListExpression(parameters)))); 3506 } 3507 3508 FieldNode ownerField = answer.addField("owner", ACC_PRIVATE, outerClassName, null); 3509 3510 // lets make the constructor 3511 BlockStatement block = new BlockStatement(); 3512 block.addStatement( 3513 new ExpressionStatement( 3514 new MethodCallExpression( 3515 new VariableExpression("super"), 3516 "<init>", 3517 new VariableExpression("_outerInstance")))); 3518 block.addStatement( 3519 new ExpressionStatement( 3520 new BinaryExpression( 3521 new FieldExpression(ownerField), 3522 Token.newSymbol(Types.EQUAL, -1, -1), 3523 new VariableExpression("_outerInstance")))); 3524 3525 // lets assign all the parameter fields from the outer context 3526 for (int i = 0; i < localVariableParams.length; i++) { 3527 Parameter param = localVariableParams[i]; 3528 String paramName = param.getName(); 3529 boolean holder = mutableVars.contains(paramName); 3530 Expression initialValue = null; 3531 String type = param.getType(); 3532 FieldNode paramField = null; 3533 if (holder) { 3534 initialValue = new VariableExpression(paramName); 3535 type = Reference.class.getName(); 3536 param.makeReference(); 3537 paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue); 3538 paramField.setHolder(true); 3539 String realType = param.getRealType(); 3540 String methodName = Verifier.capitalize(paramName); 3541 3542 // lets add a getter & setter 3543 Expression fieldExp = new FieldExpression(paramField); 3544 answer.addMethod( 3545 "get" + methodName, 3546 ACC_PUBLIC, 3547 realType, 3548 Parameter.EMPTY_ARRAY, 3549 new ReturnStatement(fieldExp)); 3550 3551 /* 3552 answer.addMethod( 3553 "set" + methodName, 3554 ACC_PUBLIC, 3555 "void", 3556 new Parameter[] { new Parameter(realType, "__value") }, 3557 new ExpressionStatement( 3558 new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value")))); 3559 */ 3560 } 3561 else { 3562 PropertyNode propertyNode = answer.addProperty(paramName, ACC_PUBLIC, type, initialValue, null, null); 3563 paramField = propertyNode.getField(); 3564 block.addStatement( 3565 new ExpressionStatement( 3566 new BinaryExpression( 3567 new FieldExpression(paramField), 3568 Token.newSymbol(Types.EQUAL, -1, -1), 3569 new VariableExpression(paramName)))); 3570 } 3571 } 3572 3573 Parameter[] params = new Parameter[2 + localVariableParams.length]; 3574 params[0] = new Parameter(outerClassName, "_outerInstance"); 3575 params[1] = new Parameter("java.lang.Object", "_delegate"); 3576 System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length); 3577 3578 answer.addConstructor(ACC_PUBLIC, params, block); 3579 return answer; 3580 } 3581 3582 protected ClassNode getOutermostClass() { 3583 if (outermostClass == null) { 3584 outermostClass = classNode; 3585 while (outermostClass instanceof InnerClassNode) { 3586 outermostClass = outermostClass.getOuterClass(); 3587 } 3588 } 3589 return outermostClass; 3590 } 3591 3592 protected ClassNode createGStringClass(GStringExpression expression) { 3593 ClassNode owner = classNode; 3594 if (owner instanceof InnerClassNode) { 3595 owner = owner.getOuterClass(); 3596 } 3597 String outerClassName = owner.getName(); 3598 String name = outerClassName + "$" + context.getNextInnerClassIdx(); 3599 InnerClassNode answer = new InnerClassNode(owner, name, 0, GString.class.getName()); 3600 answer.setEnclosingMethod(this.methodNode); 3601 FieldNode stringsField = 3602 answer.addField( 3603 "strings", 3604 ACC_PRIVATE /*| ACC_STATIC*/, 3605 "java.lang.String[]", 3606 new ArrayExpression("java.lang.String", expression.getStrings())); 3607 answer.addMethod( 3608 "getStrings", 3609 ACC_PUBLIC, 3610 "java.lang.String[]", 3611 Parameter.EMPTY_ARRAY, 3612 new ReturnStatement(new FieldExpression(stringsField))); 3613 // lets make the constructor 3614 BlockStatement block = new BlockStatement(); 3615 block.addStatement( 3616 new ExpressionStatement( 3617 new MethodCallExpression(new VariableExpression("super"), "<init>", new VariableExpression("values")))); 3618 Parameter[] contructorParams = new Parameter[] { new Parameter("java.lang.Object[]", "values")}; 3619 answer.addConstructor(ACC_PUBLIC, contructorParams, block); 3620 return answer; 3621 } 3622 3623 protected void doConvertAndCast(String type) { 3624 if (!type.equals("java.lang.Object")) { 3625 /** todo should probably support array coercions */ 3626 if (!type.endsWith("[]") && isValidTypeForCast(type)) { 3627 visitClassExpression(new ClassExpression(type)); 3628 asTypeMethod.call(cv); 3629 } 3630 3631 helper.doCast(type); 3632 } 3633 } 3634 3635 protected void evaluateLogicalOrExpression(BinaryExpression expression) { 3636 visitBooleanExpression(new BooleanExpression(expression.getLeftExpression())); 3637 Label l0 = new Label(); 3638 Label l2 = new Label(); 3639 cv.visitJumpInsn(IFEQ, l0); 3640 3641 cv.visitLabel(l2); 3642 3643 visitConstantExpression(ConstantExpression.TRUE); 3644 3645 Label l1 = new Label(); 3646 cv.visitJumpInsn(GOTO, l1); 3647 cv.visitLabel(l0); 3648 3649 visitBooleanExpression(new BooleanExpression(expression.getRightExpression())); 3650 3651 cv.visitJumpInsn(IFNE, l2); 3652 3653 visitConstantExpression(ConstantExpression.FALSE); 3654 cv.visitLabel(l1); 3655 } 3656 3657 // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for 3658 // consistancy. 3659 protected void evaluateLogicalAndExpression(BinaryExpression expression) { 3660 visitBooleanExpression(new BooleanExpression(expression.getLeftExpression())); 3661 Label l0 = new Label(); 3662 cv.visitJumpInsn(IFEQ, l0); 3663 3664 visitBooleanExpression(new BooleanExpression(expression.getRightExpression())); 3665 3666 cv.visitJumpInsn(IFEQ, l0); 3667 3668 visitConstantExpression(ConstantExpression.TRUE); 3669 3670 Label l1 = new Label(); 3671 cv.visitJumpInsn(GOTO, l1); 3672 cv.visitLabel(l0); 3673 3674 visitConstantExpression(ConstantExpression.FALSE); 3675 3676 cv.visitLabel(l1); 3677 } 3678 3679 protected void evaluateBinaryExpression(String method, BinaryExpression expression) { 3680 Expression leftExpression = expression.getLeftExpression(); 3681 leftHandExpression = false; 3682 leftExpression.visit(this); 3683 cv.visitLdcInsn(method); 3684 leftHandExpression = false; 3685 new ArgumentListExpression(new Expression[] { expression.getRightExpression()}).visit(this); 3686 // expression.getRightExpression().visit(this); 3687 invokeMethodMethod.call(cv); 3688 } 3689 3690 protected void evaluateCompareTo(BinaryExpression expression) { 3691 Expression leftExpression = expression.getLeftExpression(); 3692 leftHandExpression = false; 3693 leftExpression.visit(this); 3694 if (isComparisonExpression(leftExpression)) { 3695 helper.boxBoolean(); 3696 } 3697 3698 // if the right hand side is a boolean expression, we need to autobox 3699 Expression rightExpression = expression.getRightExpression(); 3700 rightExpression.visit(this); 3701 if (isComparisonExpression(rightExpression)) { 3702 helper.boxBoolean(); 3703 } 3704 compareToMethod.call(cv); 3705 } 3706 3707 protected void evaluateBinaryExpressionWithAsignment(String method, BinaryExpression expression) { 3708 Expression leftExpression = expression.getLeftExpression(); 3709 if (leftExpression instanceof BinaryExpression) { 3710 BinaryExpression leftBinExpr = (BinaryExpression) leftExpression; 3711 if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) { 3712 // lets replace this assignment to a subscript operator with a 3713 // method call 3714 // e.g. x[5] += 10 3715 // -> (x, [], 5), =, x[5] + 10 3716 // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)]) 3717 3718 MethodCallExpression methodCall = 3719 new MethodCallExpression( 3720 expression.getLeftExpression(), 3721 method, 3722 new ArgumentListExpression(new Expression[] { expression.getRightExpression()})); 3723 3724 Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression()); 3725 3726 visitMethodCallExpression( 3727 new MethodCallExpression( 3728 leftBinExpr.getLeftExpression(), 3729 "putAt", 3730 new ArgumentListExpression(new Expression[] { safeIndexExpr, methodCall }))); 3731 //cv.visitInsn(POP); 3732 return; 3733 } 3734 } 3735 3736 evaluateBinaryExpression(method, expression); 3737 3738 // br to leave a copy of rvalue on the stack. see also isPopRequired() 3739 cv.visitInsn(DUP); 3740 3741 leftHandExpression = true; 3742 evaluateExpression(leftExpression); 3743 leftHandExpression = false; 3744 } 3745 3746 private void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression bin) { 3747 if (ENABLE_EARLY_BINDING && true) { 3748 evalBinaryExp_EarlyBinding(compareMethod, bin); 3749 } 3750 else { 3751 evalBinaryExp_LateBinding(compareMethod, bin); 3752 } 3753 } 3754 3755 protected void evalBinaryExp_LateBinding(MethodCaller compareMethod, BinaryExpression expression) { 3756 Expression leftExp = expression.getLeftExpression(); 3757 Expression rightExp = expression.getRightExpression(); 3758 load(leftExp); 3759 load(rightExp); 3760 compareMethod.call(cv); 3761 } 3762 3763 /** 3764 * note: leave the primitive boolean on staock for comparison expressions. All the result types need to match the 3765 * utility methods in the ScriptBytecodeAdapter. 3766 * @param compareMethod 3767 * @param expression 3768 */ 3769 protected void evalBinaryExp_EarlyBinding(MethodCaller compareMethod, BinaryExpression expression) { 3770 Expression leftExp = expression.getLeftExpression(); 3771 Expression rightExp = expression.getRightExpression(); 3772 3773 expression.resolve(this); 3774 if (expression.isResolveFailed() || expression.getTypeClass() == null){ 3775 evalBinaryExp_LateBinding(compareMethod, expression); 3776 return; 3777 } 3778 else { 3779 Class lclass = leftExp.getTypeClass(); 3780 Class rclass = rightExp.getTypeClass(); 3781 if (lclass == null || rclass == null) { 3782 if ((lclass == null && rclass != null) || (lclass != null && rclass == null)) { 3783 // lets treat special cases: obj == null / obj != null . leave primitive boolean on the stack, which will be boxed by visitAndAutoBox() 3784 if (leftExp == ConstantExpression.NULL && !rclass.isPrimitive() || 3785 rightExp == ConstantExpression.NULL && !lclass.isPrimitive()) { 3786 Expression exp = leftExp == ConstantExpression.NULL? rightExp : leftExp; 3787 int type = expression.getOperation().getType(); 3788 switch (type) { 3789 case Types.COMPARE_EQUAL : 3790 load(exp); 3791 cv.visitInsn(ICONST_1); 3792 cv.visitInsn(SWAP); 3793 Label l1 = new Label(); 3794 cv.visitJumpInsn(IFNULL, l1); 3795 cv.visitInsn(POP); 3796 cv.visitInsn(ICONST_0); 3797 cv.visitLabel(l1); 3798 return; 3799 case Types.COMPARE_NOT_EQUAL : 3800 load(exp); 3801 cv.visitInsn(ICONST_1); 3802 cv.visitInsn(SWAP); 3803 Label l2 = new Label(); 3804 cv.visitJumpInsn(IFNONNULL, l2); 3805 cv.visitInsn(POP); 3806 cv.visitInsn(ICONST_0); 3807 cv.visitLabel(l2); 3808 return; 3809 default: 3810 evalBinaryExp_LateBinding(compareMethod, expression); 3811 return; 3812 } 3813 } 3814 else { 3815 evalBinaryExp_LateBinding(compareMethod, expression); 3816 return; 3817 } 3818 } 3819 else { 3820 evalBinaryExp_LateBinding(compareMethod, expression); 3821 return; 3822 } 3823 } 3824 else if (lclass == String.class && rclass == String.class) { 3825 int type = expression.getOperation().getType(); 3826 switch (type) { 3827 case Types.COMPARE_EQUAL : // == 3828 load(leftExp); cast(String.class); 3829 load(rightExp); cast(String.class); 3830 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z"); 3831 //helper.quickBoxIfNecessary(boolean.class); 3832 return; 3833 case Types.COMPARE_NOT_EQUAL : 3834 load(leftExp);cast(String.class); 3835 load(rightExp); cast(String.class); 3836 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z"); 3837 cv.visitInsn(ICONST_1); 3838 cv.visitInsn(IXOR); 3839 //helper.quickBoxIfNecessary(boolean.class); 3840 return; 3841 case Types.COMPARE_TO : 3842 load(leftExp);cast(String.class); 3843 load(rightExp); cast(String.class); 3844 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I"); 3845 helper.quickBoxIfNecessary(int.class); // object type 3846 return; 3847 case Types.COMPARE_GREATER_THAN : 3848 case Types.COMPARE_GREATER_THAN_EQUAL : 3849 case Types.COMPARE_LESS_THAN : 3850 case Types.COMPARE_LESS_THAN_EQUAL : 3851 { 3852 int op; 3853 switch (type) { 3854 case Types.COMPARE_GREATER_THAN : 3855 op = IFLE; 3856 break; 3857 case Types.COMPARE_GREATER_THAN_EQUAL : 3858 op = IFLT; 3859 break; 3860 case Types.COMPARE_LESS_THAN : 3861 op = IFGE; 3862 break; 3863 case Types.COMPARE_LESS_THAN_EQUAL : 3864 op = IFGT; 3865 break; 3866 default: 3867 System.err.println("flow control error: should not be here. type: " + type); 3868 return; 3869 } 3870 load(leftExp);cast(String.class); 3871 load(rightExp); cast(String.class); 3872 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "compareTo", "(Ljava/lang/Object;)I"); 3873 3874 // set true/false on stack 3875 Label l4 = new Label(); 3876 cv.visitJumpInsn(op, l4); 3877 // need to use primitive boolean //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); 3878 cv.visitInsn(ICONST_1); // true 3879 Label l5 = new Label(); 3880 cv.visitJumpInsn(GOTO, l5); 3881 cv.visitLabel(l4); 3882 cv.visitInsn(ICONST_0); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); 3883 cv.visitLabel(l5); 3884 } 3885 return; 3886 3887 default: 3888 evalBinaryExp_LateBinding(compareMethod, expression); 3889 return; 3890 } 3891 } 3892 else if (Integer.class == lclass && Integer.class == rclass) { 3893 int type = expression.getOperation().getType(); 3894 switch (type) { 3895 case Types.COMPARE_EQUAL : // == 3896 load(leftExp); cast(Integer.class); 3897 load(rightExp); 3898 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z"); 3899 //helper.quickBoxIfNecessary(boolean.class); 3900 return; 3901 case Types.COMPARE_NOT_EQUAL : 3902 load(leftExp); cast(Integer.class); 3903 load(rightExp); 3904 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "equals", "(Ljava/lang/Object;)Z"); 3905 cv.visitInsn(ICONST_1); 3906 cv.visitInsn(IXOR); 3907 //helper.quickBoxIfNecessary(boolean.class); 3908 return; 3909 case Types.COMPARE_TO : 3910 load(leftExp); cast(Integer.class); 3911 load(rightExp); 3912 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I"); 3913 helper.quickBoxIfNecessary(int.class); 3914 return; 3915 case Types.COMPARE_GREATER_THAN : 3916 case Types.COMPARE_GREATER_THAN_EQUAL : 3917 case Types.COMPARE_LESS_THAN : 3918 case Types.COMPARE_LESS_THAN_EQUAL : 3919 { 3920 int op; 3921 switch (type) { 3922 case Types.COMPARE_GREATER_THAN : 3923 op = IFLE; 3924 break; 3925 case Types.COMPARE_GREATER_THAN_EQUAL : 3926 op = IFLT; 3927 break; 3928 case Types.COMPARE_LESS_THAN : 3929 op = IFGE; 3930 break; 3931 case Types.COMPARE_LESS_THAN_EQUAL : 3932 op = IFGT; 3933 break; 3934 default: 3935 System.err.println("flow control error: should not be here. type: " + type); 3936 return; 3937 } 3938 load(leftExp); cast(Integer.class); 3939 load(rightExp); 3940 cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "compareTo", "(Ljava/lang/Object;)I"); 3941 3942 Label l4 = new Label(); 3943 cv.visitJumpInsn(op, l4); 3944 cv.visitInsn(ICONST_1); //cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); 3945 Label l5 = new Label(); 3946 cv.visitJumpInsn(GOTO, l5); 3947 cv.visitLabel(l4); 3948 cv.visitInsn(ICONST_0);//cv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); 3949 cv.visitLabel(l5); 3950 } 3951 return; 3952 3953 default: 3954 evalBinaryExp_LateBinding(compareMethod, expression); 3955 return; 3956 } 3957 } 3958 else { 3959 evalBinaryExp_LateBinding(compareMethod, expression); 3960 return; 3961 } 3962 } 3963 } 3964 3965 private void cast(Class aClass) { 3966 if (!aClass.isPrimitive() && aClass != Object.class) { 3967 cv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(aClass.getName())); 3968 } 3969 } 3970 3971 protected void evaluateEqual(BinaryExpression expression) { 3972 if (ENABLE_EARLY_BINDING) { 3973 expression.resolve(this); 3974 if (expression.isTypeResolved()) { 3975 if (expression.getRightExpression().getTypeClass() == Void.TYPE) { 3976 throwException("void value appeared on right hand side of assignment. "); 3977 } 3978 } 3979 } 3980 3981 Expression leftExpression = expression.getLeftExpression(); 3982 if (leftExpression instanceof BinaryExpression) { 3983 BinaryExpression leftBinExpr = (BinaryExpression) leftExpression; 3984 if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) { 3985 // lets replace this assignment to a subscript operator with a 3986 // method call 3987 // e.g. x[5] = 10 3988 // -> (x, [], 5), =, 10 3989 // -> methodCall(x, "putAt", [5, 10]) 3990 do { 3991 if (true && ENABLE_EARLY_BINDING){ 3992 Class typeclass = leftBinExpr.getLeftExpression().getTypeClass(); 3993 if (typeclass == null) { 3994 break; 3995 } 3996 3997 if (typeclass == Map.class) {// call aMap.put() 3998 load(expression.getRightExpression()); 3999 // let's leave a copy of the value on the stack. 4000 cv.visitInsn(DUP); 4001 final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType()); 4002 // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType()); 4003 final Class rclass = expression.getRightExpression().getTypeClass(); 4004 BytecodeExpression loadTempByteCode = new BytecodeExpression() { 4005 public void visit(GroovyCodeVisitor visitor) { 4006 cv.visitVarInsn(ALOAD, rightTemp.getIndex()); 4007 } 4008 protected void resolveType(AsmClassGenerator resolver) { 4009 setTypeClass(rclass); 4010 } 4011 }; 4012 4013 visitMethodCallExpression( 4014 new MethodCallExpression( 4015 leftBinExpr.getLeftExpression(), 4016 "put", 4017 new ArgumentListExpression( 4018 new Expression[] { 4019 leftBinExpr.getRightExpression(), 4020 loadTempByteCode}))); 4021 cv.visitInsn(POP); // pop the put method return 4022 removeVar(rightTemp); 4023 return; 4024 } 4025 else if (typeclass == List.class){ 4026 // call DefaultGroovyMethods.putAt()V 4027 // DefaultGroovyMethods.putAt(x, 5, "c"); this is faster thangoing thru metaclass 4028 // this method does not return any value. so indicate this fact in the expression 4029 4030 load(expression.getRightExpression()); 4031 // let's leave a copy of the value on the stack. this is really lazy. 4032 cv.visitInsn(DUP); 4033 final Variable rightTemp = storeInTemp("rightTemp", expression.getRightExpression().getType()); 4034 // VariableExpression tempVarExp = new VariableExpression(rightTemp.getName(), expression.getRightExpression().getType()); 4035 final Class rclass = expression.getRightExpression().getTypeClass(); 4036 BytecodeExpression loadTempBytes = new BytecodeExpression() { 4037 public void visit(GroovyCodeVisitor visitor) { 4038 cv.visitVarInsn(ALOAD, rightTemp.getIndex()); 4039 } 4040 protected void resolveType(AsmClassGenerator resolver) { 4041 setTypeClass(rclass); 4042 } 4043 }; 4044 4045 visitMethodCallExpression( 4046 new MethodCallExpression( 4047 new ClassExpression(DefaultGroovyMethods.class), 4048 "putAt", 4049 new ArgumentListExpression( 4050 new Expression[] { 4051 leftBinExpr.getLeftExpression(), 4052 leftBinExpr.getRightExpression(), 4053 loadTempBytes }))); 4054 removeVar(rightTemp); 4055 return; 4056 4057 } 4058 else { 4059 break; 4060 } 4061 } 4062 } while (false); 4063 4064 visitMethodCallExpression( 4065 new MethodCallExpression( 4066 leftBinExpr.getLeftExpression(), 4067 "putAt", 4068 new ArgumentListExpression( 4069 new Expression[] { leftBinExpr.getRightExpression(), expression.getRightExpression()}))); 4070 // cv.visitInsn(POP); //this is realted to isPopRequired() 4071 return; 4072 } 4073 } 4074 4075 // lets evaluate the RHS then hopefully the LHS will be a field 4076 leftHandExpression = false; 4077 Expression rightExpression = expression.getRightExpression(); 4078 4079 String type = getLHSType(leftExpression); 4080 if (type != null) { 4081 //System.out.println("### expression: " + leftExpression); 4082 //System.out.println("### type: " + type); 4083 4084 // lets not cast for primitive types as we handle these in field setting etc 4085 if (BytecodeHelper.isPrimitiveType(type)) { 4086 rightExpression.visit(this); 4087 } 4088 else { 4089 if (ENABLE_EARLY_BINDING) { 4090 if (leftExpression.isDynamic()) { // br the previous if() probably should check this too! 4091 visitAndAutoboxBoolean(rightExpression); 4092 } 4093 else { 4094 if (type.equals(rightExpression.getType())) { 4095 visitAndAutoboxBoolean(rightExpression); 4096 } 4097 else { 4098 if (rightExpression instanceof ConstantExpression && 4099 ((ConstantExpression)rightExpression).getValue() == null) { 4100 cv.visitInsn(ACONST_NULL); 4101 } 4102 else { 4103 visitCastExpression(new CastExpression(type, rightExpression)); 4104 } 4105 } 4106 } 4107 } 4108 else if (!type.equals("java.lang.Object")){ 4109 visitCastExpression(new CastExpression(type, rightExpression)); 4110 } 4111 else { 4112 visitAndAutoboxBoolean(rightExpression); 4113 } 4114 } 4115 } 4116 else { 4117 visitAndAutoboxBoolean(rightExpression); 4118 } 4119 4120 4121 // br: attempt to pass type info from right to left for assignment 4122 if (ENABLE_EARLY_BINDING) { 4123 Class rc = rightExpression.getTypeClass(); 4124 if (rc != null && rc.isArray()) { 4125 Class elemType = rc.getComponentType(); 4126 if (elemType.isPrimitive()) { 4127 visitClassExpression(new ClassExpression(elemType)); 4128 convertPrimitiveArray.call(cv); 4129 cast(loadClass(BytecodeHelper.formatNameForClassLoading(elemType.getName() + "[]"))); 4130 } 4131 } 4132 4133 4134 if (leftExpression.isDynamic() ) { 4135 // propagate the type from right to left if the left is dynamic 4136 if (!(leftExpression instanceof FieldExpression ) && !(leftExpression instanceof PropertyExpression)) 4137 copyTypeClass(leftExpression, rightExpression); 4138 } 4139 else { 4140 Class lc = leftExpression.getTypeClass(); 4141 // Class rc = rightExpression.getTypeClass(); 4142 if (lc != null && rc != null && !lc.isAssignableFrom(rc) && !lc.isPrimitive()) { 4143 // let's use extended conversion logic in the invoker class. 4144 if (!lc.isArray()) { 4145 visitClassExpression(new ClassExpression(lc)); 4146 asTypeMethod.call(cv); 4147 helper.doCast(lc); 4148 } 4149 else { 4150 // may not need this, since variable type converts primitive array to object array automatically 4151 Class elemType = lc.getComponentType(); 4152 if (elemType.isPrimitive()) { 4153 // let's allow type copy for primitive array, meaning [i can be changed to [Integer 4154 copyTypeClass(leftExpression, rightExpression); 4155 } 4156 } 4157 } 4158 } 4159 } 4160 cv.visitInsn(DUP); // to leave a copy of the rightexpression value on the stack after the assignment. 4161 leftHandExpression = true; 4162 leftExpression.visit(this); 4163 leftHandExpression = false; 4164 } 4165 4166 private void copyTypeClass(Expression leftExpression, Expression rightExpression) { 4167 // copy type class from the right to the left, boxing numbers & treat ClassExpression specially 4168 Class rclass = rightExpression.getTypeClass(); 4169 if (rightExpression instanceof ClassExpression) { 4170 leftExpression.setTypeClass(Class.class); 4171 } 4172 else { 4173 rclass = BytecodeHelper.boxOnPrimitive(rclass); 4174 leftExpression.setTypeClass(rclass); 4175 } 4176 } 4177 4178 private boolean canBeAssignedFrom(String ltype, String rtype) { 4179 if (rtype == null) { 4180 return false; 4181 } 4182 else if (ltype == null || ltype.equals("java.lang.Object")) { 4183 return true; 4184 } else { 4185 return false; 4186 } 4187 } 4188 4189 private boolean canBeAssignedFrom(Expression l, Expression r) { 4190 if (r.getTypeClass() == null) { 4191 return false; 4192 } 4193 else if (l.isDynamic()){ 4194 return true; 4195 } else { 4196 return false; 4197 } 4198 } 4199 private boolean canBeAssignedFrom(Class l, Class r) { 4200 if (r == null) { 4201 return false; 4202 } 4203 else if (l == null || l == Object.class){ 4204 return true; 4205 } else { 4206 return false; 4207 } 4208 } 4209 4210 /** 4211 * Deduces the type name required for some casting 4212 * 4213 * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced 4214 */ 4215 protected String getLHSType(Expression leftExpression) { 4216 do { 4217 // commented out. not quiteworking yet. would complain something like: 4218 //java.lang.ClassFormatError: Foo$1 (Illegal Field name "class$[Ljava$lang$String;") 4219 // 4220 // if (ENABLE_EARLY_BINDING) { 4221 // String type = leftExpression.getType(); 4222 // if (type == null) 4223 // break; 4224 // return isValidTypeForCast(type) ? type : null; 4225 // } 4226 } while (false); 4227 4228 if (leftExpression instanceof VariableExpression) { 4229 VariableExpression varExp = (VariableExpression) leftExpression; 4230 String type = varExp.getType(); 4231 if (isValidTypeForCast(type)) { 4232 return type; 4233 } 4234 String variableName = varExp.getVariable(); 4235 Variable variable = (Variable) variableStack.get(variableName); 4236 if (variable != null) { 4237 if (variable.isHolder() || variable.isProperty()) { 4238 return null; 4239 } 4240 type = variable.getTypeName(); 4241 if (isValidTypeForCast(type)) { 4242 return type; 4243 } 4244 } 4245 else { 4246 FieldNode field = classNode.getField(variableName); 4247 if (field == null) { 4248 field = classNode.getOuterField(variableName); 4249 } 4250 if (field != null) { 4251 type = field.getType(); 4252 if (!field.isHolder() && isValidTypeForCast(type)) { 4253 return type; 4254 } 4255 } 4256 } 4257 } 4258 else if (leftExpression instanceof FieldExpression) { 4259 FieldExpression fieldExp = (FieldExpression) leftExpression; 4260 String type = fieldExp.getType(); 4261 if (isValidTypeForCast(type)) { 4262 return type; 4263 } 4264 } 4265 return null; 4266 } 4267 4268 protected boolean isValidTypeForCast(String type) { 4269 return type != null && !type.equals("java.lang.Object") && !type.equals("groovy.lang.Reference") && !BytecodeHelper.isPrimitiveType(type); 4270 } 4271 4272 protected void visitAndAutoboxBoolean(Expression expression) { 4273 expression.visit(this); 4274 4275 if (isComparisonExpression(expression)) { 4276 helper.boxBoolean(); // convert boolean to Boolean 4277 } 4278 } 4279 4280 protected void evaluatePrefixMethod(String method, Expression expression) { 4281 if (isNonStaticField(expression) && ! isHolderVariable(expression) && !isStaticMethod()) { 4282 cv.visitVarInsn(ALOAD, 0); 4283 } 4284 expression.visit(this); 4285 cv.visitLdcInsn(method); 4286 invokeNoArgumentsMethod.call(cv); 4287 4288 leftHandExpression = true; 4289 expression.visit(this); 4290 leftHandExpression = false; 4291 expression.visit(this); 4292 } 4293 4294 protected void evaluatePostfixMethod(String method, Expression expression) { 4295 leftHandExpression = false; 4296 expression.visit(this); 4297 4298 Variable tv = visitASTOREInTemp("postfix_" + method); 4299 int tempIdx = tv.getIndex(); 4300 cv.visitVarInsn(ALOAD, tempIdx); 4301 4302 cv.visitLdcInsn(method); 4303 invokeNoArgumentsMethod.call(cv); 4304 4305 store(expression); 4306 4307 cv.visitVarInsn(ALOAD, tempIdx); 4308 removeVar(tv); 4309 } 4310 4311 protected boolean isHolderVariable(Expression expression) { 4312 if (expression instanceof FieldExpression) { 4313 FieldExpression fieldExp = (FieldExpression) expression; 4314 return fieldExp.getField().isHolder(); 4315 } 4316 if (expression instanceof VariableExpression) { 4317 VariableExpression varExp = (VariableExpression) expression; 4318 Variable variable = (Variable) variableStack.get(varExp.getVariable()); 4319 if (variable != null) { 4320 return variable.isHolder(); 4321 } 4322 FieldNode field = classNode.getField(varExp.getVariable()); 4323 if (field != null) { 4324 return field.isHolder(); 4325 } 4326 } 4327 return false; 4328 } 4329 4330 protected void evaluateInstanceof(BinaryExpression expression) { 4331 expression.getLeftExpression().visit(this); 4332 Expression rightExp = expression.getRightExpression(); 4333 String className = null; 4334 if (rightExp instanceof ClassExpression) { 4335 ClassExpression classExp = (ClassExpression) rightExp; 4336 className = classExp.getType(); 4337 } 4338 else { 4339 throw new RuntimeException( 4340 "Right hand side of the instanceof keyworld must be a class name, not: " + rightExp); 4341 } 4342 className = checkValidType(className, expression, "Must be a valid type name for an instanceof statement"); 4343 String classInternalName = BytecodeHelper.getClassInternalName(className); 4344 cv.visitTypeInsn(INSTANCEOF, classInternalName); 4345 } 4346 4347 /** 4348 * @return true if the given argument expression requires the stack, in 4349 * which case the arguments are evaluated first, stored in the 4350 * variable stack and then reloaded to make a method call 4351 */ 4352 protected boolean argumentsUseStack(Expression arguments) { 4353 return arguments instanceof TupleExpression || arguments instanceof ClosureExpression; 4354 } 4355 4356 /** 4357 * @return true if the given expression represents a non-static field 4358 */ 4359 protected boolean isNonStaticField(Expression expression) { 4360 FieldNode field = null; 4361 if (expression instanceof VariableExpression) { 4362 VariableExpression varExp = (VariableExpression) expression; 4363 field = classNode.getField(varExp.getVariable()); 4364 } 4365 else if (expression instanceof FieldExpression) { 4366 FieldExpression fieldExp = (FieldExpression) expression; 4367 field = classNode.getField(fieldExp.getFieldName()); 4368 } 4369 else if (expression instanceof PropertyExpression) { 4370 PropertyExpression fieldExp = (PropertyExpression) expression; 4371 field = classNode.getField(fieldExp.getProperty()); 4372 } 4373 if (field != null) { 4374 return !field.isStatic(); 4375 } 4376 return false; 4377 } 4378 4379 protected boolean isThisExpression(Expression expression) { 4380 if (expression instanceof VariableExpression) { 4381 VariableExpression varExp = (VariableExpression) expression; 4382 return varExp.getVariable().equals("this"); 4383 } 4384 return false; 4385 } 4386 4387 /** 4388 * For assignment expressions, return a safe expression for the LHS we can use 4389 * to return the value 4390 */ 4391 protected Expression createReturnLHSExpression(Expression expression) { 4392 if (expression instanceof BinaryExpression) { 4393 BinaryExpression binExpr = (BinaryExpression) expression; 4394 if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) { 4395 return createReusableExpression(binExpr.getLeftExpression()); 4396 } 4397 } 4398 return null; 4399 } 4400 4401 protected Expression createReusableExpression(Expression expression) { 4402 ExpressionTransformer transformer = new ExpressionTransformer() { 4403 public Expression transform(Expression expression) { 4404 if (expression instanceof PostfixExpression) { 4405 PostfixExpression postfixExp = (PostfixExpression) expression; 4406 return postfixExp.getExpression(); 4407 } 4408 else if (expression instanceof PrefixExpression) { 4409 PrefixExpression prefixExp = (PrefixExpression) expression; 4410 return prefixExp.getExpression(); 4411 } 4412 return expression; 4413 } 4414 }; 4415 4416 // could just be a postfix / prefix expression or nested inside some other expression 4417 return transformer.transform(expression.transformExpression(transformer)); 4418 } 4419 4420 protected boolean isComparisonExpression(Expression expression) { 4421 if (expression instanceof BinaryExpression) { 4422 BinaryExpression binExpr = (BinaryExpression) expression; 4423 switch (binExpr.getOperation().getType()) { 4424 case Types.COMPARE_EQUAL : 4425 case Types.MATCH_REGEX : 4426 case Types.COMPARE_GREATER_THAN : 4427 case Types.COMPARE_GREATER_THAN_EQUAL : 4428 case Types.COMPARE_LESS_THAN : 4429 case Types.COMPARE_LESS_THAN_EQUAL : 4430 case Types.COMPARE_IDENTICAL : 4431 case Types.COMPARE_NOT_EQUAL : 4432 case Types.KEYWORD_INSTANCEOF : 4433 return true; 4434 } 4435 } 4436 else if (expression instanceof BooleanExpression) { 4437 return true; 4438 } 4439 return false; 4440 } 4441 4442 protected void onLineNumber(ASTNode statement, String message) { 4443 int line = statement.getLineNumber(); 4444 int col = statement.getColumnNumber(); 4445 this.currentASTNode = statement; 4446 4447 if (line >=0) { 4448 lineNumber = line; 4449 columnNumber = col; 4450 } 4451 if (CREATE_LINE_NUMBER_INFO && line >= 0 && cv != null) { 4452 Label l = new Label(); 4453 cv.visitLabel(l); 4454 cv.visitLineNumber(line, l); 4455 if (ASM_DEBUG) { 4456 helper.mark(message + "[" + statement.getLineNumber() + ":" + statement.getColumnNumber() + "]"); 4457 } 4458 } 4459 } 4460 4461 protected VariableScope getVariableScope() { 4462 if (variableScope == null) { 4463 if (methodNode != null) { 4464 // if we're a closure method we'll have our variable scope already created 4465 variableScope = methodNode.getVariableScope(); 4466 } 4467 else if (constructorNode != null) { 4468 variableScope = constructorNode.getVariableScope(); 4469 } 4470 else { 4471 throw new RuntimeException("Can't create a variable scope outside of a method or constructor"); 4472 } 4473 } 4474 return variableScope; 4475 } 4476 4477 /** 4478 * @return a list of parameters for each local variable which needs to be 4479 * passed into a closure 4480 */ 4481 protected Parameter[] getClosureSharedVariables(ClosureExpression expression) { 4482 List vars = new ArrayList(); 4483 4484 // 4485 // First up, get the scopes for outside and inside the closure. 4486 // The inner scope must cover all nested closures, as well, as 4487 // everything that will be needed must be imported. 4488 4489 VariableScope outerScope = getVariableScope().createRecursiveParentScope(); 4490 VariableScope innerScope = expression.getVariableScope(); 4491 if (innerScope == null) { 4492 System.out.println( 4493 "No variable scope for: " + expression + " method: " + methodNode + " constructor: " + constructorNode); 4494 innerScope = new VariableScope(getVariableScope()); 4495 } 4496 else { 4497 innerScope = innerScope.createRecursiveChildScope(); 4498 } 4499 4500 4501 // 4502 // DeclaredVariables include any name that was assigned to within 4503 // the scope. ReferencedVariables include any name that was read 4504 // from within the scope. We get the sets from each and must piece 4505 // together the stack variable import list for the closure. Note 4506 // that we don't worry about field variables here, as we don't have 4507 // to do anything special with them. Stack variables, on the other 4508 // hand, have to be wrapped up in References for use. 4509 4510 Set outerDecls = outerScope.getDeclaredVariables(); 4511 Set outerRefs = outerScope.getReferencedVariables(); 4512 Set innerDecls = innerScope.getDeclaredVariables(); 4513 Set innerRefs = innerScope.getReferencedVariables(); 4514 4515 4516 // 4517 // So, we care about any name referenced in the closure UNLESS: 4518 // 1) it's not declared in the outer context; 4519 // 2) it's a parameter; 4520 // 3) it's a field in the context class that isn't overridden 4521 // by a stack variable in the outer context. 4522 // 4523 // BUG: We don't actually have the necessary information to do 4524 // this right! The outer declarations don't distinguish 4525 // between assignments and variable declarations. Therefore 4526 // we can't tell when field variables have been overridden 4527 // by stack variables in the outer context. This must 4528 // be fixed! 4529 4530 Set varSet = new HashSet(); 4531 for (Iterator iter = innerRefs.iterator(); iter.hasNext();) { 4532 String var = (String) iter.next(); 4533 // lets not pass in fields from the most-outer class, but pass in values from an outer closure 4534 if (outerDecls.contains(var) && (isNotFieldOfOutermostClass(var))) { 4535 String type = getVariableType(var); 4536 vars.add(new Parameter(type, var)); 4537 varSet.add(var); 4538 } 4539 } 4540 for (Iterator iter = outerRefs.iterator(); iter.hasNext();) { 4541 String var = (String) iter.next(); 4542 // lets not pass in fields from the most-outer class, but pass in values from an outer closure 4543 if (innerDecls.contains(var) && (isNotFieldOfOutermostClass(var)) && !varSet.contains(var)) { 4544 String type = getVariableType(var); 4545 vars.add(new Parameter(type, var)); 4546 } 4547 } 4548 4549 4550 Parameter[] answer = new Parameter[vars.size()]; 4551 vars.toArray(answer); 4552 return answer; 4553 } 4554 4555 protected boolean isNotFieldOfOutermostClass(String var) { 4556 //return classNode.getField(var) == null || isInnerClass(); 4557 return getOutermostClass().getField(var) == null; 4558 } 4559 4560 protected void findMutableVariables() { 4561 /* 4562 VariableScopeCodeVisitor outerVisitor = new VariableScopeCodeVisitor(true); 4563 node.getCode().visit(outerVisitor); 4564 4565 addFieldsToVisitor(outerVisitor); 4566 4567 VariableScopeCodeVisitor innerVisitor = outerVisitor.getClosureVisitor(); 4568 */ 4569 VariableScope outerScope = getVariableScope(); 4570 4571 // lets create a scope concatenating all the closure expressions 4572 VariableScope innerScope = outerScope.createCompositeChildScope(); 4573 4574 Set outerDecls = outerScope.getDeclaredVariables(); 4575 Set outerRefs = outerScope.getReferencedVariables(); 4576 Set innerDecls = innerScope.getDeclaredVariables(); 4577 Set innerRefs = innerScope.getReferencedVariables(); 4578 4579 mutableVars.clear(); 4580 4581 for (Iterator iter = innerDecls.iterator(); iter.hasNext();) { 4582 String var = (String) iter.next(); 4583 if ((outerDecls.contains(var) || outerRefs.contains(var)) && classNode.getField(var) == null) { 4584 mutableVars.add(var); 4585 } 4586 } 4587 4588 // we may call the closure twice and modify the variable in the outer scope 4589 // so for now lets assume that all variables are mutable 4590 for (Iterator iter = innerRefs.iterator(); iter.hasNext();) { 4591 String var = (String) iter.next(); 4592 if (outerDecls.contains(var) && classNode.getField(var) == null) { 4593 mutableVars.add(var); 4594 } 4595 } 4596 4597 // System.out.println(); 4598 // System.out.println("method: " + methodNode + " classNode: " + classNode); 4599 // System.out.println("child scopes: " + outerScope.getChildren()); 4600 // System.out.println("outerDecls: " + outerDecls); 4601 // System.out.println("outerRefs: " + outerRefs); 4602 // System.out.println("innerDecls: " + innerDecls); 4603 // System.out.println("innerRefs: " + innerRefs); 4604 } 4605 4606 private boolean isInnerClass() { 4607 return classNode instanceof InnerClassNode; 4608 } 4609 4610 protected String getVariableType(String name) { 4611 Variable variable = (Variable) variableStack.get(name); 4612 if (variable != null) { 4613 return variable.getTypeName(); 4614 } 4615 return null; 4616 } 4617 4618 protected void resetVariableStack(Parameter[] parameters) { 4619 lastVariableIndex = -1; 4620 variableStack.clear(); 4621 4622 scope = new BlockScope(null); 4623 //pushBlockScope(); 4624 4625 // lets push this onto the stack 4626 definingParameters = true; 4627 if (!isStaticMethod()) { 4628 defineVariable("this", classNode.getName()).getIndex(); 4629 } // now lets create indices for the parameteres 4630 for (int i = 0; i < parameters.length; i++) { 4631 Parameter parameter = parameters[i]; 4632 String type = parameter.getType(); 4633 Variable v = defineVariable(parameter.getName(), type); 4634 int idx = v.getIndex(); 4635 if (BytecodeHelper.isPrimitiveType(type)) { 4636 helper.load(type, idx); 4637 helper.box(type); 4638 cv.visitVarInsn(ASTORE, idx); 4639 } 4640 } 4641 definingParameters = false; 4642 } 4643 4644 protected void popScope() { 4645 int lastID = scope.getFirstVariableIndex(); 4646 4647 List removeKeys = new ArrayList(); 4648 for (Iterator iter = variableStack.entrySet().iterator(); iter.hasNext();) { 4649 Map.Entry entry = (Map.Entry) iter.next(); 4650 String name = (String) entry.getKey(); 4651 Variable value = (Variable) entry.getValue(); 4652 if (value.getIndex() >= lastID) { 4653 removeKeys.add(name); 4654 } 4655 } 4656 for (Iterator iter = removeKeys.iterator(); iter.hasNext();) { 4657 Variable v = (Variable) variableStack.remove(iter.next()); 4658 if (CREATE_DEBUG_INFO) { // set localvartable 4659 if (v != null) { 4660 visitVariableEndLabel(v); 4661 cv.visitLocalVariable( 4662 v.getName(), 4663 BytecodeHelper.getTypeDescription(v.getTypeName()), 4664 null, 4665 v.getStartLabel(), 4666 v.getEndLabel(), 4667 v.getIndex() 4668 ); 4669 } 4670 } 4671 } 4672 scope = scope.getParent(); 4673 } 4674 4675 void removeVar(Variable v ) { 4676 variableStack.remove(v.getName()); 4677 if (CREATE_DEBUG_INFO) { // set localvartable 4678 Label endl = new Label(); 4679 cv.visitLabel(endl); 4680 cv.visitLocalVariable( 4681 v.getName(), 4682 BytecodeHelper.getTypeDescription(v.getTypeName()), 4683 null, 4684 v.getStartLabel(), 4685 endl, 4686 v.getIndex() 4687 ); 4688 } 4689 } 4690 private void visitVariableEndLabel(Variable v) { 4691 if (CREATE_DEBUG_INFO) { 4692 if(v.getEndLabel() == null) { 4693 Label end = new Label(); 4694 v.setEndLabel(end); 4695 } 4696 cv.visitLabel(v.getEndLabel()); 4697 } 4698 } 4699 4700 protected void pushBlockScope() { 4701 pushBlockScope(true, true); 4702 } 4703 4704 /** 4705 * create a new scope. Set break/continue label if the canXXX parameter is true. Otherwise 4706 * inherit parent's label. 4707 * @param canContinue true if the start of the scope can take continue label 4708 * @param canBreak true if the end of the scope can take break label 4709 */ 4710 protected void pushBlockScope(boolean canContinue, boolean canBreak) { 4711 BlockScope parentScope = scope; 4712 scope = new BlockScope(parentScope); 4713 scope.setContinueLabel(canContinue ? new Label() : (parentScope == null ? null : parentScope.getContinueLabel())); 4714 scope.setBreakLabel(canBreak? new Label() : (parentScope == null ? null : parentScope.getBreakLabel())); 4715 scope.setFirstVariableIndex(getNextVariableID()); 4716 } 4717 4718 /** 4719 * Defines the given variable in scope and assigns it to the stack 4720 */ 4721 protected Variable defineVariable(String name, String type) { 4722 return defineVariable(name, type, true); 4723 } 4724 4725 protected Variable defineVariable(String name, String type, boolean define) { 4726 return defineVariable(name, new Type(type), define); 4727 } 4728 4729 private Variable defineVariable(String name, Type type, boolean define) { 4730 Variable answer = (Variable) variableStack.get(name); 4731 if (answer == null) { 4732 lastVariableIndex = getNextVariableID(); 4733 answer = new Variable(lastVariableIndex, type, name); 4734 if (mutableVars.contains(name)) { 4735 answer.setHolder(true); 4736 } 4737 variableStack.put(name, answer); 4738 4739 Label startLabel = new Label(); 4740 answer.setStartLabel(startLabel); 4741 if (define) { 4742 if (definingParameters) { 4743 if (answer.isHolder()) { 4744 cv.visitTypeInsn(NEW, "groovy/lang/Reference"); // br todo to associate a label with the variable 4745 cv.visitInsn(DUP); 4746 cv.visitVarInsn(ALOAD, lastVariableIndex); 4747 cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V"); 4748 cv.visitVarInsn(ASTORE, lastVariableIndex); 4749 cv.visitLabel(startLabel); 4750 } 4751 } 4752 else { 4753 // using new variable inside a comparison expression 4754 // so lets initialize it too 4755 if (answer.isHolder() && !isInScriptBody()) { 4756 //cv.visitVarInsn(ASTORE, lastVariableIndex + 1); // I might need this to set the reference value 4757 4758 cv.visitTypeInsn(NEW, "groovy/lang/Reference"); 4759 cv.visitInsn(DUP); 4760 cv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "()V"); 4761 4762 cv.visitVarInsn(ASTORE, lastVariableIndex); 4763 cv.visitLabel(startLabel); 4764 //cv.visitVarInsn(ALOAD, idx + 1); 4765 } 4766 else { 4767 if (!leftHandExpression) { // new var on the RHS: init with null 4768 cv.visitInsn(ACONST_NULL); 4769 cv.visitVarInsn(ASTORE, lastVariableIndex); 4770 cv.visitLabel(startLabel); 4771 } 4772 } 4773 } 4774 } 4775 } 4776 return answer; 4777 } 4778 4779 private boolean isDoubleSizeVariable(Type type) { 4780 return "long".equals(type.getName()) || "double".equals(type.getName()); 4781 } 4782 4783 private int getNextVariableID() { 4784 int index = 0; 4785 for (Iterator iter = variableStack.values().iterator(); iter.hasNext();) { 4786 Variable var = (Variable) iter.next(); 4787 if (isDoubleSizeVariable(var.getType())) { 4788 index += 2; 4789 } else { 4790 index++; 4791 } 4792 } 4793 return index; 4794 } 4795 4796 /** @return true if the given name is a local variable or a field */ 4797 protected boolean isFieldOrVariable(String name) { 4798 return variableStack.containsKey(name) || classNode.getField(name) != null; 4799 } 4800 4801 protected Type checkValidType(Type type, ASTNode node, String message) { 4802 if (type.isDynamic()) { 4803 return type; 4804 } 4805 String name = checkValidType(type.getName(), node, message); 4806 if (type.getName().equals(name)) { 4807 return type; 4808 } 4809 return new Type(name); 4810 } 4811 4812 protected String checkValidType(String type, ASTNode node, String message) { 4813 if (type != null) { 4814 if (type.length() == 0) { 4815 return "java.lang.Object"; 4816 } 4817 if (type.endsWith("[]")) { 4818 String postfix = "[]"; 4819 String prefix = type.substring(0, type.length() - 2); 4820 return checkValidType(prefix, node, message) + postfix; 4821 } 4822 int idx = type.indexOf('$'); 4823 if (idx > 0) { 4824 String postfix = type.substring(idx); 4825 String prefix = type.substring(0, idx); 4826 return checkValidType(prefix, node, message) + postfix; 4827 } 4828 if (BytecodeHelper.isPrimitiveType(type) || "void".equals(type)) { 4829 return type; 4830 } 4831 } 4832 String original = type; 4833 type = resolveClassName(type); 4834 if (type != null) { 4835 return type; 4836 } 4837 throw new MissingClassException(original, node, message + " for class: " + classNode.getName()); 4838 } 4839 4840 protected String resolveClassName(String type) { 4841 return classNode.resolveClassName(type); 4842 } 4843 4844 protected String createVariableName(String type) { 4845 return "__" + type + (++tempVariableNameCounter); 4846 } 4847 4848 /** 4849 * @return if the type of the expression can be determined at compile time 4850 * then this method returns the type - otherwise null 4851 */ 4852 protected String getExpressionType(Expression expression) { 4853 if (isComparisonExpression(expression)) { 4854 return "boolean"; 4855 } 4856 if (expression instanceof VariableExpression) { 4857 VariableExpression varExpr = (VariableExpression) expression; 4858 Variable variable = (Variable) variableStack.get(varExpr.getVariable()); 4859 if (variable != null && !variable.isHolder()) { 4860 Type type = variable.getType(); 4861 if (! type.isDynamic()) { 4862 return type.getName(); 4863 } 4864 } 4865 } 4866 return null; 4867 } 4868 4869 /** 4870 * @return true if the value is an Integer, a Float, a Long, a Double or a 4871 * String . 4872 */ 4873 protected static boolean isPrimitiveFieldType(String type) { 4874 return type.equals("java.lang.String") 4875 || type.equals("java.lang.Integer") 4876 || type.equals("java.lang.Double") 4877 || type.equals("java.lang.Long") 4878 || type.equals("java.lang.Float"); 4879 } 4880 4881 protected boolean isInClosureConstructor() { 4882 return constructorNode != null 4883 && classNode.getOuterClass() != null 4884 && classNode.getSuperClass().equals(Closure.class.getName()); 4885 } 4886 4887 protected boolean isStaticMethod() { 4888 if (methodNode == null) { // we're in a constructor 4889 return false; 4890 } 4891 return methodNode.isStatic(); 4892 } 4893 4894 Map classCache = new HashMap(); 4895 { 4896 classCache.put("int", Integer.TYPE); 4897 classCache.put("byte", Byte.TYPE); 4898 classCache.put("short", Short.TYPE); 4899 classCache.put("char", Character.TYPE); 4900 classCache.put("boolean", Boolean.TYPE); 4901 classCache.put("long", Long.TYPE); 4902 classCache.put("double", Double.TYPE); 4903 classCache.put("float", Float.TYPE); 4904 classCache.put("void", Void.TYPE); 4905 } 4906 /** 4907 * @return loads the given type name 4908 */ 4909 protected Class loadClass(String name) { 4910 4911 if (name.equals(this.classNode.getName())) { 4912 return Object.class; 4913 } 4914 4915 if (name == null) { 4916 return null; 4917 } 4918 else if (name.length() == 0) { 4919 return Object.class; 4920 } 4921 4922 name = BytecodeHelper.formatNameForClassLoading(name); 4923 4924 try { 4925 Class cls = (Class)classCache.get(name); 4926 if (cls != null) 4927 return cls; 4928 4929 CompileUnit compileUnit = getCompileUnit(); 4930 if (compileUnit != null) { 4931 cls = compileUnit.loadClass(name); 4932 classCache.put(name, cls); 4933 return cls; 4934 } 4935 else { 4936 throw new ClassGeneratorException("Could not load class: " + name); 4937 } 4938 } 4939 catch (ClassNotFoundException e) { 4940 throw new ClassGeneratorException("Error when compiling class: " + classNode.getName() + ". Reason: could not load class: " + name + " reason: " + e, e); 4941 } 4942 } 4943 4944 protected CompileUnit getCompileUnit() { 4945 CompileUnit answer = classNode.getCompileUnit(); 4946 if (answer == null) { 4947 answer = context.getCompileUnit(); 4948 } 4949 return answer; 4950 } 4951 4952 /** 4953 * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding. 4954 * @param call 4955 */ 4956 public void resolve(MethodCallExpression call) { 4957 if (call.isResolveFailed()) { 4958 return; 4959 } 4960 else if (call.isTypeResolved()) { 4961 return; 4962 } 4963 4964 Expression obj = call.getObjectExpression(); 4965 String meth = call.getMethod(); 4966 Class ownerClass = null; 4967 boolean isStaticCall = false; 4968 boolean isSuperCall = false; 4969 4970 List arglist = new ArrayList(); 4971 Expression args = call.getArguments(); 4972 if (args instanceof TupleExpression) { 4973 TupleExpression tupleExpression = (TupleExpression) args; 4974 List argexps = tupleExpression.getExpressions(); 4975 for (int i = 0; i < argexps.size(); i++) { 4976 Expression expression = (Expression) argexps.get(i); 4977 Class cls = expression.getTypeClass(); 4978 if (cls == null) { 4979 call.setResolveFailed(true); 4980 return ; 4981 } 4982 else { 4983 arglist.add(cls); 4984 } 4985 } 4986 } else if (args instanceof ClosureExpression) { 4987 call.setResolveFailed(true); 4988 return ;// todo 4989 } else { 4990 call.setResolveFailed(true); 4991 return ; 4992 } 4993 4994 4995 Class[] argsArray = new Class[arglist.size()]; 4996 arglist.toArray(argsArray); 4997 4998 4999 if (obj instanceof ClassExpression) { 5000 // static call 5001 //ClassExpression cls = (ClassExpression)obj; 5002 //String type = cls.getType(); 5003 ownerClass = obj.getTypeClass(); //loadClass(type); 5004 isStaticCall = true; 5005 } else if (obj instanceof VariableExpression) { 5006 VariableExpression var = (VariableExpression) obj; 5007 if (var.getVariable().equals("this") && (methodNode == null? true : !methodNode.isStatic()) ) { 5008 isStaticCall = false; 5009 if (methodNode != null) { 5010 isStaticCall = Modifier.isStatic(methodNode.getModifiers()); 5011 } 5012 MetaMethod mmeth = getMethodOfThisAndSuper(meth, argsArray, isStaticCall); 5013 if (mmeth != null) { 5014 call.setMethod(mmeth); 5015 return ; 5016 } 5017 else { 5018 call.setResolveFailed(true); 5019 return ; 5020 } 5021 } 5022 else if (var.getVariable().equals("super") ) { 5023 isSuperCall = true; 5024 ownerClass = var.getTypeClass(); 5025 } 5026 else { 5027 ownerClass = var.getTypeClass(); 5028 } 5029 } 5030 else /*if (obj instanceof PropertyExpression)*/ { 5031 ownerClass = obj.getTypeClass(); 5032 if (ownerClass == null) { 5033 call.setResolveFailed(true); 5034 call.setFailure("target class is null"); 5035 return ; // take care other cases later version 5036 } 5037 } 5038 5039 if (ownerClass == Object.class) { 5040 call.setResolveFailed(true); 5041 return ; // use late binding for dynamic types 5042 } 5043 else if (ownerClass == null) { 5044 call.setResolveFailed(true); 5045 return ; // use dynamic dispatching for GroovyObject 5046 } 5047 else 5048 if (!isSuperCall && !isStaticCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // not optimize GroovyObject meth call for now 5049 call.setResolveFailed(true); 5050 return ; 5051 } 5052 else 5053 if (ownerClass.isPrimitive()) { 5054 call.setResolveFailed(true); 5055 return ; // todo handle primitives 5056 } 5057 5058 5059 //MetaMethod mmethod = ScriptBytecodeAdapter.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall); 5060 // todo is this thread safe? 5061 MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall); 5062 if (mmethod!= null) { 5063 call.setMethod(mmethod); 5064 } 5065 else { 5066 call.setResolveFailed(true); 5067 } 5068 return ; 5069 } 5070 /** 5071 * attemtp to identify the exact runtime method call the expression is intended for, for possible early binding. 5072 * @param call 5073 */ 5074 public void resolve(ConstructorCallExpression call) { 5075 if (call.isResolveFailed()) { 5076 return ; 5077 } 5078 else if (call.isTypeResolved()) { 5079 return ; 5080 } 5081 5082 String declaredType = call.getTypeToSet(); 5083 if (declaredType.equals(classNode.getName())) { 5084 call.setResolveFailed(true); 5085 call.setFailure("cannot resolve on the current class itself. "); 5086 return; 5087 } 5088 else { 5089 call.setType(declaredType); 5090 if (call.getTypeClass() == null) { 5091 call.setResolveFailed(true); 5092 call.setFailure("type name cannot be resolved. "); 5093 return; 5094 } 5095 } 5096 5097 boolean isSuperCall = false; 5098 5099 List arglist = new ArrayList(); 5100 Expression args = call.getArguments(); 5101 if (args instanceof TupleExpression) { 5102 TupleExpression tupleExpression = (TupleExpression) args; 5103 List argexps = tupleExpression.getExpressions(); 5104 for (int i = 0; i < argexps.size(); i++) { 5105 Expression expression = (Expression) argexps.get(i); 5106 Class cls = expression.getTypeClass(); 5107 if (cls == null) { 5108 call.setResolveFailed(true); 5109 return ; 5110 } 5111 else { 5112 arglist.add(cls); 5113 } 5114 } 5115 } else if (args instanceof ClosureExpression) { 5116 call.setResolveFailed(true); 5117 call.setFailure("don't know how to handle closure arg. "); 5118 return ;// todo 5119 } else { 5120 call.setResolveFailed(true); 5121 call.setFailure("unknown arg type: " + args.getClass().getName()); 5122 return ; 5123 } 5124 5125 5126 Class[] argsArray = new Class[arglist.size()]; 5127 arglist.toArray(argsArray); 5128 5129 Class ownerClass = call.getTypeClass(); 5130 if (ownerClass == null) { 5131 String typeName = call.getType(); 5132 if (typeName.equals(this.classNode.getName())) { 5133 // this is a ctor call to this class 5134 call.setResolveFailed(true); 5135 call.setFailure("invoke constructor for this. no optimization for now"); 5136 return ; 5137 } 5138 else { 5139 try { 5140 ownerClass = loadClass(typeName); 5141 if (ownerClass == null) { 5142 call.setResolveFailed(true); 5143 call.setFailure("owner class type is null. "); 5144 return ; 5145 } 5146 } 5147 catch (Throwable th) { 5148 call.setResolveFailed(true); 5149 call.setFailure("Exception: " + th); 5150 return ; 5151 } 5152 } 5153 } 5154 5155 if (ownerClass == Object.class) { 5156 call.setResolveFailed(true); 5157 call.setFailure("owner class type java.lang.Object. use late binding for dynamic types"); 5158 return ; // 5159 } 5160 else if (ownerClass == null) { 5161 call.setResolveFailed(true); 5162 call.setFailure("owner class type is null. use dynamic dispatching for GroovyObject"); 5163 return; // use dynamic dispatching for GroovyObject 5164 } 5165 // safe to call groovyobject ctor 5166 // else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call 5167 // call.setResolveFailed(true); 5168 // return null; 5169 // } 5170 else if (ownerClass.isPrimitive()) { 5171 call.setResolveFailed(true); 5172 throwException("The owner of the constructor is primitive."); 5173 return ; 5174 } 5175 5176 Constructor ctor = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedConstructor(ownerClass, argsArray); 5177 if (ctor!= null) { 5178 call.setConstructor(ctor); 5179 } 5180 else { 5181 call.setResolveFailed(true); 5182 } 5183 return ; 5184 } 5185 5186 public void resolve(PropertyExpression propertyExpression) { 5187 if (propertyExpression.getTypeClass() != null) 5188 return ; 5189 if (propertyExpression.isResolveFailed()) 5190 return ; 5191 5192 Expression ownerExp = propertyExpression.getObjectExpression(); 5193 Class ownerClass = ownerExp.getTypeClass(); 5194 String propName = propertyExpression.getProperty(); 5195 if (propName.equals("class")) { 5196 propertyExpression.setTypeClass(Class.class); 5197 return ; 5198 } 5199 5200 // handle arraylength 5201 if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) { 5202 propertyExpression.setTypeClass(int.class); 5203 return; 5204 } 5205 5206 if (isThisExpression(ownerExp)) { 5207 // lets use the field expression if its available 5208 if (classNode == null) { 5209 propertyExpression.setResolveFailed(true); 5210 return ; 5211 } 5212 FieldNode field = null; 5213 ownerExp.setType(classNode.getName()); 5214 try { 5215 if( (field = classNode.getField(propName)) != null ) { 5216 // Class cls =loadClass(field.getType()); 5217 // propertyExpression.setAccess(PropertyExpression.LOCAL_FIELD_ACCESS); 5218 // propertyExpression.setTypeClass(cls); 5219 // local property access. to be determined in the future 5220 propertyExpression.setResolveFailed(true); 5221 propertyExpression.setFailure("local property access. to be determined in the future."); 5222 return ; 5223 } else { 5224 // search for super classes/interface 5225 // interface first first 5226 String[] interfaces = classNode.getInterfaces(); 5227 String[] supers = new String[interfaces.length + 1]; 5228 5229 int i = 0; 5230 for (; i < interfaces.length; i++) { 5231 supers[i] = interfaces[i]; 5232 } 5233 supers[i] = classNode.getSuperClass(); 5234 for (int j = 0; j < supers.length; j++) { 5235 String aSuper = supers[j]; 5236 Class superClass = loadClass(aSuper); 5237 Field fld = superClass.getDeclaredField(propName); 5238 if (fld != null && !Modifier.isPrivate(fld.getModifiers())) { 5239 propertyExpression.setField(fld); 5240 return ; 5241 } 5242 } 5243 } 5244 } catch (Exception e) { 5245 propertyExpression.setResolveFailed(true); 5246 propertyExpression.setFailure(e.getMessage()); 5247 return ; 5248 } 5249 } 5250 else if (ownerExp instanceof ClassExpression) { 5251 if (ownerClass != null) { 5252 Field fld = null; 5253 try { 5254 fld = ownerClass.getDeclaredField(propName); 5255 if (!Modifier.isPrivate(fld.getModifiers())) { 5256 propertyExpression.setField(fld); 5257 return ; 5258 } 5259 } catch (NoSuchFieldException e) { 5260 propertyExpression.setResolveFailed(true); 5261 return ; 5262 } 5263 } 5264 } 5265 else { // search public field and then setter/getter 5266 if (ownerClass != null) { 5267 propertyExpression.setResolveFailed(true); // will get reset if property/getter/setter were found 5268 Field fld = null; 5269 try { 5270 fld = ownerClass.getDeclaredField(propName); 5271 } catch (NoSuchFieldException e) {} 5272 5273 if (fld != null && Modifier.isPublic(fld.getModifiers())) { 5274 propertyExpression.setField(fld); 5275 } 5276 5277 // let's get getter and setter 5278 String getterName = "get" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1); 5279 String setterName = "set" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1); 5280 5281 Method[] meths = ownerClass.getMethods(); 5282 for (int i = 0; i < meths.length; i++) { 5283 Method method = meths[i]; 5284 String methName =method.getName(); 5285 Class[] paramClasses = method.getParameterTypes(); 5286 if (methName.equals(getterName) && paramClasses.length == 0) { 5287 propertyExpression.setGetter(method); 5288 } else if (methName.equals(setterName) && paramClasses.length == 1) { 5289 propertyExpression.setSetter(method); 5290 } 5291 } 5292 return ; 5293 } 5294 } 5295 propertyExpression.setResolveFailed(true); 5296 return ; 5297 } 5298 5299 public void resolve(AttributeExpression attributeExpression) { 5300 if (attributeExpression.getTypeClass() != null) 5301 return ; 5302 if (attributeExpression.isResolveFailed()) 5303 return ; 5304 5305 Expression ownerExp = attributeExpression.getObjectExpression(); 5306 Class ownerClass = ownerExp.getTypeClass(); 5307 String propName = attributeExpression.getProperty(); 5308 if (propName.equals("class")) { 5309 attributeExpression.setTypeClass(Class.class); 5310 return ; 5311 } 5312 5313 // handle arraylength 5314 if (ownerClass != null && ownerClass.isArray() && propName.equals("length")) { 5315 attributeExpression.setTypeClass(int.class); 5316 return; 5317 } 5318 5319 if (isThisExpression(ownerExp)) { 5320 // lets use the field expression if its available 5321 if (classNode == null) { 5322 attributeExpression.setResolveFailed(true); 5323 return ; 5324 } 5325 FieldNode field = null; 5326 ownerExp.setType(classNode.getName()); 5327 try { 5328 if( (field = classNode.getField(propName)) != null ) { 5329 // Class cls =loadClass(field.getType()); 5330 // attributeExpression.setAccess(PropertyExpression.LOCAL_FIELD_ACCESS); 5331 // attributeExpression.setTypeClass(cls); 5332 // local property access. to be determined in the future 5333 attributeExpression.setResolveFailed(true); 5334 attributeExpression.setFailure("local property access. to be determined in the future."); 5335 return ; 5336 } else { 5337 // search for super classes/interface 5338 // interface first first 5339 String[] interfaces = classNode.getInterfaces(); 5340 String[] supers = new String[interfaces.length + 1]; 5341 5342 int i = 0; 5343 for (; i < interfaces.length; i++) { 5344 supers[i] = interfaces[i]; 5345 } 5346 supers[i] = classNode.getSuperClass(); 5347 for (int j = 0; j < supers.length; j++) { 5348 String aSuper = supers[j]; 5349 Class superClass = loadClass(aSuper); 5350 Field fld = superClass.getDeclaredField(propName); 5351 if (fld != null && !Modifier.isPrivate(fld.getModifiers())) { 5352 attributeExpression.setField(fld); 5353 return ; 5354 } 5355 } 5356 } 5357 } catch (Exception e) { 5358 attributeExpression.setResolveFailed(true); 5359 attributeExpression.setFailure(e.getMessage()); 5360 return ; 5361 } 5362 } 5363 else if (ownerExp instanceof ClassExpression) { 5364 if (ownerClass != null) { 5365 Field fld = null; 5366 try { 5367 fld = ownerClass.getDeclaredField(propName); 5368 if (!Modifier.isPrivate(fld.getModifiers())) { 5369 attributeExpression.setField(fld); 5370 return ; 5371 } 5372 } catch (NoSuchFieldException e) { 5373 attributeExpression.setResolveFailed(true); 5374 return ; 5375 } 5376 } 5377 } 5378 else { // search public field and then setter/getter 5379 if (ownerClass != null) { 5380 attributeExpression.setResolveFailed(true); // will get reset if property/getter/setter were found 5381 Field fld = null; 5382 try { 5383 fld = ownerClass.getDeclaredField(propName); 5384 } catch (NoSuchFieldException e) {} 5385 5386 if (fld != null && Modifier.isPublic(fld.getModifiers())) { 5387 attributeExpression.setField(fld); 5388 } 5389 5390 // let's get getter and setter 5391 String getterName = "get" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1); 5392 String setterName = "set" + Character.toUpperCase(propName.charAt(0)) + propName.substring(1); 5393 5394 Method[] meths = ownerClass.getMethods(); 5395 for (int i = 0; i < meths.length; i++) { 5396 Method method = meths[i]; 5397 String methName =method.getName(); 5398 Class[] paramClasses = method.getParameterTypes(); 5399 if (methName.equals(getterName) && paramClasses.length == 0) { 5400 attributeExpression.setGetter(method); 5401 } else if (methName.equals(setterName) && paramClasses.length == 1) { 5402 attributeExpression.setSetter(method); 5403 } 5404 } 5405 return ; 5406 } 5407 } 5408 attributeExpression.setResolveFailed(true); 5409 return ; 5410 } 5411 /** search in the current classNode and super class for matching method */ 5412 private MetaMethod getMethodOfThisAndSuper(String methName, Class[] argsArray, boolean isStaticCall) { 5413 MethodNode candidate = null; 5414 List meths = classNode.getMethods(); 5415 Class[] candidateParamClasses = null; 5416 for (int i = 0; i < meths.size(); i++) { 5417 MethodNode meth = (MethodNode) meths.get(i); 5418 if (meth.getName().equals(methName)) { 5419 Parameter[] params = meth.getParameters(); 5420 if (params.length == argsArray.length) { 5421 Class[] paramClasses = new Class[params.length]; 5422 for (int j = 0; j < params.length; j++) { 5423 Parameter param = params[j]; 5424 String type = param.getType(); 5425 Class paramClass = null; 5426 try { 5427 paramClass = loadClass(type); 5428 } catch (Exception e) { 5429 log.warning(e.getMessage()); 5430 return null; 5431 } 5432 paramClasses[j] = paramClass; 5433 } 5434 if (MetaClass.isValidMethod(paramClasses, argsArray, false)) { 5435 candidateParamClasses = paramClasses; 5436 candidate = meth; 5437 break; 5438 } 5439 else { 5440 if (MetaClass.isValidMethod(paramClasses, argsArray, true)){ 5441 candidateParamClasses = paramClasses; 5442 candidate = meth; 5443 break; 5444 } 5445 } 5446 } 5447 } 5448 } 5449 5450 if (candidate != null && candidateParamClasses != null) { 5451 // let's synth a MetaMethod from the MethodNode 5452 try { 5453 return new MetaMethod(methName, null, candidateParamClasses, loadClass(candidate.getReturnType()), candidate.getModifiers()); 5454 } catch (Exception e) { 5455 log.warning(e.getMessage()); 5456 return null; 5457 } 5458 } 5459 else { 5460 // try super class 5461 Class superClass = null; 5462 try { 5463 superClass = loadClass(classNode.getSuperClass()); 5464 } 5465 catch(Exception e) { 5466 // the super may be a groovy class that's not compiled yet 5467 log.warning(e.getMessage()); 5468 } 5469 // should I filter out GroovyObject super class here? 5470 if (superClass != null ) { 5471 MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(superClass, methName, argsArray, isStaticCall); 5472 if (mmethod == null) 5473 return null; 5474 int modies = mmethod.getModifiers(); 5475 if (Modifier.isPrivate(modies)) { 5476 return null; 5477 } 5478 else if(modies == 0) { 5479 // match package 5480 int pThis = classNode.getName().lastIndexOf("."); 5481 String packageNameThis = pThis > 0? classNode.getName().substring(0, pThis) : ""; 5482 5483 int pSuper = classNode.getSuperClass().lastIndexOf("."); 5484 String packageNameSuper = pSuper > 0? classNode.getSuperClass().substring(0, pSuper) : ""; 5485 if (packageNameThis.equals(packageNameSuper)) { 5486 return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers()); 5487 } 5488 else { 5489 return null; 5490 } 5491 } 5492 else { 5493 // let changes the declaring class back to null (meaning "this"), so that proper class inheritance permission control is obeyed 5494 return new MetaMethod(methName, null, mmethod.getParameterTypes(), mmethod.getReturnType(), mmethod.getModifiers()); 5495 } 5496 } 5497 return null; 5498 } 5499 } 5500 5501 5502 /** 5503 * to find out the real type of a Variable Object 5504 */ 5505 public void resolve(VariableExpression expression) { 5506 5507 String variableName = expression.getVariable(); 5508 // todo process arrays! 5509 //----------------------------------------------------------------------- 5510 // SPECIAL CASES 5511 5512 // 5513 // "this" for static methods is the Class instance 5514 5515 if (isStaticMethod() && variableName.equals("this")) { 5516 expression.setTypeClass(Class.class); 5517 return; 5518 } else if (variableName.equals("super")) { 5519 if (isStaticMethod() ) { 5520 expression.setTypeClass(Class.class); 5521 return; 5522 } 5523 else { 5524 try { 5525 Class cls = loadClass(classNode.getSuperClass()); 5526 expression.setTypeClass(cls); 5527 return ; 5528 } 5529 catch (Exception e) { 5530 expression.setResolveFailed(true); 5531 expression.setFailure(e.getMessage()); 5532 return ; 5533 } 5534 } 5535 } else if (variableName.equals("this")){ 5536 return ; 5537 } else { 5538 // String className = resolveClassName(variableName); 5539 // if (className != null) { 5540 // return loadClass(className); 5541 // } 5542 } 5543 5544 5545 //----------------------------------------------------------------------- 5546 // GENERAL VARIABLE LOOKUP 5547 5548 // 5549 // We are handling only unqualified variables here. Therefore, 5550 // we do not care about accessors, because local access doesn't 5551 // go through them. Therefore, precedence is as follows: 5552 // 1) local variables, nearest block first 5553 // 2) class fields 5554 // 3) repeat search from 2) in next outer class 5555 5556 boolean handled = false; 5557 Variable variable = (Variable)variableStack.get( variableName ); 5558 5559 try { 5560 if( variable != null ) { 5561 Type t = variable.getType(); 5562 if (t.getRealName().length() == 0) { 5563 String tname = t.getName(); 5564 if (tname.endsWith("[]")) { 5565 expression.setResolveFailed(true); 5566 expression.setFailure("array type to be supported later"); 5567 return ; // todo hanlde array 5568 } 5569 else if (tname.equals(classNode.getName())){ 5570 expression.setResolveFailed(true); 5571 return ; 5572 } 5573 else if (classNode.getOuterClass() != null && tname.equals(classNode.getOuterClass().getName())){ 5574 expression.setResolveFailed(true); 5575 return ; 5576 } 5577 Class cls = loadClass(tname); 5578 expression.setTypeClass(cls); 5579 expression.setDynamic(t.isDynamic()); 5580 return ; 5581 } else { 5582 String tname = t.getRealName(); 5583 if (tname.endsWith("[]")) { 5584 expression.setResolveFailed(true); 5585 expression.setFailure("array type to be supported later"); 5586 return ; // todo hanlde array 5587 } 5588 Class cls = loadClass(tname); 5589 expression.setTypeClass(cls); 5590 expression.setDynamic(t.isDynamic()); 5591 return ; 5592 } 5593 5594 // if( variable.isProperty() ) { 5595 // processPropertyVariable(variable ); 5596 // } 5597 // else { 5598 // processStackVariable(variable ); 5599 // } 5600 // 5601 } else { 5602 int steps = 0; 5603 ClassNode currentClassNode = classNode; 5604 FieldNode field = null; 5605 do { 5606 if( (field = currentClassNode.getField(variableName)) != null ) { 5607 if (methodNode == null || !methodNode.isStatic() || field.isStatic() ) { 5608 if (/*field.isDynamicType() || */field.isHolder()) { 5609 expression.setResolveFailed(true); 5610 expression.setFailure("reference type to be supported later"); 5611 return ; 5612 } else { 5613 String type = field.getType(); 5614 Class cls = loadClass(type); 5615 expression.setTypeClass(cls); 5616 expression.setDynamic(field.isDynamicType()); 5617 return ; 5618 } 5619 } 5620 } 5621 steps++; 5622 } while( (currentClassNode = currentClassNode.getOuterClass()) != null ); 5623 } 5624 5625 // 5626 // Finally, a new variable 5627 5628 String variableType = expression.getType(); 5629 if (variableType.length() > 0 && !variableType.equals("java.lang.Object")) { 5630 Class cls = loadClass(variableType); 5631 expression.setTypeClass(cls); 5632 return ; 5633 } 5634 } catch (Exception e) { 5635 log.warning(e.getMessage()); 5636 expression.setResolveFailed(true); 5637 expression.setFailure(e.getMessage()); 5638 } 5639 return ; 5640 } 5641 5642 public MetaMethod resolve(StaticMethodCallExpression staticCall) { 5643 if (staticCall.isResolveFailed()) { 5644 return null; 5645 } 5646 else if (staticCall.isTypeResolved()) { 5647 return staticCall.getMetaMethod(); 5648 } 5649 5650 String ownerTypeName = staticCall.getOwnerType(); 5651 String meth = staticCall.getMethod(); 5652 Class ownerClass = null; 5653 try { 5654 ownerClass = loadClass(ownerTypeName); 5655 } catch (Exception e) { 5656 staticCall.setResolveFailed(true); 5657 staticCall.setFailure("Owner type could not be resolved: " + e); 5658 return null; 5659 } 5660 5661 boolean isStaticCall = true; 5662 boolean isSuperCall = false; 5663 5664 List arglist = new ArrayList(); 5665 Expression args = staticCall.getArguments(); 5666 if (args instanceof TupleExpression) { 5667 TupleExpression tupleExpression = (TupleExpression) args; 5668 List argexps = tupleExpression.getExpressions(); 5669 for (int i = 0; i < argexps.size(); i++) { 5670 Expression expression = (Expression) argexps.get(i); 5671 Class cls = expression.getTypeClass(); 5672 if (cls == null) { 5673 staticCall.setResolveFailed(true); 5674 staticCall.setFailure("Argument type could not be resolved."); 5675 return null; 5676 } 5677 else { 5678 arglist.add(cls); 5679 } 5680 } 5681 } else if (args instanceof ClosureExpression) { 5682 staticCall.setResolveFailed(true); 5683 staticCall.setFailure("Resolving on Closure call not implemented yet. "); 5684 return null;// todo 5685 } else { 5686 staticCall.setResolveFailed(true); 5687 staticCall.setFailure("Unknown argument expression type."); 5688 return null; 5689 } 5690 5691 5692 Class[] argsArray = new Class[arglist.size()]; 5693 arglist.toArray(argsArray); 5694 5695 if (ownerClass == Object.class) { 5696 staticCall.setResolveFailed(true); 5697 staticCall.setFailure("Resolving on java.lang.Object static call not supported. "); 5698 return null; // use late binding for dynamic types 5699 } 5700 else if (ownerClass == null) { 5701 staticCall.setResolveFailed(true); 5702 staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. "); 5703 return null; // use dynamic dispatching for GroovyObject 5704 } 5705 else if (!isSuperCall && GroovyObject.class.isAssignableFrom(ownerClass) ) { // ie, to allow early binding for a super call 5706 staticCall.setResolveFailed(true); 5707 staticCall.setFailure("Resolving on GrovyObject static call not implemented yet. "); 5708 return null; 5709 } 5710 else if (ownerClass.isPrimitive()) { 5711 staticCall.setResolveFailed(true); 5712 staticCall.setFailure("Could not use primitive as method owner"); 5713 return null; // todo handle primitives 5714 } 5715 5716 5717 //MetaMethod mmethod = ScriptBytecodeAdapter.getInstance().getMetaRegistry().getDefinedMethod(ownerClass, meth, argsArray, isStaticCall); 5718 // todo is this thread safe? 5719 MetaMethod mmethod = MetaClassRegistry.getIntance(MetaClassRegistry.DONT_LOAD_DEFAULT).getDefinedMethod(ownerClass, meth, argsArray, isStaticCall); 5720 if (mmethod!= null) { 5721 staticCall.setMetaMethod(mmethod); 5722 } 5723 else { 5724 staticCall.setResolveFailed(true); 5725 staticCall.setFailure("Could not find MetaMethod in the MetaClass."); 5726 } 5727 return mmethod; 5728 } 5729 5730 // the fowllowing asXXX() methods are copied from the Invoker class, to avoid initilization of an invoker instance, 5731 // which has lots of baggage with it, notably the meta class stuff. 5732 private static Object asType(Object object, Class type) { 5733 if (object == null) { 5734 return null; 5735 } 5736 if (type.isInstance(object)) { 5737 return object; 5738 } 5739 if (type.equals(String.class)) { 5740 return object.toString(); 5741 } 5742 if (type.equals(Character.class)) { 5743 if (object instanceof Number) { 5744 return asCharacter((Number) object); 5745 } 5746 else { 5747 String text = object.toString(); 5748 if (text.length() == 1) { 5749 return new Character(text.charAt(0)); 5750 } 5751 else { 5752 throw new ClassCastException("Cannot cast: " + text + " to a Character"); 5753 } 5754 } 5755 } 5756 if (Number.class.isAssignableFrom(type)) { 5757 if (object instanceof Character) { 5758 return new Integer(((Character) object).charValue()); 5759 } 5760 else if (object instanceof String) { 5761 String c = (String) object; 5762 if (c.length() == 1) { 5763 return new Integer(c.charAt(0)); 5764 } 5765 else { 5766 throw new ClassCastException("Cannot cast: '" + c + "' to an Integer"); 5767 } 5768 } 5769 } 5770 if (object instanceof Number) { 5771 Number n = (Number) object; 5772 if (type.isPrimitive()) { 5773 if (type == byte.class) { 5774 return new Byte(n.byteValue()); 5775 } 5776 if (type == char.class) { 5777 return new Character((char) n.intValue()); 5778 } 5779 if (type == short.class) { 5780 return new Short(n.shortValue()); 5781 } 5782 if (type == int.class) { 5783 return new Integer(n.intValue()); 5784 } 5785 if (type == long.class) { 5786 return new Long(n.longValue()); 5787 } 5788 if (type == float.class) { 5789 return new Float(n.floatValue()); 5790 } 5791 if (type == double.class) { 5792 Double answer = new Double(n.doubleValue()); 5793 //throw a runtime exception if conversion would be out-of-range for the type. 5794 if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY 5795 || answer.doubleValue() == Double.POSITIVE_INFINITY)) { 5796 throw new GroovyRuntimeException("Automatic coercion of "+n.getClass().getName() 5797 +" value "+n+" to double failed. Value is out of range."); 5798 } 5799 return answer; 5800 } 5801 } 5802 else { 5803 if (Number.class.isAssignableFrom(type)) { 5804 if (type == Byte.class) { 5805 return new Byte(n.byteValue()); 5806 } 5807 if (type == Character.class) { 5808 return new Character((char) n.intValue()); 5809 } 5810 if (type == Short.class) { 5811 return new Short(n.shortValue()); 5812 } 5813 if (type == Integer.class) { 5814 return new Integer(n.intValue()); 5815 } 5816 if (type == Long.class) { 5817 return new Long(n.longValue()); 5818 } 5819 if (type == Float.class) { 5820 return new Float(n.floatValue()); 5821 } 5822 if (type == Double.class) { 5823 Double answer = new Double(n.doubleValue()); 5824 //throw a runtime exception if conversion would be out-of-range for the type. 5825 if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY 5826 || answer.doubleValue() == Double.POSITIVE_INFINITY)) { 5827 throw new GroovyRuntimeException("Automatic coercion of "+n.getClass().getName() 5828 +" value "+n+" to double failed. Value is out of range."); 5829 } 5830 return answer; 5831 } 5832 5833 } 5834 } 5835 } 5836 if (type == Boolean.class) { 5837 return asBool(object) ? Boolean.TRUE : Boolean.FALSE; 5838 } 5839 return object; 5840 } 5841 5842 public static boolean asBool(Object object) { 5843 if (object instanceof Boolean) { 5844 Boolean booleanValue = (Boolean) object; 5845 return booleanValue.booleanValue(); 5846 } 5847 else if (object instanceof Matcher) { 5848 Matcher matcher = (Matcher) object; 5849 RegexSupport.setLastMatcher(matcher); 5850 return matcher.find(); 5851 } 5852 else if (object instanceof Collection) { 5853 Collection collection = (Collection) object; 5854 return !collection.isEmpty(); 5855 } 5856 else if (object instanceof Map) { 5857 Map map = (Map) object; 5858 return !map.isEmpty(); 5859 } 5860 else if (object instanceof String) { 5861 String string = (String) object; 5862 return string.length() > 0; 5863 } 5864 else if (object instanceof Number) { 5865 Number n = (Number) object; 5866 return n.doubleValue() != 0; 5867 } 5868 else { 5869 return object != null; 5870 } 5871 } 5872 private static Character asCharacter(Number value) { 5873 return new Character((char) value.intValue()); 5874 } 5875 5876 private static Character asCharacter(String text) { 5877 return new Character(text.charAt(0)); 5878 } 5879 }