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