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 }