001 /* 002 * $Id: ResolveVisitor.java 4295 2006-12-02 21:15:54Z 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 that the 008 * following conditions are met: 1. Redistributions of source code must retain 009 * copyright statements and notices. Redistributions must also contain a copy 010 * of this document. 2. Redistributions in binary form must reproduce the above 011 * copyright notice, this list of conditions and the following disclaimer in 012 * the documentation and/or other materials provided with the distribution. 3. 013 * The name "groovy" must not be used to endorse or promote products derived 014 * from this Software without prior written permission of The Codehaus. For 015 * written permission, please contact info@codehaus.org. 4. Products derived 016 * from this Software may not be called "groovy" nor may "groovy" appear in 017 * their names without prior written permission of The Codehaus. "groovy" is a 018 * registered trademark of The Codehaus. 5. Due credit should be given to The 019 * Codehaus - http://groovy.codehaus.org/ 020 * 021 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 022 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 024 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 025 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 026 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 027 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 028 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 029 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 030 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 031 * DAMAGE. 032 * 033 */ 034 package org.codehaus.groovy.control; 035 036 import groovy.lang.GroovyClassLoader; 037 038 import java.io.IOException; 039 import java.io.File; 040 import java.lang.reflect.Field; 041 import java.util.HashMap; 042 import java.util.Iterator; 043 import java.util.LinkedList; 044 import java.util.List; 045 import java.util.Map; 046 import java.net.URL; 047 import java.net.MalformedURLException; 048 049 import org.codehaus.groovy.ast.ASTNode; 050 import org.codehaus.groovy.ast.AnnotatedNode; 051 import org.codehaus.groovy.ast.AnnotationNode; 052 import org.codehaus.groovy.ast.ClassCodeVisitorSupport; 053 import org.codehaus.groovy.ast.ClassHelper; 054 import org.codehaus.groovy.ast.ClassNode; 055 import org.codehaus.groovy.ast.CompileUnit; 056 import org.codehaus.groovy.ast.ConstructorNode; 057 import org.codehaus.groovy.ast.DynamicVariable; 058 import org.codehaus.groovy.ast.FieldNode; 059 import org.codehaus.groovy.ast.ImportNode; 060 import org.codehaus.groovy.ast.MethodNode; 061 import org.codehaus.groovy.ast.ModuleNode; 062 import org.codehaus.groovy.ast.Parameter; 063 import org.codehaus.groovy.ast.PropertyNode; 064 import org.codehaus.groovy.ast.Variable; 065 import org.codehaus.groovy.ast.VariableScope; 066 import org.codehaus.groovy.ast.expr.BinaryExpression; 067 import org.codehaus.groovy.ast.expr.BooleanExpression; 068 import org.codehaus.groovy.ast.expr.ClassExpression; 069 import org.codehaus.groovy.ast.expr.ClosureExpression; 070 import org.codehaus.groovy.ast.expr.ConstructorCallExpression; 071 import org.codehaus.groovy.ast.expr.DeclarationExpression; 072 import org.codehaus.groovy.ast.expr.Expression; 073 import org.codehaus.groovy.ast.expr.ExpressionTransformer; 074 import org.codehaus.groovy.ast.expr.ListExpression; 075 import org.codehaus.groovy.ast.expr.MethodCallExpression; 076 import org.codehaus.groovy.ast.expr.PropertyExpression; 077 import org.codehaus.groovy.ast.expr.VariableExpression; 078 import org.codehaus.groovy.ast.stmt.AssertStatement; 079 import org.codehaus.groovy.ast.stmt.BlockStatement; 080 import org.codehaus.groovy.ast.stmt.CaseStatement; 081 import org.codehaus.groovy.ast.stmt.CatchStatement; 082 import org.codehaus.groovy.ast.stmt.DoWhileStatement; 083 import org.codehaus.groovy.ast.stmt.ExpressionStatement; 084 import org.codehaus.groovy.ast.stmt.ForStatement; 085 import org.codehaus.groovy.ast.stmt.IfStatement; 086 import org.codehaus.groovy.ast.stmt.ReturnStatement; 087 import org.codehaus.groovy.ast.stmt.Statement; 088 import org.codehaus.groovy.ast.stmt.SwitchStatement; 089 import org.codehaus.groovy.ast.stmt.SynchronizedStatement; 090 import org.codehaus.groovy.ast.stmt.ThrowStatement; 091 import org.codehaus.groovy.ast.stmt.WhileStatement; 092 import org.codehaus.groovy.classgen.Verifier; 093 import org.codehaus.groovy.control.messages.ExceptionMessage; 094 import org.codehaus.groovy.syntax.Types; 095 096 /** 097 * Visitor to resolve Types and convert VariableExpression to 098 * ClassExpressions if needed. The ResolveVisitor will try to 099 * find the Class for a ClassExpression and prints an error if 100 * it fails to do so. Constructions like C[], foo as C, (C) foo 101 * will force creation of a ClasssExpression for C 102 * 103 * Note: the method to start the resolving is startResolving(ClassNode, SourceUnit). 104 * 105 * 106 * @author Jochen Theodorou 107 */ 108 public class ResolveVisitor extends ClassCodeVisitorSupport implements ExpressionTransformer { 109 private ClassNode currentClass; 110 // note: BigInteger and BigDecimal are also imported by default 111 private static final String[] DEFAULT_IMPORTS = {"java.lang.", "java.io.", "java.net.", "java.util.", "groovy.lang.", "groovy.util."}; 112 private CompilationUnit compilationUnit; 113 private Map cachedClasses = new HashMap(); 114 private static final Object NO_CLASS = new Object(); 115 private static final Object SCRIPT = new Object(); 116 private SourceUnit source; 117 private VariableScope currentScope; 118 119 private boolean isTopLevelProperty = true; 120 private boolean inClosure = false; 121 122 public ResolveVisitor(CompilationUnit cu) { 123 compilationUnit = cu; 124 } 125 126 public void startResolving(ClassNode node,SourceUnit source) { 127 this.source = source; 128 visitClass(node); 129 } 130 131 public void visitConstructor(ConstructorNode node) { 132 visitAnnotations(node); 133 VariableScope oldScope = currentScope; 134 currentScope = node.getVariableScope(); 135 Parameter[] paras = node.getParameters(); 136 for (int i=0; i<paras.length; i++) { 137 ClassNode t = paras[i].getType(); 138 resolveOrFail(t,node); 139 } 140 ClassNode[] exceptions = node.getExceptions(); 141 for (int i=0; i<exceptions.length; i++) { 142 ClassNode t = exceptions[i]; 143 resolveOrFail(t,node); 144 } 145 Statement code = node.getCode(); 146 if (code!=null) code.visit(this); 147 currentScope = oldScope; 148 } 149 150 public void visitSwitch(SwitchStatement statement) { 151 Expression exp = statement.getExpression(); 152 statement.setExpression(transform(exp)); 153 List list = statement.getCaseStatements(); 154 for (Iterator iter = list.iterator(); iter.hasNext(); ) { 155 CaseStatement caseStatement = (CaseStatement) iter.next(); 156 caseStatement.visit(this); 157 } 158 statement.getDefaultStatement().visit(this); 159 } 160 161 public void visitMethod(MethodNode node) { 162 visitAnnotations(node); 163 VariableScope oldScope = currentScope; 164 currentScope = node.getVariableScope(); 165 Parameter[] paras = node.getParameters(); 166 for (int i=0; i<paras.length; i++) { 167 ClassNode t = paras[i].getType(); 168 resolveOrFail(t,node); 169 if (paras[i].hasInitialExpression()) { 170 Expression init = paras[i].getInitialExpression(); 171 paras[i].setInitialExpression(transform(init)); 172 } 173 } 174 ClassNode[] exceptions = node.getExceptions(); 175 for (int i=0; i<exceptions.length; i++) { 176 ClassNode t = exceptions[i]; 177 resolveOrFail(t,node); 178 } 179 resolveOrFail(node.getReturnType(),node); 180 Statement code = node.getCode(); 181 if (code!=null) code.visit(this); 182 currentScope = oldScope; 183 } 184 185 public void visitField(FieldNode node) { 186 visitAnnotations(node); 187 ClassNode t = node.getType(); 188 resolveOrFail(t,node); 189 Expression init = node.getInitialExpression(); 190 node.setInitialValueExpression(transform(init)); 191 } 192 193 public void visitProperty(PropertyNode node) { 194 visitAnnotations(node); 195 ClassNode t = node.getType(); 196 resolveOrFail(t,node); 197 Statement code = node.getGetterBlock(); 198 if (code!=null) code.visit(this); 199 code = node.getSetterBlock(); 200 if (code!=null) code.visit(this); 201 } 202 203 public void visitIfElse(IfStatement ifElse) { 204 visitStatement(ifElse); 205 ifElse.setBooleanExpression((BooleanExpression) (transform(ifElse.getBooleanExpression()))); 206 ifElse.getIfBlock().visit(this); 207 ifElse.getElseBlock().visit(this); 208 } 209 210 private void resolveOrFail(ClassNode type, String msg, ASTNode node) { 211 if (resolve(type)) return; 212 addError("unable to resolve class "+type.getName()+" "+msg,node); 213 } 214 215 private void resolveOrFail(ClassNode type, ASTNode node, boolean prefereImports) { 216 if (prefereImports && resolveAliasFromModule(type)) return; 217 resolveOrFail(type,node); 218 } 219 220 private void resolveOrFail(ClassNode type, ASTNode node) { 221 resolveOrFail(type,"",node); 222 } 223 224 private boolean resolve(ClassNode type) { 225 String name = type.getName(); 226 return resolve(type,true,true,true); 227 } 228 229 private boolean resolve(ClassNode type, boolean testModuleImports, boolean testDefaultImports, boolean testStaticInnerClasses) { 230 if (type.isResolved()) return true; 231 if (type.isArray()) { 232 ClassNode element = type.getComponentType(); 233 boolean resolved = resolve(element,testModuleImports,testDefaultImports,testStaticInnerClasses); 234 if (resolved) { 235 ClassNode cn = element.makeArray(); 236 type.setRedirect(cn); 237 } 238 return resolved; 239 } 240 241 // test if vanilla name is current class name 242 if (currentClass==type) return true; 243 if (currentClass.getNameWithoutPackage().equals(type.getName())) { 244 type.setRedirect(currentClass); 245 return true; 246 } 247 248 return resolveFromModule(type,testModuleImports) || 249 resolveFromCompileUnit(type) || 250 resovleFromDefaultImports(type,testDefaultImports) || 251 resolveFromStaticInnerClasses(type,testStaticInnerClasses) || 252 resolveFromClassCache(type) || 253 resolveToClass(type) || 254 resolveToScript(type); 255 256 } 257 258 private boolean resolveFromClassCache(ClassNode type) { 259 String name = type.getName(); 260 Object val = cachedClasses.get(name); 261 if (val==null || val==NO_CLASS){ 262 return false; 263 } else { 264 setClass(type,(Class) val); 265 return true; 266 } 267 } 268 269 // NOTE: copied from GroovyClassLoader 270 private long getTimeStamp(Class cls) { 271 Field field; 272 Long o; 273 try { 274 field = cls.getField(Verifier.__TIMESTAMP); 275 o = (Long) field.get(null); 276 } catch (Exception e) { 277 return Long.MAX_VALUE; 278 } 279 return o.longValue(); 280 } 281 282 // NOTE: copied from GroovyClassLoader 283 private boolean isSourceNewer(URL source, Class cls) { 284 try { 285 long lastMod; 286 287 // Special handling for file:// protocol, as getLastModified() often reports 288 // incorrect results (-1) 289 if (source.getProtocol().equals("file")) { 290 // Coerce the file URL to a File 291 String path = source.getPath().replace('/', File.separatorChar).replace('|', ':'); 292 File file = new File(path); 293 lastMod = file.lastModified(); 294 } 295 else { 296 lastMod = source.openConnection().getLastModified(); 297 } 298 return lastMod > getTimeStamp(cls); 299 } catch (IOException e) { 300 // if the stream can't be opened, let's keep the old reference 301 return false; 302 } 303 } 304 305 306 private boolean resolveToScript(ClassNode type) { 307 String name = type.getName(); 308 if (cachedClasses.get(name)==NO_CLASS) return false; 309 if (cachedClasses.get(name)==SCRIPT) cachedClasses.put(name,NO_CLASS); 310 if (name.startsWith("java.")) return type.isResolved(); 311 //TODO: don't ignore inner static classes completly 312 if (name.indexOf('$')!=-1) return type.isResolved(); 313 ModuleNode module = currentClass.getModule(); 314 if (module.hasPackageName() && name.indexOf('.')==-1) return type.isResolved(); 315 // try to find a script from classpath 316 GroovyClassLoader gcl = compilationUnit.getClassLoader(); 317 URL url = null; 318 try { 319 url = gcl.getResourceLoader().loadGroovySource(name); 320 } catch (MalformedURLException e) { 321 // fall through and let the URL be null 322 } 323 if (url !=null) { 324 if (type.isResolved()) { 325 Class cls = type.getTypeClass(); 326 // if the file is not newer we don't want to recompile 327 if (!isSourceNewer(url,cls)) return true; 328 cachedClasses.remove(type.getName()); 329 type.setRedirect(null); 330 } 331 SourceUnit su = compilationUnit.addSource(url); 332 currentClass.getCompileUnit().addClassNodeToCompile(type,su); 333 return true; 334 } 335 // type may be resolved through the classloader before 336 return type.isResolved(); 337 } 338 339 340 private boolean resolveFromStaticInnerClasses(ClassNode type, boolean testStaticInnerClasses) { 341 // try to resolve a public static inner class' name 342 testStaticInnerClasses &= type.hasPackageName(); 343 if (testStaticInnerClasses) { 344 String name = type.getName(); 345 String replacedPointType = name; 346 int lastPoint = replacedPointType.lastIndexOf('.'); 347 replacedPointType = new StringBuffer() 348 .append(replacedPointType.substring(0, lastPoint)) 349 .append("$") 350 .append(replacedPointType.substring(lastPoint + 1)) 351 .toString(); 352 type.setName(replacedPointType); 353 if (resolve(type,false,false,true)) return true; 354 type.setName(name); 355 } 356 return false; 357 } 358 359 private boolean resovleFromDefaultImports(ClassNode type, boolean testDefaultImports) { 360 // test default imports 361 testDefaultImports &= !type.hasPackageName(); 362 if (testDefaultImports) { 363 for (int i = 0, size = DEFAULT_IMPORTS.length; i < size; i++) { 364 String packagePrefix = DEFAULT_IMPORTS[i]; 365 String name = type.getName(); 366 String fqn = packagePrefix+name; 367 type.setName(fqn); 368 if (resolve(type,false,false,false)) return true; 369 type.setName(name); 370 } 371 String name = type.getName(); 372 if (name.equals("BigInteger")) { 373 type.setRedirect(ClassHelper.BigInteger_TYPE); 374 return true; 375 } else if (name.equals("BigDecimal")) { 376 type.setRedirect(ClassHelper.BigDecimal_TYPE); 377 return true; 378 } 379 } 380 return false; 381 } 382 383 private boolean resolveFromCompileUnit(ClassNode type) { 384 // look into the compile unit if there is a class with that name 385 CompileUnit compileUnit = currentClass.getCompileUnit(); 386 if (compileUnit == null) return false; 387 ClassNode cuClass = compileUnit.getClass(type.getName()); 388 if (cuClass!=null) { 389 if (type!=cuClass) type.setRedirect(cuClass); 390 return true; 391 } 392 return false; 393 } 394 395 396 private void setClass(ClassNode n, Class cls) { 397 ClassNode cn = ClassHelper.make(cls); 398 n.setRedirect(cn); 399 } 400 401 private void ambigousClass(ClassNode type, ClassNode iType, String name, boolean resolved){ 402 if (resolved && !type.getName().equals(iType.getName())) { 403 addError("reference to "+name+" is ambigous, both class "+type.getName()+" and "+iType.getName()+" match",type); 404 } else { 405 type.setRedirect(iType); 406 } 407 } 408 409 private boolean resolveAliasFromModule(ClassNode type) { 410 ModuleNode module = currentClass.getModule(); 411 if (module==null) return false; 412 String name = type.getName(); 413 414 // check module node imports aliases 415 // the while loop enables a check for inner classes which are not fully imported, 416 // but visible as the surrounding class is imported and the inner class is public/protected static 417 String pname = name; 418 int index = name.length(); 419 /* 420 * we have a name foo.bar and an import foo.foo. This means foo.bar is possibly 421 * foo.foo.bar rather than foo.bar. This means to cut at the dot in foo.bar and 422 * foo for import 423 */ 424 while (true) { 425 pname = name.substring(0,index); 426 ClassNode aliasedNode = module.getImport(pname); 427 if (aliasedNode!=null) { 428 if (pname.length()==name.length()){ 429 // full match, no need to create a new class 430 type.setRedirect(aliasedNode); 431 return true; 432 } else { 433 //partial match 434 String newName = aliasedNode.getName()+name.substring(pname.length()); 435 type.setName(newName); 436 if (resolve(type,true,true,true)) return true; 437 // was not resolved soit was a fake match 438 type.setName(name); 439 } 440 } 441 index = pname.lastIndexOf('.'); 442 if (index==-1) break; 443 } 444 return false; 445 446 } 447 448 private boolean resolveFromModule(ClassNode type, boolean testModuleImports) { 449 ModuleNode module = currentClass.getModule(); 450 if (module==null) return false; 451 452 String name = type.getName(); 453 454 if (!type.hasPackageName() && module.hasPackageName()){ 455 type.setName(module.getPackageName()+name); 456 } 457 // look into the module node if there is a class with that name 458 List moduleClasses = module.getClasses(); 459 for (Iterator iter = moduleClasses.iterator(); iter.hasNext();) { 460 ClassNode mClass = (ClassNode) iter.next(); 461 if (mClass.getName().equals(type.getName())){ 462 if (mClass!=type) type.setRedirect(mClass); 463 return true; 464 } 465 } 466 type.setName(name); 467 468 if (testModuleImports) { 469 if (resolveAliasFromModule(type)) return true; 470 471 boolean resolved = false; 472 if (module.hasPackageName()) { 473 // check package this class is defined in 474 type.setName(module.getPackageName()+name); 475 resolved = resolve(type,false,false,false); 476 } 477 // check module node imports packages 478 List packages = module.getImportPackages(); 479 ClassNode iType = ClassHelper.makeWithoutCaching(name); 480 for (Iterator iter = packages.iterator(); iter.hasNext();) { 481 String packagePrefix = (String) iter.next(); 482 String fqn = packagePrefix+name; 483 iType.setName(fqn); 484 if (resolve(iType,false,false,true)) { 485 ambigousClass(type,iType,name,resolved); 486 return true; 487 } 488 iType.setName(name); 489 } 490 if (!resolved) type.setName(name); 491 return resolved; 492 } 493 return false; 494 } 495 496 private boolean resolveToClass(ClassNode type) { 497 String name = type.getName(); 498 if (cachedClasses.get(name)==NO_CLASS) return false; 499 if (currentClass.getModule().hasPackageName() && name.indexOf('.')==-1) return false; 500 GroovyClassLoader loader = compilationUnit.getClassLoader(); 501 Class cls = null; 502 try { 503 // NOTE: it's important to do no lookup against script files 504 // here since the GroovyClassLoader would create a new 505 // CompilationUnit 506 cls = loader.loadClass(name,false,true); 507 } catch (ClassNotFoundException cnfe) { 508 cachedClasses.put(name,SCRIPT); 509 return false; 510 } catch (CompilationFailedException cfe) { 511 compilationUnit.getErrorCollector().addErrorAndContinue(new ExceptionMessage(cfe,true,source)); 512 return false; 513 } 514 //TODO: the case of a NoClassDefFoundError needs a bit more research 515 // a simple recompilation is not possible it seems. The current class 516 // we are searching for is there, so we should mark that somehow. 517 // Basically the missing class needs to be completly compiled before 518 // we can again search for the current name. 519 /*catch (NoClassDefFoundError ncdfe) { 520 cachedClasses.put(name,SCRIPT); 521 return false; 522 }*/ 523 if (cls==null) return false; 524 cachedClasses.put(name,cls); 525 setClass(type,cls); 526 //NOTE: we return false here even if we found a class, 527 //but we want to give a possible script a chance to recompile. 528 //this can only be done if the loader was not the instance 529 //defining the class. 530 return cls.getClassLoader()==loader; 531 } 532 533 534 535 public Expression transform(Expression exp) { 536 if (exp==null) return null; 537 if (exp instanceof VariableExpression) { 538 return transformVariableExpression((VariableExpression) exp); 539 } else if (exp.getClass()==PropertyExpression.class) { 540 return transformPropertyExpression((PropertyExpression) exp); 541 } else if (exp instanceof DeclarationExpression) { 542 return transformDeclarationExpression((DeclarationExpression)exp); 543 } else if (exp instanceof BinaryExpression) { 544 return transformBinaryExpression((BinaryExpression)exp); 545 } else if (exp instanceof MethodCallExpression) { 546 return transformMethodCallExpression((MethodCallExpression)exp); 547 } else if (exp instanceof ClosureExpression) { 548 return transformClosureExpression((ClosureExpression) exp); 549 } else if (exp instanceof ConstructorCallExpression) { 550 return transformConstructorCallExpression((ConstructorCallExpression) exp); 551 } else { 552 resolveOrFail(exp.getType(),exp); 553 return exp.transformExpression(this); 554 } 555 } 556 557 558 private String lookupClassName(PropertyExpression pe) { 559 String name = ""; 560 for (Expression it = pe; it!=null; it = ((PropertyExpression)it).getObjectExpression()) { 561 if (it instanceof VariableExpression) { 562 VariableExpression ve = (VariableExpression) it; 563 // stop at super and this 564 if (ve==VariableExpression.SUPER_EXPRESSION || ve==VariableExpression.THIS_EXPRESSION) { 565 return null; 566 } 567 name= ve.getName()+"."+name; 568 break; 569 } 570 // anything other than PropertyExpressions, ClassExpression or 571 // VariableExpressions will stop resolving 572 else if (!(it.getClass()==PropertyExpression.class)) { 573 return null; 574 } else { 575 PropertyExpression current = (PropertyExpression) it; 576 String propertyPart = current.getPropertyAsString(); 577 // the class property stops resolving, dynamic property names too 578 if (propertyPart==null || propertyPart.equals("class")) { 579 return null; 580 } 581 name = propertyPart+"."+name; 582 } 583 } 584 if (name.length()>0) return name.substring(0,name.length()-1); 585 return null; 586 } 587 588 // iterate from the inner most to the outer and check for classes 589 // this check will ignore a .class property, for Exmaple Integer.class will be 590 // a PropertyExpression with the ClassExpression of Integer as objectExpression 591 // and class as property 592 private Expression correctClassClassChain(PropertyExpression pe){ 593 LinkedList stack = new LinkedList(); 594 ClassExpression found = null; 595 for (Expression it = pe; it!=null; it = ((PropertyExpression)it).getObjectExpression()) { 596 if (it instanceof ClassExpression) { 597 found = (ClassExpression) it; 598 break; 599 } else if (! (it.getClass()==PropertyExpression.class)) { 600 return pe; 601 } 602 stack.addFirst(it); 603 } 604 if (found==null) return pe; 605 606 if (stack.isEmpty()) return pe; 607 Object stackElement = stack.removeFirst(); 608 if (!(stackElement.getClass()==PropertyExpression.class)) return pe; 609 PropertyExpression classPropertyExpression = (PropertyExpression) stackElement; 610 String propertyNamePart = classPropertyExpression.getPropertyAsString(); 611 if (propertyNamePart==null || ! propertyNamePart.equals("class")) return pe; 612 613 if (stack.isEmpty()) return found; 614 stackElement = stack.removeFirst(); 615 if (!(stackElement.getClass()==PropertyExpression.class)) return pe; 616 PropertyExpression classPropertyExpressionContainer = (PropertyExpression) stackElement; 617 618 classPropertyExpressionContainer.setObjectExpression(found); 619 return pe; 620 } 621 622 protected Expression transformPropertyExpression(PropertyExpression pe) { 623 boolean itlp = isTopLevelProperty; 624 625 Expression objectExpression = pe.getObjectExpression(); 626 isTopLevelProperty = !(objectExpression.getClass()==PropertyExpression.class); 627 objectExpression = transform(objectExpression); 628 Expression property = transform(pe.getProperty()); 629 isTopLevelProperty = itlp; 630 631 boolean spreadSafe = pe.isSpreadSafe(); 632 pe = new PropertyExpression(objectExpression,property,pe.isSafe()); 633 pe.setSpreadSafe(spreadSafe); 634 635 String className = lookupClassName(pe); 636 if (className!=null) { 637 ClassNode type = ClassHelper.make(className); 638 if (resolve(type)) return new ClassExpression(type); 639 } 640 if (objectExpression instanceof ClassExpression && pe.getPropertyAsString()!=null){ 641 // possibly a inner class 642 ClassExpression ce = (ClassExpression) objectExpression; 643 ClassNode type = ClassHelper.make(ce.getType().getName()+"$"+pe.getPropertyAsString()); 644 if (resolve(type,false,false,false)) return new ClassExpression(type); 645 } 646 if (isTopLevelProperty) return correctClassClassChain(pe); 647 648 return pe; 649 } 650 651 protected Expression transformVariableExpression(VariableExpression ve) { 652 if (ve.getName().equals("this")) return VariableExpression.THIS_EXPRESSION; 653 if (ve.getName().equals("super")) return VariableExpression.SUPER_EXPRESSION; 654 Variable v = ve.getAccessedVariable(); 655 if (v instanceof DynamicVariable) { 656 ClassNode t = ClassHelper.make(ve.getName()); 657 if (resolve(t)) { 658 // the name is a type so remove it from the scoping 659 // as it is only a classvariable, it is only in 660 // referencedClassVariables, but must be removed 661 // for each parentscope too 662 for (VariableScope scope = currentScope; scope!=null && !scope.isRoot(); scope = scope.getParent()) { 663 if (scope.isRoot()) break; 664 if (scope.getReferencedClassVariables().remove(ve.getName())==null) break; 665 } 666 ClassExpression ce = new ClassExpression(t); 667 ce.setSourcePosition(ve); 668 return ce; 669 } else if (!inClosure && ve.isInStaticContext()) { 670 addError("the name "+v.getName()+" doesn't refer to a declared variable or class. The static"+ 671 " scope requires to declare variables before using them. If the variable should have"+ 672 " been a class check the spelling.",ve); 673 } 674 } 675 resolveOrFail(ve.getType(),ve); 676 return ve; 677 } 678 679 protected Expression transformBinaryExpression(BinaryExpression be) { 680 Expression left = transform(be.getLeftExpression()); 681 if (be.getOperation().getType()==Types.ASSIGNMENT_OPERATOR && left instanceof ClassExpression){ 682 ClassExpression ce = (ClassExpression) left; 683 addError("you tried to assign a value to "+ce.getType().getName(),be.getLeftExpression()); 684 return be; 685 } 686 if (left instanceof ClassExpression && be.getRightExpression() instanceof ListExpression) { 687 // we have C[] if the list is empty -> should be an array then! 688 ListExpression list = (ListExpression) be.getRightExpression(); 689 ClassExpression ce = (ClassExpression) left; 690 if (list.getExpressions().isEmpty()) { 691 return new ClassExpression(left.getType().makeArray()); 692 } 693 } 694 Expression right = transform(be.getRightExpression()); 695 Expression ret = new BinaryExpression(left,be.getOperation(),right); 696 ret.setSourcePosition(be); 697 return ret; 698 } 699 700 protected Expression transformClosureExpression(ClosureExpression ce) { 701 boolean oldInClosure = inClosure; 702 inClosure = true; 703 Parameter[] paras = ce.getParameters(); 704 if (paras!=null) { 705 for (int i=0; i<paras.length; i++) { 706 ClassNode t = paras[i].getType(); 707 resolveOrFail(t,ce); 708 } 709 } 710 Statement code = ce.getCode(); 711 if (code!=null) code.visit(this); 712 ClosureExpression newCe= new ClosureExpression(paras,code); 713 newCe.setVariableScope(ce.getVariableScope()); 714 newCe.setSourcePosition(ce); 715 inClosure = oldInClosure; 716 return newCe; 717 } 718 719 protected Expression transformConstructorCallExpression(ConstructorCallExpression cce){ 720 ClassNode type = cce.getType(); 721 resolveOrFail(type,cce); 722 Expression expr = cce.transformExpression(this); 723 return expr; 724 } 725 726 protected Expression transformMethodCallExpression(MethodCallExpression mce) { 727 Expression obj = mce.getObjectExpression(); 728 Expression newObject = transform(obj); 729 Expression args = transform(mce.getArguments()); 730 Expression method = transform(mce.getMethod()); 731 MethodCallExpression ret = new MethodCallExpression(newObject,method,args); 732 ret.setSafe(mce.isSafe()); 733 ret.setImplicitThis(mce.isImplicitThis()); 734 ret.setSpreadSafe(mce.isSpreadSafe()); 735 ret.setSourcePosition(mce); 736 return ret; 737 } 738 739 protected Expression transformDeclarationExpression(DeclarationExpression de) { 740 Expression oldLeft = de.getLeftExpression(); 741 Expression left = transform(oldLeft); 742 if (left!=oldLeft){ 743 ClassExpression ce = (ClassExpression) left; 744 addError("you tried to assign a value to "+ce.getType().getName(),oldLeft); 745 return de; 746 } 747 Expression right = transform(de.getRightExpression()); 748 if (right==de.getRightExpression()) return de; 749 return new DeclarationExpression((VariableExpression) left,de.getOperation(),right); 750 } 751 752 public void visitAnnotations(AnnotatedNode node) { 753 Map annotionMap = node.getAnnotations(); 754 if (annotionMap.isEmpty()) return; 755 Iterator it = annotionMap.values().iterator(); 756 while (it.hasNext()) { 757 AnnotationNode an = (AnnotationNode) it.next(); 758 //skip builtin properties 759 if (an.isBuiltIn()) continue; 760 ClassNode type = an.getClassNode(); 761 resolveOrFail(type,"unable to find class for annotation",an); 762 } 763 } 764 765 public void visitClass(ClassNode node) { 766 ClassNode oldNode = currentClass; 767 currentClass = node; 768 769 ModuleNode module = node.getModule(); 770 if (!module.hasImportsResolved()) { 771 List l = module.getImports(); 772 for (Iterator iter = l.iterator(); iter.hasNext();) { 773 ImportNode element = (ImportNode) iter.next(); 774 ClassNode type = element.getType(); 775 if (resolve(type,false,false,false)) continue; 776 addError("unable to resolve class "+type.getName(),type); 777 } 778 module.setImportsResolved(true); 779 } 780 781 ClassNode sn = node.getUnresolvedSuperClass(); 782 if (sn!=null) resolveOrFail(sn,node,true); 783 ClassNode[] interfaces = node.getInterfaces(); 784 for (int i=0; i<interfaces.length; i++) { 785 resolveOrFail(interfaces[i],node,true); 786 } 787 super.visitClass(node); 788 currentClass = oldNode; 789 } 790 791 public void visitReturnStatement(ReturnStatement statement) { 792 statement.setExpression(transform(statement.getExpression())); 793 } 794 795 public void visitAssertStatement(AssertStatement as) { 796 as.setBooleanExpression((BooleanExpression) (transform(as.getBooleanExpression()))); 797 as.setMessageExpression(transform(as.getMessageExpression())); 798 } 799 800 public void visitCaseStatement(CaseStatement statement) { 801 statement.setExpression(transform(statement.getExpression())); 802 statement.getCode().visit(this); 803 } 804 805 public void visitCatchStatement(CatchStatement cs) { 806 resolveOrFail(cs.getExceptionType(),cs); 807 if (cs.getExceptionType()==ClassHelper.DYNAMIC_TYPE) { 808 cs.getVariable().setType(ClassHelper.make(Exception.class)); 809 } 810 super.visitCatchStatement(cs); 811 } 812 813 public void visitDoWhileLoop(DoWhileStatement loop) { 814 loop.setBooleanExpression((BooleanExpression) (transform(loop.getBooleanExpression()))); 815 super.visitDoWhileLoop(loop); 816 } 817 818 public void visitForLoop(ForStatement forLoop) { 819 forLoop.setCollectionExpression(transform(forLoop.getCollectionExpression())); 820 resolveOrFail(forLoop.getVariableType(),forLoop); 821 super.visitForLoop(forLoop); 822 } 823 824 public void visitSynchronizedStatement(SynchronizedStatement sync) { 825 sync.setExpression(transform(sync.getExpression())); 826 super.visitSynchronizedStatement(sync); 827 } 828 829 public void visitThrowStatement(ThrowStatement ts) { 830 ts.setExpression(transform(ts.getExpression())); 831 } 832 833 public void visitWhileLoop(WhileStatement loop) { 834 loop.setBooleanExpression((BooleanExpression) transform(loop.getBooleanExpression())); 835 super.visitWhileLoop(loop); 836 } 837 838 public void visitExpressionStatement(ExpressionStatement es) { 839 es.setExpression(transform(es.getExpression())); 840 } 841 842 public void visitBlockStatement(BlockStatement block) { 843 VariableScope oldScope = currentScope; 844 currentScope = block.getVariableScope(); 845 super.visitBlockStatement(block); 846 currentScope = oldScope; 847 } 848 849 protected SourceUnit getSourceUnit() { 850 return source; 851 } 852 }