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
109: ExpressionTransformer {
110: private ClassNode currentClass;
111: // note: BigInteger and BigDecimal are also imported by default
112: private static final String[] DEFAULT_IMPORTS = { "java.lang.",
113: "java.io.", "java.net.", "java.util.", "groovy.lang.",
114: "groovy.util." };
115: private CompilationUnit compilationUnit;
116: private Map cachedClasses = new HashMap();
117: private static final Object NO_CLASS = new Object();
118: private static final Object SCRIPT = new Object();
119: private SourceUnit source;
120: private VariableScope currentScope;
121:
122: private boolean isTopLevelProperty = true;
123: private boolean inClosure = false;
124:
125: public ResolveVisitor(CompilationUnit cu) {
126: compilationUnit = cu;
127: }
128:
129: public void startResolving(ClassNode node, SourceUnit source) {
130: this .source = source;
131: visitClass(node);
132: }
133:
134: public void visitConstructor(ConstructorNode node) {
135: visitAnnotations(node);
136: VariableScope oldScope = currentScope;
137: currentScope = node.getVariableScope();
138: Parameter[] paras = node.getParameters();
139: for (int i = 0; i < paras.length; i++) {
140: ClassNode t = paras[i].getType();
141: resolveOrFail(t, node);
142: }
143: ClassNode[] exceptions = node.getExceptions();
144: for (int i = 0; i < exceptions.length; i++) {
145: ClassNode t = exceptions[i];
146: resolveOrFail(t, node);
147: }
148: Statement code = node.getCode();
149: if (code != null)
150: code.visit(this );
151: currentScope = oldScope;
152: }
153:
154: public void visitSwitch(SwitchStatement statement) {
155: Expression exp = statement.getExpression();
156: statement.setExpression(transform(exp));
157: List list = statement.getCaseStatements();
158: for (Iterator iter = list.iterator(); iter.hasNext();) {
159: CaseStatement caseStatement = (CaseStatement) iter.next();
160: caseStatement.visit(this );
161: }
162: statement.getDefaultStatement().visit(this );
163: }
164:
165: public void visitMethod(MethodNode node) {
166: visitAnnotations(node);
167: VariableScope oldScope = currentScope;
168: currentScope = node.getVariableScope();
169: Parameter[] paras = node.getParameters();
170: for (int i = 0; i < paras.length; i++) {
171: ClassNode t = paras[i].getType();
172: resolveOrFail(t, node);
173: if (paras[i].hasInitialExpression()) {
174: Expression init = paras[i].getInitialExpression();
175: paras[i].setInitialExpression(transform(init));
176: }
177: }
178: ClassNode[] exceptions = node.getExceptions();
179: for (int i = 0; i < exceptions.length; i++) {
180: ClassNode t = exceptions[i];
181: resolveOrFail(t, node);
182: }
183: resolveOrFail(node.getReturnType(), node);
184: Statement code = node.getCode();
185: if (code != null)
186: code.visit(this );
187: currentScope = oldScope;
188: }
189:
190: public void visitField(FieldNode node) {
191: visitAnnotations(node);
192: ClassNode t = node.getType();
193: resolveOrFail(t, node);
194: Expression init = node.getInitialExpression();
195: node.setInitialValueExpression(transform(init));
196: }
197:
198: public void visitProperty(PropertyNode node) {
199: visitAnnotations(node);
200: ClassNode t = node.getType();
201: resolveOrFail(t, node);
202: Statement code = node.getGetterBlock();
203: if (code != null)
204: code.visit(this );
205: code = node.getSetterBlock();
206: if (code != null)
207: code.visit(this );
208: }
209:
210: public void visitIfElse(IfStatement ifElse) {
211: visitStatement(ifElse);
212: ifElse
213: .setBooleanExpression((BooleanExpression) (transform(ifElse
214: .getBooleanExpression())));
215: ifElse.getIfBlock().visit(this );
216: ifElse.getElseBlock().visit(this );
217: }
218:
219: private void resolveOrFail(ClassNode type, String msg, ASTNode node) {
220: if (resolve(type))
221: return;
222: addError("unable to resolve class " + type.getName() + " "
223: + msg, node);
224: }
225:
226: private void resolveOrFail(ClassNode type, ASTNode node,
227: boolean prefereImports) {
228: if (prefereImports && resolveAliasFromModule(type))
229: return;
230: resolveOrFail(type, node);
231: }
232:
233: private void resolveOrFail(ClassNode type, ASTNode node) {
234: resolveOrFail(type, "", node);
235: }
236:
237: private boolean resolve(ClassNode type) {
238: String name = type.getName();
239: return resolve(type, true, true, true);
240: }
241:
242: private boolean resolve(ClassNode type, boolean testModuleImports,
243: boolean testDefaultImports, boolean testStaticInnerClasses) {
244: if (type.isResolved())
245: return true;
246: if (type.isArray()) {
247: ClassNode element = type.getComponentType();
248: boolean resolved = resolve(element, testModuleImports,
249: testDefaultImports, testStaticInnerClasses);
250: if (resolved) {
251: ClassNode cn = element.makeArray();
252: type.setRedirect(cn);
253: }
254: return resolved;
255: }
256:
257: // test if vanilla name is current class name
258: if (currentClass == type)
259: return true;
260: if (currentClass.getNameWithoutPackage().equals(type.getName())) {
261: type.setRedirect(currentClass);
262: return true;
263: }
264:
265: return resolveFromModule(type, testModuleImports)
266: || resolveFromCompileUnit(type)
267: || resovleFromDefaultImports(type, testDefaultImports)
268: || resolveFromStaticInnerClasses(type,
269: testStaticInnerClasses)
270: || resolveFromClassCache(type) || resolveToClass(type)
271: || resolveToScript(type);
272:
273: }
274:
275: private boolean resolveFromClassCache(ClassNode type) {
276: String name = type.getName();
277: Object val = cachedClasses.get(name);
278: if (val == null || val == NO_CLASS) {
279: return false;
280: } else {
281: setClass(type, (Class) val);
282: return true;
283: }
284: }
285:
286: // NOTE: copied from GroovyClassLoader
287: private long getTimeStamp(Class cls) {
288: Field field;
289: Long o;
290: try {
291: field = cls.getField(Verifier.__TIMESTAMP);
292: o = (Long) field.get(null);
293: } catch (Exception e) {
294: return Long.MAX_VALUE;
295: }
296: return o.longValue();
297: }
298:
299: // NOTE: copied from GroovyClassLoader
300: private boolean isSourceNewer(URL source, Class cls) {
301: try {
302: long lastMod;
303:
304: // Special handling for file:// protocol, as getLastModified() often reports
305: // incorrect results (-1)
306: if (source.getProtocol().equals("file")) {
307: // Coerce the file URL to a File
308: String path = source.getPath().replace('/',
309: File.separatorChar).replace('|', ':');
310: File file = new File(path);
311: lastMod = file.lastModified();
312: } else {
313: lastMod = source.openConnection().getLastModified();
314: }
315: return lastMod > getTimeStamp(cls);
316: } catch (IOException e) {
317: // if the stream can't be opened, let's keep the old reference
318: return false;
319: }
320: }
321:
322: private boolean resolveToScript(ClassNode type) {
323: String name = type.getName();
324: if (cachedClasses.get(name) == NO_CLASS)
325: return false;
326: if (cachedClasses.get(name) == SCRIPT)
327: cachedClasses.put(name, NO_CLASS);
328: if (name.startsWith("java."))
329: return type.isResolved();
330: //TODO: don't ignore inner static classes completly
331: if (name.indexOf('$') != -1)
332: return type.isResolved();
333: ModuleNode module = currentClass.getModule();
334: if (module.hasPackageName() && name.indexOf('.') == -1)
335: return type.isResolved();
336: // try to find a script from classpath
337: GroovyClassLoader gcl = compilationUnit.getClassLoader();
338: URL url = null;
339: try {
340: url = gcl.getResourceLoader().loadGroovySource(name);
341: } catch (MalformedURLException e) {
342: // fall through and let the URL be null
343: }
344: if (url != null) {
345: if (type.isResolved()) {
346: Class cls = type.getTypeClass();
347: // if the file is not newer we don't want to recompile
348: if (!isSourceNewer(url, cls))
349: return true;
350: cachedClasses.remove(type.getName());
351: type.setRedirect(null);
352: }
353: SourceUnit su = compilationUnit.addSource(url);
354: currentClass.getCompileUnit().addClassNodeToCompile(type,
355: su);
356: return true;
357: }
358: // type may be resolved through the classloader before
359: return type.isResolved();
360: }
361:
362: private boolean resolveFromStaticInnerClasses(ClassNode type,
363: boolean testStaticInnerClasses) {
364: // try to resolve a public static inner class' name
365: testStaticInnerClasses &= type.hasPackageName();
366: if (testStaticInnerClasses) {
367: String name = type.getName();
368: String replacedPointType = name;
369: int lastPoint = replacedPointType.lastIndexOf('.');
370: replacedPointType = new StringBuffer().append(
371: replacedPointType.substring(0, lastPoint)).append(
372: "$").append(
373: replacedPointType.substring(lastPoint + 1))
374: .toString();
375: type.setName(replacedPointType);
376: if (resolve(type, false, false, true))
377: return true;
378: type.setName(name);
379: }
380: return false;
381: }
382:
383: private boolean resovleFromDefaultImports(ClassNode type,
384: boolean testDefaultImports) {
385: // test default imports
386: testDefaultImports &= !type.hasPackageName();
387: if (testDefaultImports) {
388: for (int i = 0, size = DEFAULT_IMPORTS.length; i < size; i++) {
389: String packagePrefix = DEFAULT_IMPORTS[i];
390: String name = type.getName();
391: String fqn = packagePrefix + name;
392: type.setName(fqn);
393: if (resolve(type, false, false, false))
394: return true;
395: type.setName(name);
396: }
397: String name = type.getName();
398: if (name.equals("BigInteger")) {
399: type.setRedirect(ClassHelper.BigInteger_TYPE);
400: return true;
401: } else if (name.equals("BigDecimal")) {
402: type.setRedirect(ClassHelper.BigDecimal_TYPE);
403: return true;
404: }
405: }
406: return false;
407: }
408:
409: private boolean resolveFromCompileUnit(ClassNode type) {
410: // look into the compile unit if there is a class with that name
411: CompileUnit compileUnit = currentClass.getCompileUnit();
412: if (compileUnit == null)
413: return false;
414: ClassNode cuClass = compileUnit.getClass(type.getName());
415: if (cuClass != null) {
416: if (type != cuClass)
417: type.setRedirect(cuClass);
418: return true;
419: }
420: return false;
421: }
422:
423: private void setClass(ClassNode n, Class cls) {
424: ClassNode cn = ClassHelper.make(cls);
425: n.setRedirect(cn);
426: }
427:
428: private void ambigousClass(ClassNode type, ClassNode iType,
429: String name, boolean resolved) {
430: if (resolved && !type.getName().equals(iType.getName())) {
431: addError("reference to " + name
432: + " is ambigous, both class " + type.getName()
433: + " and " + iType.getName() + " match", type);
434: } else {
435: type.setRedirect(iType);
436: }
437: }
438:
439: private boolean resolveAliasFromModule(ClassNode type) {
440: ModuleNode module = currentClass.getModule();
441: if (module == null)
442: return false;
443: String name = type.getName();
444:
445: // check module node imports aliases
446: // the while loop enables a check for inner classes which are not fully imported,
447: // but visible as the surrounding class is imported and the inner class is public/protected static
448: String pname = name;
449: int index = name.length();
450: /*
451: * we have a name foo.bar and an import foo.foo. This means foo.bar is possibly
452: * foo.foo.bar rather than foo.bar. This means to cut at the dot in foo.bar and
453: * foo for import
454: */
455: while (true) {
456: pname = name.substring(0, index);
457: ClassNode aliasedNode = module.getImport(pname);
458: if (aliasedNode != null) {
459: if (pname.length() == name.length()) {
460: // full match, no need to create a new class
461: type.setRedirect(aliasedNode);
462: return true;
463: } else {
464: //partial match
465: String newName = aliasedNode.getName()
466: + name.substring(pname.length());
467: type.setName(newName);
468: if (resolve(type, true, true, true))
469: return true;
470: // was not resolved soit was a fake match
471: type.setName(name);
472: }
473: }
474: index = pname.lastIndexOf('.');
475: if (index == -1)
476: break;
477: }
478: return false;
479:
480: }
481:
482: private boolean resolveFromModule(ClassNode type,
483: boolean testModuleImports) {
484: ModuleNode module = currentClass.getModule();
485: if (module == null)
486: return false;
487:
488: String name = type.getName();
489:
490: if (!type.hasPackageName() && module.hasPackageName()) {
491: type.setName(module.getPackageName() + name);
492: }
493: // look into the module node if there is a class with that name
494: List moduleClasses = module.getClasses();
495: for (Iterator iter = moduleClasses.iterator(); iter.hasNext();) {
496: ClassNode mClass = (ClassNode) iter.next();
497: if (mClass.getName().equals(type.getName())) {
498: if (mClass != type)
499: type.setRedirect(mClass);
500: return true;
501: }
502: }
503: type.setName(name);
504:
505: if (testModuleImports) {
506: if (resolveAliasFromModule(type))
507: return true;
508:
509: boolean resolved = false;
510: if (module.hasPackageName()) {
511: // check package this class is defined in
512: type.setName(module.getPackageName() + name);
513: resolved = resolve(type, false, false, false);
514: }
515: // check module node imports packages
516: List packages = module.getImportPackages();
517: ClassNode iType = ClassHelper.makeWithoutCaching(name);
518: for (Iterator iter = packages.iterator(); iter.hasNext();) {
519: String packagePrefix = (String) iter.next();
520: String fqn = packagePrefix + name;
521: iType.setName(fqn);
522: if (resolve(iType, false, false, true)) {
523: ambigousClass(type, iType, name, resolved);
524: return true;
525: }
526: iType.setName(name);
527: }
528: if (!resolved)
529: type.setName(name);
530: return resolved;
531: }
532: return false;
533: }
534:
535: private boolean resolveToClass(ClassNode type) {
536: String name = type.getName();
537: if (cachedClasses.get(name) == NO_CLASS)
538: return false;
539: if (currentClass.getModule().hasPackageName()
540: && name.indexOf('.') == -1)
541: return false;
542: GroovyClassLoader loader = compilationUnit.getClassLoader();
543: Class cls = null;
544: try {
545: // NOTE: it's important to do no lookup against script files
546: // here since the GroovyClassLoader would create a new
547: // CompilationUnit
548: cls = loader.loadClass(name, false, true);
549: } catch (ClassNotFoundException cnfe) {
550: cachedClasses.put(name, SCRIPT);
551: return false;
552: } catch (CompilationFailedException cfe) {
553: compilationUnit.getErrorCollector().addErrorAndContinue(
554: new ExceptionMessage(cfe, true, source));
555: return false;
556: }
557: //TODO: the case of a NoClassDefFoundError needs a bit more research
558: // a simple recompilation is not possible it seems. The current class
559: // we are searching for is there, so we should mark that somehow.
560: // Basically the missing class needs to be completly compiled before
561: // we can again search for the current name.
562: /*catch (NoClassDefFoundError ncdfe) {
563: cachedClasses.put(name,SCRIPT);
564: return false;
565: }*/
566: if (cls == null)
567: return false;
568: cachedClasses.put(name, cls);
569: setClass(type, cls);
570: //NOTE: we return false here even if we found a class,
571: //but we want to give a possible script a chance to recompile.
572: //this can only be done if the loader was not the instance
573: //defining the class.
574: return cls.getClassLoader() == loader;
575: }
576:
577: public Expression transform(Expression exp) {
578: if (exp == null)
579: return null;
580: if (exp instanceof VariableExpression) {
581: return transformVariableExpression((VariableExpression) exp);
582: } else if (exp.getClass() == PropertyExpression.class) {
583: return transformPropertyExpression((PropertyExpression) exp);
584: } else if (exp instanceof DeclarationExpression) {
585: return transformDeclarationExpression((DeclarationExpression) exp);
586: } else if (exp instanceof BinaryExpression) {
587: return transformBinaryExpression((BinaryExpression) exp);
588: } else if (exp instanceof MethodCallExpression) {
589: return transformMethodCallExpression((MethodCallExpression) exp);
590: } else if (exp instanceof ClosureExpression) {
591: return transformClosureExpression((ClosureExpression) exp);
592: } else if (exp instanceof ConstructorCallExpression) {
593: return transformConstructorCallExpression((ConstructorCallExpression) exp);
594: } else {
595: resolveOrFail(exp.getType(), exp);
596: return exp.transformExpression(this );
597: }
598: }
599:
600: private String lookupClassName(PropertyExpression pe) {
601: String name = "";
602: for (Expression it = pe; it != null; it = ((PropertyExpression) it)
603: .getObjectExpression()) {
604: if (it instanceof VariableExpression) {
605: VariableExpression ve = (VariableExpression) it;
606: // stop at super and this
607: if (ve == VariableExpression.SUPER_EXPRESSION
608: || ve == VariableExpression.THIS_EXPRESSION) {
609: return null;
610: }
611: name = ve.getName() + "." + name;
612: break;
613: }
614: // anything other than PropertyExpressions, ClassExpression or
615: // VariableExpressions will stop resolving
616: else if (!(it.getClass() == PropertyExpression.class)) {
617: return null;
618: } else {
619: PropertyExpression current = (PropertyExpression) it;
620: String propertyPart = current.getPropertyAsString();
621: // the class property stops resolving, dynamic property names too
622: if (propertyPart == null
623: || propertyPart.equals("class")) {
624: return null;
625: }
626: name = propertyPart + "." + name;
627: }
628: }
629: if (name.length() > 0)
630: return name.substring(0, name.length() - 1);
631: return null;
632: }
633:
634: // iterate from the inner most to the outer and check for classes
635: // this check will ignore a .class property, for Exmaple Integer.class will be
636: // a PropertyExpression with the ClassExpression of Integer as objectExpression
637: // and class as property
638: private Expression correctClassClassChain(PropertyExpression pe) {
639: LinkedList stack = new LinkedList();
640: ClassExpression found = null;
641: for (Expression it = pe; it != null; it = ((PropertyExpression) it)
642: .getObjectExpression()) {
643: if (it instanceof ClassExpression) {
644: found = (ClassExpression) it;
645: break;
646: } else if (!(it.getClass() == PropertyExpression.class)) {
647: return pe;
648: }
649: stack.addFirst(it);
650: }
651: if (found == null)
652: return pe;
653:
654: if (stack.isEmpty())
655: return pe;
656: Object stackElement = stack.removeFirst();
657: if (!(stackElement.getClass() == PropertyExpression.class))
658: return pe;
659: PropertyExpression classPropertyExpression = (PropertyExpression) stackElement;
660: String propertyNamePart = classPropertyExpression
661: .getPropertyAsString();
662: if (propertyNamePart == null
663: || !propertyNamePart.equals("class"))
664: return pe;
665:
666: if (stack.isEmpty())
667: return found;
668: stackElement = stack.removeFirst();
669: if (!(stackElement.getClass() == PropertyExpression.class))
670: return pe;
671: PropertyExpression classPropertyExpressionContainer = (PropertyExpression) stackElement;
672:
673: classPropertyExpressionContainer.setObjectExpression(found);
674: return pe;
675: }
676:
677: protected Expression transformPropertyExpression(
678: PropertyExpression pe) {
679: boolean itlp = isTopLevelProperty;
680:
681: Expression objectExpression = pe.getObjectExpression();
682: isTopLevelProperty = !(objectExpression.getClass() == PropertyExpression.class);
683: objectExpression = transform(objectExpression);
684: Expression property = transform(pe.getProperty());
685: isTopLevelProperty = itlp;
686:
687: boolean spreadSafe = pe.isSpreadSafe();
688: pe = new PropertyExpression(objectExpression, property, pe
689: .isSafe());
690: pe.setSpreadSafe(spreadSafe);
691:
692: String className = lookupClassName(pe);
693: if (className != null) {
694: ClassNode type = ClassHelper.make(className);
695: if (resolve(type))
696: return new ClassExpression(type);
697: }
698: if (objectExpression instanceof ClassExpression
699: && pe.getPropertyAsString() != null) {
700: // possibly a inner class
701: ClassExpression ce = (ClassExpression) objectExpression;
702: ClassNode type = ClassHelper.make(ce.getType().getName()
703: + "$" + pe.getPropertyAsString());
704: if (resolve(type, false, false, false))
705: return new ClassExpression(type);
706: }
707: if (isTopLevelProperty)
708: return correctClassClassChain(pe);
709:
710: return pe;
711: }
712:
713: protected Expression transformVariableExpression(
714: VariableExpression ve) {
715: if (ve.getName().equals("this"))
716: return VariableExpression.THIS_EXPRESSION;
717: if (ve.getName().equals("super"))
718: return VariableExpression.SUPER_EXPRESSION;
719: Variable v = ve.getAccessedVariable();
720: if (v instanceof DynamicVariable) {
721: ClassNode t = ClassHelper.make(ve.getName());
722: if (resolve(t)) {
723: // the name is a type so remove it from the scoping
724: // as it is only a classvariable, it is only in
725: // referencedClassVariables, but must be removed
726: // for each parentscope too
727: for (VariableScope scope = currentScope; scope != null
728: && !scope.isRoot(); scope = scope.getParent()) {
729: if (scope.isRoot())
730: break;
731: if (scope.getReferencedClassVariables().remove(
732: ve.getName()) == null)
733: break;
734: }
735: ClassExpression ce = new ClassExpression(t);
736: ce.setSourcePosition(ve);
737: return ce;
738: } else if (!inClosure && ve.isInStaticContext()) {
739: addError(
740: "the name "
741: + v.getName()
742: + " doesn't refer to a declared variable or class. The static"
743: + " scope requires to declare variables before using them. If the variable should have"
744: + " been a class check the spelling.",
745: ve);
746: }
747: }
748: resolveOrFail(ve.getType(), ve);
749: return ve;
750: }
751:
752: protected Expression transformBinaryExpression(BinaryExpression be) {
753: Expression left = transform(be.getLeftExpression());
754: if (be.getOperation().getType() == Types.ASSIGNMENT_OPERATOR
755: && left instanceof ClassExpression) {
756: ClassExpression ce = (ClassExpression) left;
757: addError("you tried to assign a value to "
758: + ce.getType().getName(), be.getLeftExpression());
759: return be;
760: }
761: if (left instanceof ClassExpression
762: && be.getRightExpression() instanceof ListExpression) {
763: // we have C[] if the list is empty -> should be an array then!
764: ListExpression list = (ListExpression) be
765: .getRightExpression();
766: ClassExpression ce = (ClassExpression) left;
767: if (list.getExpressions().isEmpty()) {
768: return new ClassExpression(left.getType().makeArray());
769: }
770: }
771: Expression right = transform(be.getRightExpression());
772: Expression ret = new BinaryExpression(left, be.getOperation(),
773: right);
774: ret.setSourcePosition(be);
775: return ret;
776: }
777:
778: protected Expression transformClosureExpression(ClosureExpression ce) {
779: boolean oldInClosure = inClosure;
780: inClosure = true;
781: Parameter[] paras = ce.getParameters();
782: if (paras != null) {
783: for (int i = 0; i < paras.length; i++) {
784: ClassNode t = paras[i].getType();
785: resolveOrFail(t, ce);
786: }
787: }
788: Statement code = ce.getCode();
789: if (code != null)
790: code.visit(this );
791: ClosureExpression newCe = new ClosureExpression(paras, code);
792: newCe.setVariableScope(ce.getVariableScope());
793: newCe.setSourcePosition(ce);
794: inClosure = oldInClosure;
795: return newCe;
796: }
797:
798: protected Expression transformConstructorCallExpression(
799: ConstructorCallExpression cce) {
800: ClassNode type = cce.getType();
801: resolveOrFail(type, cce);
802: Expression expr = cce.transformExpression(this );
803: return expr;
804: }
805:
806: protected Expression transformMethodCallExpression(
807: MethodCallExpression mce) {
808: Expression obj = mce.getObjectExpression();
809: Expression newObject = transform(obj);
810: Expression args = transform(mce.getArguments());
811: Expression method = transform(mce.getMethod());
812: MethodCallExpression ret = new MethodCallExpression(newObject,
813: method, args);
814: ret.setSafe(mce.isSafe());
815: ret.setImplicitThis(mce.isImplicitThis());
816: ret.setSpreadSafe(mce.isSpreadSafe());
817: ret.setSourcePosition(mce);
818: return ret;
819: }
820:
821: protected Expression transformDeclarationExpression(
822: DeclarationExpression de) {
823: Expression oldLeft = de.getLeftExpression();
824: Expression left = transform(oldLeft);
825: if (left != oldLeft) {
826: ClassExpression ce = (ClassExpression) left;
827: addError("you tried to assign a value to "
828: + ce.getType().getName(), oldLeft);
829: return de;
830: }
831: Expression right = transform(de.getRightExpression());
832: if (right == de.getRightExpression())
833: return de;
834: return new DeclarationExpression((VariableExpression) left, de
835: .getOperation(), right);
836: }
837:
838: public void visitAnnotations(AnnotatedNode node) {
839: Map annotionMap = node.getAnnotations();
840: if (annotionMap.isEmpty())
841: return;
842: Iterator it = annotionMap.values().iterator();
843: while (it.hasNext()) {
844: AnnotationNode an = (AnnotationNode) it.next();
845: //skip builtin properties
846: if (an.isBuiltIn())
847: continue;
848: ClassNode type = an.getClassNode();
849: resolveOrFail(type, "unable to find class for annotation",
850: an);
851: }
852: }
853:
854: public void visitClass(ClassNode node) {
855: ClassNode oldNode = currentClass;
856: currentClass = node;
857:
858: ModuleNode module = node.getModule();
859: if (!module.hasImportsResolved()) {
860: List l = module.getImports();
861: for (Iterator iter = l.iterator(); iter.hasNext();) {
862: ImportNode element = (ImportNode) iter.next();
863: ClassNode type = element.getType();
864: if (resolve(type, false, false, false))
865: continue;
866: addError("unable to resolve class " + type.getName(),
867: type);
868: }
869: module.setImportsResolved(true);
870: }
871:
872: ClassNode sn = node.getUnresolvedSuperClass();
873: if (sn != null)
874: resolveOrFail(sn, node, true);
875: ClassNode[] interfaces = node.getInterfaces();
876: for (int i = 0; i < interfaces.length; i++) {
877: resolveOrFail(interfaces[i], node, true);
878: }
879: super .visitClass(node);
880: currentClass = oldNode;
881: }
882:
883: public void visitReturnStatement(ReturnStatement statement) {
884: statement.setExpression(transform(statement.getExpression()));
885: }
886:
887: public void visitAssertStatement(AssertStatement as) {
888: as.setBooleanExpression((BooleanExpression) (transform(as
889: .getBooleanExpression())));
890: as.setMessageExpression(transform(as.getMessageExpression()));
891: }
892:
893: public void visitCaseStatement(CaseStatement statement) {
894: statement.setExpression(transform(statement.getExpression()));
895: statement.getCode().visit(this );
896: }
897:
898: public void visitCatchStatement(CatchStatement cs) {
899: resolveOrFail(cs.getExceptionType(), cs);
900: if (cs.getExceptionType() == ClassHelper.DYNAMIC_TYPE) {
901: cs.getVariable().setType(ClassHelper.make(Exception.class));
902: }
903: super .visitCatchStatement(cs);
904: }
905:
906: public void visitDoWhileLoop(DoWhileStatement loop) {
907: loop.setBooleanExpression((BooleanExpression) (transform(loop
908: .getBooleanExpression())));
909: super .visitDoWhileLoop(loop);
910: }
911:
912: public void visitForLoop(ForStatement forLoop) {
913: forLoop.setCollectionExpression(transform(forLoop
914: .getCollectionExpression()));
915: resolveOrFail(forLoop.getVariableType(), forLoop);
916: super .visitForLoop(forLoop);
917: }
918:
919: public void visitSynchronizedStatement(SynchronizedStatement sync) {
920: sync.setExpression(transform(sync.getExpression()));
921: super .visitSynchronizedStatement(sync);
922: }
923:
924: public void visitThrowStatement(ThrowStatement ts) {
925: ts.setExpression(transform(ts.getExpression()));
926: }
927:
928: public void visitWhileLoop(WhileStatement loop) {
929: loop.setBooleanExpression((BooleanExpression) transform(loop
930: .getBooleanExpression()));
931: super .visitWhileLoop(loop);
932: }
933:
934: public void visitExpressionStatement(ExpressionStatement es) {
935: es.setExpression(transform(es.getExpression()));
936: }
937:
938: public void visitBlockStatement(BlockStatement block) {
939: VariableScope oldScope = currentScope;
940: currentScope = block.getVariableScope();
941: super .visitBlockStatement(block);
942: currentScope = oldScope;
943: }
944:
945: protected SourceUnit getSourceUnit() {
946: return source;
947: }
948: }
|