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