0001: /* ====================================================================
0002: * Tea - Copyright (c) 1997-2000 Walt Disney Internet Group
0003: * ====================================================================
0004: * The Tea Software License, Version 1.1
0005: *
0006: * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
0007: *
0008: * Redistribution and use in source and binary forms, with or without
0009: * modification, are permitted provided that the following conditions
0010: * are met:
0011: *
0012: * 1. Redistributions of source code must retain the above copyright
0013: * notice, this list of conditions and the following disclaimer.
0014: *
0015: * 2. Redistributions in binary form must reproduce the above copyright
0016: * notice, this list of conditions and the following disclaimer in
0017: * the documentation and/or other materials provided with the
0018: * distribution.
0019: *
0020: * 3. The end-user documentation included with the redistribution,
0021: * if any, must include the following acknowledgment:
0022: * "This product includes software developed by the
0023: * Walt Disney Internet Group (http://opensource.go.com/)."
0024: * Alternately, this acknowledgment may appear in the software itself,
0025: * if and wherever such third-party acknowledgments normally appear.
0026: *
0027: * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
0028: * not be used to endorse or promote products derived from this
0029: * software without prior written permission. For written
0030: * permission, please contact opensource@dig.com.
0031: *
0032: * 5. Products derived from this software may not be called "Tea",
0033: * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
0034: * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
0035: * written permission of the Walt Disney Internet Group.
0036: *
0037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0040: * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
0041: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0042: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0043: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0044: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
0045: * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0046: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0047: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0048: * ====================================================================
0049: *
0050: * For more information about Tea, please see http://opensource.go.com/.
0051: */
0052:
0053: package com.go.tea.compiler;
0054:
0055: import java.util.Vector;
0056: import java.util.Collection;
0057: import java.util.Map;
0058: import java.util.List;
0059: import java.util.ArrayList;
0060: import java.util.Stack;
0061: import java.lang.reflect.Array;
0062: import java.lang.reflect.Method;
0063: import java.lang.reflect.Modifier;
0064: import java.beans.*;
0065: import com.go.tea.parsetree.*;
0066: import com.go.tea.util.BeanAnalyzer;
0067:
0068: /******************************************************************************
0069: * A TypeChecker operates on a template's parse tree, created by a
0070: * {@link Parser}, filling in type information while it checks the validity of
0071: * the whole template. After a template has been type-checked and there are no
0072: * errors, the template is ready for code generation. Add an
0073: * {@link ErrorListener} to capture any semantic errors detected by the
0074: * TypeChecker.
0075: *
0076: * @author Brian S O'Neill
0077: * @version
0078: * <!--$$Revision:--> 111 <!-- $-->, <!--$$JustDate:--> 7/26/01 <!-- $-->
0079: */
0080: public class TypeChecker {
0081: private CompilationUnit mUnit;
0082: private String[] mImports;
0083:
0084: private Vector mListeners = new Vector(1);
0085: private int mErrorCount = 0;
0086:
0087: private ClassLoader mClassLoader;
0088: private boolean mExceptionGuardian;
0089:
0090: private MessageFormatter mFormatter;
0091: private int mForeachCount;
0092:
0093: public TypeChecker(CompilationUnit unit) {
0094: mUnit = unit;
0095: mImports = mUnit.getImportedPackages();
0096: mFormatter = MessageFormatter.lookup(this );
0097: }
0098:
0099: public void addErrorListener(ErrorListener listener) {
0100: mListeners.addElement(listener);
0101: }
0102:
0103: public void removeErrorListener(ErrorListener listener) {
0104: mListeners.removeElement(listener);
0105: }
0106:
0107: private void dispatchParseError(ErrorEvent e) {
0108: mErrorCount++;
0109:
0110: synchronized (mListeners) {
0111: for (int i = 0; i < mListeners.size(); i++) {
0112: ((ErrorListener) mListeners.elementAt(i))
0113: .compileError(e);
0114: }
0115: }
0116: }
0117:
0118: private void error(String str, Node culprit) {
0119: str = mFormatter.format(str);
0120: dispatchParseError(new ErrorEvent(this , str, culprit
0121: .getSourceInfo(), mUnit));
0122: }
0123:
0124: private void error(String str, String arg, Node culprit) {
0125: str = mFormatter.format(str, arg);
0126: dispatchParseError(new ErrorEvent(this , str, culprit
0127: .getSourceInfo(), mUnit));
0128: }
0129:
0130: private void error(String str, String arg1, String arg2,
0131: Node culprit) {
0132: str = mFormatter.format(str, arg1, arg2);
0133: dispatchParseError(new ErrorEvent(this , str, culprit
0134: .getSourceInfo(), mUnit));
0135: }
0136:
0137: private void error(String str, String arg1, String arg2,
0138: String arg3, Node culprit) {
0139: str = mFormatter.format(str, arg1, arg2, arg3);
0140: dispatchParseError(new ErrorEvent(this , str, culprit
0141: .getSourceInfo(), mUnit));
0142: }
0143:
0144: private void error(String str, Token culprit) {
0145: str = mFormatter.format(str);
0146: dispatchParseError(new ErrorEvent(this , str, culprit
0147: .getSourceInfo(), mUnit));
0148: }
0149:
0150: private void error(String str, String arg, Token culprit) {
0151: str = mFormatter.format(str, arg);
0152: dispatchParseError(new ErrorEvent(this , str, culprit
0153: .getSourceInfo(), mUnit));
0154: }
0155:
0156: private void error(String str, SourceInfo info) {
0157: str = mFormatter.format(str);
0158: dispatchParseError(new ErrorEvent(this , str, info, mUnit));
0159: }
0160:
0161: private void error(String str, String arg, SourceInfo info) {
0162: str = mFormatter.format(str, arg);
0163: dispatchParseError(new ErrorEvent(this , str, info, mUnit));
0164: }
0165:
0166: private void error(String str, String arg1, String arg2,
0167: SourceInfo info) {
0168: str = mFormatter.format(str, arg1, arg2);
0169: dispatchParseError(new ErrorEvent(this , str, info, mUnit));
0170: }
0171:
0172: /**
0173: * Sets the ClassLoader to use to load classes with. If set to null,
0174: * then classes are loaded using Class.forName.
0175: */
0176: public void setClassLoader(ClassLoader loader) {
0177: mClassLoader = loader;
0178: }
0179:
0180: /**
0181: * Enabling Exception Guardian causes statements that might throw a
0182: * exception to be guarded. By default, this feature is off. If a guarded
0183: * statement throws a RuntimeException, it is caught and handed off to
0184: * {@link ThreadGroup.uncaughtException}. Execution then proceeds to the
0185: * next statement. If the guarded statement is an assignment, then the
0186: * variable is assigned null if an exception is caught.
0187: */
0188: public void setExceptionGuardianEnabled(boolean enabled) {
0189: mExceptionGuardian = enabled;
0190: }
0191:
0192: private Class loadClass(String name) throws ClassNotFoundException {
0193: while (true) {
0194: try {
0195: if (mClassLoader == null) {
0196: return Class.forName(name);
0197: } else {
0198: return mClassLoader.loadClass(name);
0199: }
0200: } catch (ClassNotFoundException e) {
0201: int index = name.lastIndexOf('.');
0202: if (index < 0) {
0203: throw e;
0204: }
0205:
0206: // Search for inner class.
0207: name = name.substring(0, index) + '$'
0208: + name.substring(index + 1);
0209: }
0210: }
0211: }
0212:
0213: public void typeCheck() {
0214: Template template = mUnit.getParseTree();
0215: Name name = template.getName();
0216:
0217: if (name != null) {
0218: if (!(mUnit.getShortName().equals(name.getName()))) {
0219: error("template.name", mUnit.getName(), name);
0220: }
0221: }
0222:
0223: // Convert some ExpressionStatements into ReturnStatements.
0224: template.accept(new ReturnConvertor());
0225:
0226: // Reduce the amount of concatenation operations.
0227: // It is important that this step be performed after using the
0228: // ReturnConvertor and before the main type checking. Otherwise, a
0229: // return may only capture a partial result or an expression may be
0230: // set to a type which is only appropriate for concatenation.
0231: //
0232: // Note: Decreasing the amount of concatenations can have a slight
0233: // impact on the language semantics depending on how the context is
0234: // defined. Generally, reducing the amount of concatenations improves
0235: // performance. If reducing the amount of concatenations hinders
0236: // performance, then the data buffering and/or output strategy in the
0237: // context may need to be improved.
0238: //
0239: //template.accept(new ConcatenationReducer());
0240:
0241: // Increase the amount of concatenation operations.
0242: // It is important that this step be performed after using the
0243: // ReturnConvertor and before the main type checking. Otherwise, a
0244: // return may capture too large of a result or an expression may be
0245: // set to a type which is only appropriate for output.
0246: //
0247: // Note: Increasing the amount of concatenations can have a profound
0248: // impact on the language semantics depending on how the context is
0249: // defined. Generally, increasing the amount of concatenations hinders
0250: // performance. If increasing the amount of concatenations improves
0251: // performance, then the data buffering and/or output strategy in the
0252: // context may need to be improved.
0253: //
0254: //template.accept(new ConcatenationIncreaser());
0255:
0256: // Perform major type checking.
0257: new Visitor().check(template);
0258:
0259: if (mErrorCount == 0 && mExceptionGuardian) {
0260: // Equip the parse tree with ExceptionGuard statements.
0261: template.accept(new ExceptionGuardian());
0262: }
0263: }
0264:
0265: public int getErrorCount() {
0266: return mErrorCount;
0267: }
0268:
0269: private class Visitor extends TreeWalker {
0270: private Type mReturnType;
0271: private Scope mScope = new Scope();
0272:
0273: private Stack mLoopVariables;
0274:
0275: public Visitor() {
0276: super ();
0277: mLoopVariables = new Stack();
0278: }
0279:
0280: private Scope enterScope() {
0281: return mScope = new Scope(mScope);
0282: }
0283:
0284: private Scope exitScope() {
0285: return mScope = mScope.getParent();
0286: }
0287:
0288: private void defineVariable(VariableRef ref, Type type) {
0289: mScope.declareVariable(new Variable(ref.getSourceInfo(),
0290: ref.getName(), type));
0291:
0292: if (!mScope.bindToVariable(ref)) {
0293: // Shouldn't happen.
0294: error("variable.undefined", ref.getName(), ref);
0295: }
0296: }
0297:
0298: public void check(Node node) {
0299: node.accept(this );
0300: }
0301:
0302: private void checkAccess(Class clazz, Node node) {
0303: if (!(Modifier.isPublic(clazz.getModifiers()))) {
0304: error("access.check", clazz.getName(), node);
0305: }
0306: }
0307:
0308: public Object visit(Template node) {
0309: enterScope();
0310:
0311: Variable[] declared = node.getParams();
0312: if (declared != null) {
0313: for (int i = 0; i < declared.length; i++) {
0314: check(declared[i]);
0315: }
0316: }
0317:
0318: Statement stmt = node.getStatement();
0319: if (stmt != null) {
0320: check(stmt);
0321: }
0322:
0323: Type returnType = mReturnType;
0324: if (returnType == null) {
0325: returnType = Type.VOID_TYPE;
0326: }
0327: node.setReturnType(returnType);
0328:
0329: exitScope();
0330: return null;
0331: }
0332:
0333: public Object visit(Name node) {
0334: return null;
0335: }
0336:
0337: public Object visit(TypeName node) {
0338: // Check that type name is a valid java class.
0339: String name = node.getName();
0340: String checkName = name;
0341: String errorMsg = null;
0342:
0343: for (int i = -1; i < mImports.length; i++) {
0344: if (i >= 0) {
0345: checkName = mImports[i] + '.' + name;
0346: }
0347:
0348: try {
0349: Class clazz = loadClass(checkName);
0350: checkAccess(clazz, node);
0351:
0352: // Found class, check if should be array.
0353: int dim = node.getDimensions();
0354: if (dim > 0) {
0355: clazz = Array.newInstance(clazz, new int[dim])
0356: .getClass();
0357: }
0358:
0359: node.setType(new Type(clazz));
0360: errorMsg = null;
0361: break;
0362: } catch (ClassNotFoundException e) {
0363: if (errorMsg == null) {
0364: errorMsg = mFormatter.format(
0365: "typename.unknown", name);
0366: }
0367: } catch (RuntimeException e) {
0368: error(e.toString(), node);
0369: } catch (LinkageError e) {
0370: error(e.toString(), node);
0371: return null;
0372: }
0373: }
0374:
0375: // Fall through to here only if class wasn't found.
0376: if (errorMsg != null) {
0377: error(errorMsg, node);
0378: }
0379:
0380: return null;
0381: }
0382:
0383: public Object visit(Variable node) {
0384: String name = node.getName();
0385: Variable v = mScope.getDeclaredVariable(name);
0386: if (v == null) {
0387: mScope.declareVariable(node);
0388: } else {
0389: error("variable.declared", name, node);
0390: error("variable.declared.here", name, v);
0391: }
0392:
0393: TypeName typeName = node.getTypeName();
0394: check(typeName);
0395: node.setType(typeName.getType());
0396:
0397: return null;
0398: }
0399:
0400: public Object visit(ExpressionList node) {
0401: Expression[] exprs = node.getExpressions();
0402: for (int i = 0; i < exprs.length; i++) {
0403: check(exprs[i]);
0404: }
0405: return null;
0406: }
0407:
0408: public Object visit(Statement node) {
0409: return null;
0410: }
0411:
0412: public Object visit(StatementList node) {
0413: Statement[] stmts = node.getStatements();
0414: for (int i = 0; i < stmts.length; i++) {
0415: if (i > 0 && stmts[i - 1].isBreak()
0416: && stmts[i].getClass() != Statement.class) {
0417: error("break.code.unreachable", stmts[i]);
0418: }
0419: check(stmts[i]);
0420: }
0421: return null;
0422: }
0423:
0424: public Object visit(Block node) {
0425: return visit((StatementList) node);
0426: }
0427:
0428: public Object visit(AssignmentStatement node) {
0429: VariableRef lvalue = node.getLValue();
0430: String lname = lvalue.getName();
0431: if (mLoopVariables.contains(lname)) {
0432: error("foreach.loopvar.nomodify", lname, node
0433: .getSourceInfo());
0434: return null;
0435: }
0436:
0437: Expression rvalue = node.getRValue();
0438: check(rvalue);
0439:
0440: Type type = rvalue.getType();
0441:
0442: if (type != null) {
0443: if (mExceptionGuardian && rvalue.isExceptionPossible()
0444: && type.isNonNull()) {
0445:
0446: // Since the expression may throw an exception and be
0447: // guarded, an assignment of null needs to work.
0448:
0449: rvalue.convertTo(type.toNullable());
0450: type = rvalue.getType();
0451: }
0452: defineVariable(lvalue, type);
0453: }
0454:
0455: return null;
0456: }
0457:
0458: public Object visit(BreakStatement node) {
0459: // check to make sure that the break is contained in a loop
0460: if (mForeachCount <= 0) {
0461: error("break.not.inside.foreach", node);
0462: }
0463:
0464: return null;
0465: }
0466:
0467: public Object visit(ForeachStatement node) {
0468:
0469: ++mForeachCount;
0470:
0471: VariableRef loopVar = node.getLoopVariable();
0472: String varName = loopVar.getName();
0473: if (mLoopVariables.contains(varName)) {
0474: error("foreach.loopvar.noreuse", varName, loopVar
0475: .getSourceInfo());
0476: return null;
0477: }
0478: mLoopVariables.push(varName);
0479:
0480: Expression range = node.getRange();
0481: Expression endRange = node.getEndRange();
0482: Block body = node.getBody();
0483:
0484: check(range);
0485: Type rangeType = range.getType();
0486:
0487: Type endRangeType;
0488: if (endRange == null) {
0489: endRangeType = null;
0490: } else {
0491: check(endRange);
0492: endRangeType = endRange.getType();
0493: }
0494:
0495: if (rangeType == null
0496: || (endRange != null && endRangeType == null)) {
0497:
0498: // Some error must have been detected in the range, so just
0499: // check everything else possible and bail out.
0500:
0501: enterScope();
0502: check(body);
0503: exitScope();
0504: return null;
0505: }
0506:
0507: Class rangeClass = rangeType.getObjectClass();
0508: Type elementType;
0509:
0510: // If there is no end range, then range expression type must be
0511: // an array or Collection.
0512:
0513: if (endRangeType == null) {
0514: try {
0515: elementType = rangeType.getIterationElementType();
0516: } catch (IntrospectionException e) {
0517: error(e.toString(), node);
0518: return null;
0519: }
0520:
0521: if (elementType == null) {
0522: error("foreach.iteration.not.supported", rangeType
0523: .getSimpleName(), range);
0524: } else {
0525: checkAccess(elementType.getNaturalClass(), range);
0526:
0527: if (node.isReverse()
0528: && !rangeType.isReverseIterationSupported()) {
0529: error("foreach.reverse.not.supported",
0530: rangeType.getSimpleName(), range);
0531: }
0532: }
0533: } else {
0534: elementType = Type.INT_TYPE;
0535:
0536: if (!Number.class.isAssignableFrom(rangeClass)) {
0537: error("foreach.range.start", rangeType
0538: .getSimpleName(), range);
0539: range.setType(elementType);
0540: } else {
0541: if (Long.class.isAssignableFrom(rangeClass)) {
0542: elementType = Type.LONG_TYPE;
0543: }
0544: }
0545:
0546: Class endRangeClass = endRangeType.getObjectClass();
0547: if (!Number.class.isAssignableFrom(endRangeClass)) {
0548: error("foreach.range.end", endRangeType
0549: .getSimpleName(), endRange);
0550: endRange.setType(elementType);
0551: } else {
0552: if (Long.class.isAssignableFrom(endRangeClass)) {
0553: elementType = Type.LONG_TYPE;
0554: }
0555: }
0556:
0557: range.convertTo(elementType);
0558: endRange.convertTo(elementType);
0559: }
0560:
0561: // Enter a new scope because variable declarations local
0562: // to a foreach loop might never get a value assigned because
0563: // there is no guarantee that the body will ever execute.
0564: Scope bodyScope = enterScope();
0565:
0566: if (elementType != null) {
0567: defineVariable(loopVar, elementType);
0568: }
0569:
0570: check(body);
0571: exitScope();
0572:
0573: Variable[] vars = bodyScope.promote();
0574:
0575: if (vars.length > 0) {
0576: // Since the body can be executed multiple times, variables
0577: // must be of the correct type before entering the scope.
0578: node.setInitializer(createConversions(mScope, vars,
0579: node.getSourceInfo()));
0580:
0581: // Apply any conversions at the end of the body as well in
0582: // order for the variables to be of the correct type when
0583: // accessed again at the start of the body.
0584: body.setFinalizer(createConversions(bodyScope, vars,
0585: body.getSourceInfo()));
0586:
0587: // Declare all promoted variables outside body scope.
0588: mScope.declareVariables(vars);
0589:
0590: // Re-check body to account for variable declaration changes.
0591: if (mErrorCount == 0) {
0592: bodyScope.delete();
0593: bodyScope = enterScope();
0594:
0595: if (elementType != null) {
0596: defineVariable(loopVar, elementType);
0597: }
0598:
0599: check(body);
0600: exitScope();
0601: }
0602: }
0603:
0604: mLoopVariables.pop();
0605: --mForeachCount;
0606: return null;
0607: }
0608:
0609: public Object visit(IfStatement node) {
0610: Expression condition = node.getCondition();
0611: Block thenPart = node.getThenPart();
0612: Block elsePart = node.getElsePart();
0613: Variable[] thenCasts = null;
0614: Variable[] elseCasts = null;
0615:
0616: int preCondErrorCount = mErrorCount;
0617: check(condition);
0618:
0619: Type type = condition.getType();
0620: if (type != null && type.getObjectClass() != Boolean.class) {
0621: error("if.condition", condition);
0622: } else {
0623: condition.convertTo(Type.BOOLEAN_TYPE);
0624: if (preCondErrorCount == mErrorCount) {
0625: IsaDetector detector = new IsaDetector();
0626: condition.accept(detector);
0627: thenCasts = detector.getThenCasts();
0628: elseCasts = detector.getElseCasts();
0629: }
0630: }
0631:
0632: Scope thenScope = null;
0633: Scope elseScope = null;
0634:
0635: thenScope = enterScope();
0636: if (thenPart != null) {
0637: Statement stmt = createCasts(thenScope, thenCasts,
0638: thenPart.getSourceInfo());
0639: // Don't typecheck any inserted casts, they are already correct
0640: check(thenPart);
0641: thenPart.setInitializer(stmt);
0642: }
0643: exitScope();
0644:
0645: elseScope = enterScope();
0646: if (elsePart != null) {
0647: Statement stmt = createCasts(elseScope, elseCasts,
0648: elsePart.getSourceInfo());
0649: // Don't typecheck any inserted casts, they are already correct
0650: check(elsePart);
0651: elsePart.setInitializer(stmt);
0652: }
0653: exitScope();
0654:
0655: // Merge then and else scopes
0656: Variable[] vars = thenScope.intersect(elseScope);
0657:
0658: if (vars.length == 0) {
0659: node.setMergedVariables(vars);
0660: } else {
0661: if (mExceptionGuardian
0662: && condition.isExceptionPossible()) {
0663: // If condition could throw an exception, then the
0664: // exception handler needs to ensure that these variables
0665: // are assigned null. Therefore, the variables need to be
0666: // able to accept null.
0667: for (int i = 0; i < vars.length; i++) {
0668: Variable v = vars[i];
0669: Type t = v.getType();
0670: if (t.isNonNull()) {
0671: v = (Variable) v.clone();
0672: v.setType(t.toNullable());
0673: vars[i] = v;
0674: }
0675: }
0676: }
0677:
0678: node.setMergedVariables(vars);
0679:
0680: // Add corrective code as a result of merging.
0681: if (thenPart == null) {
0682: thenPart = new Block(node.getSourceInfo());
0683: node.setThenPart(thenPart);
0684: }
0685: Statement fin = createConversions(thenScope, vars,
0686: thenPart.getSourceInfo());
0687: thenPart.setFinalizer(fin);
0688:
0689: if (elsePart == null) {
0690: elsePart = new Block(node.getSourceInfo());
0691: node.setElsePart(elsePart);
0692: }
0693: fin = createConversions(elseScope, vars, elsePart
0694: .getSourceInfo());
0695: elsePart.setFinalizer(fin);
0696:
0697: // Re-declare all new variables with new types for future
0698: // variable references.
0699: mScope.declareVariables(vars);
0700: }
0701:
0702: return null;
0703: }
0704:
0705: /**
0706: * Create variable casting assignment statements.
0707: *
0708: * @param scope scope of statement
0709: * @param newVars optional new variables to assign to
0710: * @param info source info to apply to created assignments
0711: * @return statement with assignments or null if none
0712: */
0713: private Statement createCasts(Scope scope, Variable[] newVars,
0714: SourceInfo info) {
0715: if (newVars == null) {
0716: return null;
0717: }
0718:
0719: int length = newVars.length;
0720: if (length == 0) {
0721: return null;
0722: }
0723:
0724: List newStatements = new ArrayList(length);
0725:
0726: for (int i = 0; i < length; i++) {
0727: Variable newVar = newVars[i];
0728: String name = newVar.getName();
0729: Variable oldVar = scope.getDeclaredVariable(name);
0730:
0731: // It is important that the variable be declared private.
0732: newVar = scope.declareVariable(newVar, true);
0733:
0734: if (newVar == oldVar) {
0735: continue;
0736: }
0737:
0738: VariableRef lvalue = new VariableRef(info, name);
0739: scope.bindToVariable(lvalue);
0740: lvalue.setVariable(newVar);
0741:
0742: VariableRef rvalue = new VariableRef(info, name);
0743: scope.bindToVariable(rvalue);
0744: rvalue.setVariable(oldVar);
0745: try {
0746: rvalue.convertTo(newVar.getType(), true);
0747: } catch (IllegalArgumentException e) {
0748: if (mErrorCount == 0) {
0749: throw e;
0750: }
0751: }
0752:
0753: newStatements.add(new AssignmentStatement(info, lvalue,
0754: rvalue));
0755: }
0756:
0757: if (newStatements.size() == 0) {
0758: return null;
0759: } else {
0760: Statement[] stmts = new Statement[newStatements.size()];
0761: stmts = (Statement[]) newStatements.toArray(stmts);
0762: return new StatementList(info, stmts);
0763: }
0764: }
0765:
0766: /**
0767: * Create variable conversion assignment statements.
0768: *
0769: * @param scope scope of statement
0770: * @param newVars optional new variables to assign to
0771: * @param info source info to apply to created assignments
0772: * @return statement with assignments or null if none
0773: */
0774: private Statement createConversions(Scope scope,
0775: Variable[] newVars, SourceInfo info) {
0776: if (newVars == null) {
0777: return null;
0778: }
0779:
0780: int length = newVars.length;
0781: if (length == 0) {
0782: return null;
0783: }
0784:
0785: List newStatements = new ArrayList(length);
0786:
0787: for (int i = 0; i < length; i++) {
0788: Variable newVar = newVars[i];
0789: String name = newVar.getName();
0790: Variable oldVar = scope.getDeclaredVariable(name);
0791:
0792: newVar = scope.declareVariable(newVar);
0793:
0794: if (newVar == oldVar) {
0795: continue;
0796: }
0797:
0798: VariableRef lvalue = new VariableRef(info, name);
0799: scope.bindToVariable(lvalue);
0800: lvalue.setVariable(newVar);
0801:
0802: VariableRef rvalue = new VariableRef(info, name);
0803: scope.bindToVariable(rvalue);
0804: rvalue.setVariable(oldVar);
0805: rvalue.convertTo(newVar.getType(), false);
0806:
0807: newStatements.add(new AssignmentStatement(info, lvalue,
0808: rvalue));
0809: }
0810:
0811: if (newStatements.size() == 0) {
0812: return null;
0813: } else {
0814: Statement[] stmts = new Statement[newStatements.size()];
0815: stmts = (Statement[]) newStatements.toArray(stmts);
0816: return new StatementList(info, stmts);
0817: }
0818: }
0819:
0820: public Object visit(SubstitutionStatement node) {
0821: // Check if substitution allowed in this template.
0822: if (!mUnit.getParseTree().hasSubstitutionParam()) {
0823: error("substitution.undeclared", node);
0824: }
0825:
0826: return null;
0827: }
0828:
0829: public Object visit(ExpressionStatement node) {
0830: Expression expr = node.getExpression();
0831: if (expr instanceof CallExpression) {
0832: ((CallExpression) expr).setVoidPermitted(true);
0833: }
0834: check(expr);
0835:
0836: Type type = expr.getType();
0837: Compiler c = mUnit.getCompiler();
0838:
0839: if (type != null && type.getNaturalClass() != void.class
0840: && c != null) {
0841:
0842: Method[] methods = c.getRuntimeContextMethods();
0843: String name = c.getRuntimeReceiver();
0844:
0845: int cnt = MethodMatcher.match(methods, name,
0846: new Type[] { type });
0847:
0848: if (cnt < 1) {
0849: error("expressionstatement.receiver", expr
0850: .getType().getSimpleName(), node);
0851: } else {
0852: Method receiver = methods[0];
0853: node.setReceiverMethod(receiver);
0854: expr.convertTo(new Type(receiver
0855: .getParameterTypes()[0]));
0856: }
0857: }
0858:
0859: return null;
0860: }
0861:
0862: public Object visit(ReturnStatement node) {
0863: Type type;
0864: Expression expr = node.getExpression();
0865:
0866: if (expr != null) {
0867: check(expr);
0868: type = expr.getType();
0869: } else {
0870: type = Type.VOID_TYPE;
0871: }
0872:
0873: if (mReturnType == null) {
0874: mReturnType = type;
0875: } else {
0876: Type newType = mReturnType.getCompatibleType(type);
0877: if (newType == null) {
0878: error("returnstatement.type", type.getSimpleName(),
0879: mReturnType.getSimpleName(), node);
0880: }
0881: mReturnType = newType;
0882: }
0883:
0884: return null;
0885: }
0886:
0887: public Object visit(Expression node) {
0888: return null;
0889: }
0890:
0891: public Object visit(ParenExpression node) {
0892: Expression expr = node.getExpression();
0893: check(expr);
0894: return null;
0895: }
0896:
0897: public Object visit(NewArrayExpression node) {
0898: ExpressionList list = node.getExpressionList();
0899: check(list);
0900:
0901: Expression[] exprs = list.getExpressions();
0902:
0903: if (node.isAssociative()) {
0904: if (exprs.length % 2 != 0) {
0905: error("newarrayexpression.associative", node);
0906: }
0907:
0908: Type elementType = newArrayElementType(exprs, 1, 2);
0909: // Since an element might not be found for a given key, the
0910: // element type is set to be nullable.
0911: elementType = elementType.toNonPrimitive().toNullable();
0912:
0913: Type arrayType = new Type(Map.class);
0914: try {
0915: arrayType = arrayType
0916: .setArrayElementType(elementType);
0917: } catch (IntrospectionException e) {
0918: error(e.toString(), node);
0919: return null;
0920: }
0921: node.setType(arrayType);
0922:
0923: // Make sure all keys are converted to Objects.
0924: for (int i = 0; i < exprs.length; i += 2) {
0925: exprs[i].convertTo(Type.OBJECT_TYPE);
0926: }
0927: } else {
0928: Type elementType = newArrayElementType(exprs, 0, 1);
0929: Class elementClass = elementType.getNaturalClass();
0930: Class arrayClass = Array.newInstance(elementClass, 0)
0931: .getClass();
0932:
0933: Type arrayType = new Type(arrayClass);
0934: if (elementType.isNonNull()) {
0935: try {
0936: arrayType = arrayType
0937: .setArrayElementType(elementType);
0938: } catch (IntrospectionException e) {
0939: }
0940: }
0941: node.setType(arrayType);
0942: }
0943:
0944: return null;
0945: }
0946:
0947: private Type newArrayElementType(Expression[] exprs, int start,
0948: int increment) {
0949: Type elementType = null;
0950:
0951: for (int i = start; i < exprs.length; i += increment) {
0952: Type type = exprs[i].getType();
0953:
0954: if (elementType == null) {
0955: elementType = type;
0956: } else {
0957: elementType = elementType.getCompatibleType(type);
0958: }
0959: }
0960:
0961: if (elementType == null) {
0962: elementType = Type.NULL_TYPE;
0963: }
0964:
0965: return elementType;
0966: }
0967:
0968: public Object visit(FunctionCallExpression node) {
0969: if (!initialCallExpressionCheck(node)) {
0970: return null;
0971: }
0972:
0973: Expression[] exprs = node.getParams().getExpressions();
0974: int length = exprs.length;
0975: Type[] actualTypes = new Type[length];
0976: for (int i = 0; i < length; i++) {
0977: actualTypes[i] = exprs[i].getType();
0978: }
0979:
0980: Block subParam = node.getSubstitutionParam();
0981: Compiler compiler = mUnit.getCompiler();
0982: String name = node.getTarget().getName();
0983:
0984: // Look for Java function to call.
0985: name = name.replace('.', '$');
0986:
0987: if (subParam != null) {
0988: Type[] types = new Type[length + 1];
0989: System.arraycopy(actualTypes, 0, types, 0, length);
0990: types[length] = new Type(
0991: com.go.tea.runtime.Substitution.class);
0992: actualTypes = types;
0993: }
0994:
0995: Method[] methods = compiler.getRuntimeContextMethods();
0996: int cnt = MethodMatcher.match(methods, name, actualTypes);
0997: if (cnt <= 0) {
0998: error("functioncallexpression.not.found", node);
0999: return null;
1000: }
1001:
1002: Method m = methods[0];
1003: node.setCalledMethod(m);
1004:
1005: if (m.getDeclaringClass() == Object.class) {
1006: error("functioncallexpression.not.found", node);
1007: }
1008:
1009: // Set param types.
1010: Class[] paramClasses = m.getParameterTypes();
1011: for (int i = 0; i < length; i++) {
1012: exprs[i].convertTo(new Type(paramClasses[i]), false);
1013: }
1014:
1015: Class retClass = m.getReturnType();
1016: checkAccess(retClass, node);
1017: Type retType;
1018: if (retClass == char.class) {
1019: // Convert any returned char to a String.
1020: retType = Type.NON_NULL_STRING_TYPE;
1021: } else if (retClass == Character.class) {
1022: // Convert any returned Character to a String.
1023: retType = Type.STRING_TYPE;
1024: } else {
1025: retType = new Type(retClass);
1026: }
1027: node.setType(retType);
1028:
1029: if (retClass == void.class && !node.isVoidPermitted()) {
1030: error("functioncallexpression.function.void", node);
1031: }
1032:
1033: return null;
1034: }
1035:
1036: public Object visit(TemplateCallExpression node) {
1037: if (!initialCallExpressionCheck(node)) {
1038: return null;
1039: }
1040:
1041: Expression[] exprs = node.getParams().getExpressions();
1042: int length = exprs.length;
1043: Type[] actualTypes = new Type[length];
1044: for (int i = 0; i < length; i++) {
1045: actualTypes[i] = exprs[i].getType();
1046: }
1047:
1048: Block subParam = node.getSubstitutionParam();
1049: Compiler compiler = mUnit.getCompiler();
1050: String name = node.getTarget().getName();
1051:
1052: // Look for a matching template to call.
1053:
1054: CompilationUnit unit = compiler.getCompilationUnit(name,
1055: mUnit);
1056: if (unit == null) {
1057: error("templatecallexpression.not.found", node);
1058: return null;
1059: }
1060:
1061: Template tree = unit.getParseTree();
1062:
1063: Variable[] formalParams = tree.getParams();
1064: if (formalParams != null) {
1065: if (formalParams.length != length) {
1066: error("templatecallexpression.parameter.count",
1067: String.valueOf(formalParams.length), String
1068: .valueOf(length), node);
1069: return null;
1070: }
1071:
1072: if (subParam != null && !tree.hasSubstitutionParam()) {
1073: error("templatecallexpression.substitution.no",
1074: tree.getName().getName(), subParam);
1075: return null;
1076: } else if (subParam == null
1077: && tree.hasSubstitutionParam()) {
1078: error("templatecallexpression.substitution.yes",
1079: tree.getName().getName(), node);
1080: }
1081:
1082: node.setCalledTemplate(unit);
1083: for (int i = 0; i < length; i++) {
1084: Type type = formalParams[i].getType();
1085:
1086: if (type == null) {
1087: error(
1088: "templatecallexpression.parameter.unknown",
1089: node);
1090: return null;
1091: }
1092:
1093: int cost = type.convertableFrom(actualTypes[i]);
1094: if (cost < 0) {
1095: node.setCalledTemplate(null);
1096:
1097: String msg1 = actualTypes[i].getFullName();
1098: String msg2 = type.getFullName();
1099:
1100: ClassLoader CL1 = actualTypes[i]
1101: .getNaturalClass().getClassLoader();
1102:
1103: ClassLoader CL2 = type.getNaturalClass()
1104: .getClassLoader();
1105:
1106: if (CL1 != CL2) {
1107: msg1 += "(" + CL1 + ")";
1108: msg2 += "(" + CL2 + ")";
1109: }
1110:
1111: error("templatecallexpression.conversion",
1112: msg1, msg2, exprs[i]);
1113: } else {
1114: exprs[i].convertTo(type, false);
1115: }
1116: }
1117: }
1118:
1119: Type retType = tree.getReturnType();
1120: if (retType == null) {
1121: retType = Type.VOID_TYPE;
1122: }
1123: node.setType(retType);
1124:
1125: if (Type.VOID_TYPE.equals(retType)
1126: && !node.isVoidPermitted()) {
1127:
1128: error("templatecallexpression.template.void", node);
1129: return null;
1130: }
1131:
1132: return null;
1133: }
1134:
1135: /**
1136: * Returns false if initial checks failed.
1137: */
1138: private boolean initialCallExpressionCheck(CallExpression node) {
1139: ExpressionList params = node.getParams();
1140: check(params);
1141:
1142: Statement init = node.getInitializer();
1143: if (init != null) {
1144: check(init);
1145: }
1146:
1147: Block subParam = node.getSubstitutionParam();
1148: if (subParam != null) {
1149: // Enter a new scope because variable declarations local
1150: // to a substitution block might never get a value assigned
1151: // because there is no guarantee it will ever execute.
1152: Scope subScope = enterScope();
1153: check(subParam);
1154: exitScope();
1155:
1156: Variable[] vars = subScope.promote();
1157:
1158: if (vars.length > 0) {
1159: // Since the subParam can be executed multiple times,
1160: // variables must be of the correct type before entering
1161: // the scope.
1162: node.setInitializer(createConversions(mScope, vars,
1163: node.getSourceInfo()));
1164:
1165: // Apply any conversions at the end of the subParam as well
1166: // in order for the variables to be of the correct type
1167: // when accessed again at the start of the body.
1168: subParam.setFinalizer(createConversions(subScope,
1169: vars, subParam.getSourceInfo()));
1170:
1171: // Promoted variables need to become fields
1172: for (int i = 0; i < vars.length; i++) {
1173: vars[i].setField(true);
1174: }
1175:
1176: // Declare all promoted variables outside subParam scope.
1177: mScope.declareVariables(vars);
1178:
1179: // Re-check subParam to account for variable declaration
1180: // changes.
1181: if (mErrorCount == 0) {
1182: subScope.delete();
1183: subScope = enterScope();
1184: check(subParam);
1185: exitScope();
1186: }
1187: }
1188:
1189: // References inside a substitution block to variables
1190: // outside need to be fields so that they can be shared.
1191: VariableRef[] refs = subScope
1192: .getOutOfScopeVariableRefs();
1193: for (int i = 0; i < refs.length; i++) {
1194: refs[i].getVariable().setField(true);
1195: }
1196: }
1197:
1198: Expression[] exprs = params.getExpressions();
1199: int length = exprs.length;
1200: for (int i = 0; i < length; i++) {
1201: if (exprs[i].getType() == null) {
1202: // Type of a parameter is unknown, so bail out.
1203: return false;
1204: }
1205: }
1206:
1207: Compiler compiler = mUnit.getCompiler();
1208: if (compiler == null) {
1209: return false;
1210: }
1211:
1212: return true;
1213: }
1214:
1215: public Object visit(VariableRef node) {
1216: if (!mScope.bindToVariable(node)) {
1217: error("variableref.undefined", node.getName(), node);
1218: }
1219: return null;
1220: }
1221:
1222: public Object visit(Lookup node) {
1223: Expression expr = node.getExpression();
1224: check(expr);
1225:
1226: // Now check if expression type class contains the lookup name.
1227: Type type = expr.getType();
1228: String lookupName = node.getLookupName().getName();
1229:
1230: if (type != null && lookupName != null) {
1231: Class clazz = type.getObjectClass();
1232: // Lookup can only work on objects.
1233: type = type.toNonPrimitive();
1234: expr.convertTo(type);
1235:
1236: if ("length".equals(lookupName)) {
1237: if (clazz == String.class) {
1238: node.setType(Type.INT_TYPE);
1239: try {
1240: node.setReadMethod(String.class.getMethod(
1241: "length", new Class[0]));
1242: } catch (NoSuchMethodException e) {
1243: throw new LinkageError(e.toString());
1244: }
1245: return null;
1246: } else if (Collection.class.isAssignableFrom(clazz)) {
1247: node.setType(Type.INT_TYPE);
1248: try {
1249: node.setReadMethod(Collection.class
1250: .getMethod("size", new Class[0]));
1251: } catch (NoSuchMethodException e) {
1252: throw new LinkageError(e.toString());
1253: }
1254: return null;
1255: } else if (clazz.isArray()) {
1256: node.setType(Type.INT_TYPE);
1257: return null;
1258: }
1259: }
1260:
1261: Map properties;
1262: try {
1263: properties = BeanAnalyzer.getAllProperties(clazz);
1264: } catch (IntrospectionException e) {
1265: error(e.toString(), node);
1266: return null;
1267: }
1268:
1269: PropertyDescriptor prop = (PropertyDescriptor) properties
1270: .get(lookupName);
1271:
1272: if (prop == null) {
1273: error("lookup.undefined", lookupName, type
1274: .getSimpleName(), node.getLookupName());
1275: return null;
1276: }
1277:
1278: Class retClass = prop.getPropertyType();
1279: if (retClass == null) {
1280: error("lookup.array.only", lookupName, node
1281: .getLookupName());
1282: return null;
1283: }
1284:
1285: checkAccess(retClass, node.getLookupName());
1286:
1287: node.setType(new Type(retClass));
1288:
1289: if (retClass == char.class) {
1290: // Convert any returned char to a String.
1291: node.convertTo(Type.NON_NULL_STRING_TYPE);
1292: } else if (retClass == Character.class) {
1293: // Convert any returned Character to a String.
1294: node.convertTo(Type.STRING_TYPE);
1295: }
1296:
1297: Method m = prop.getReadMethod();
1298: if (m == null) {
1299: error("lookup.unreadable", lookupName, node
1300: .getLookupName());
1301: return null;
1302: }
1303:
1304: node.setReadMethod(m);
1305: }
1306:
1307: return null;
1308: }
1309:
1310: public Object visit(ArrayLookup node) {
1311: Expression expr = node.getExpression();
1312: Expression lookupIndex = node.getLookupIndex();
1313: check(expr);
1314: check(lookupIndex);
1315:
1316: Type type = expr.getType();
1317: Type lookupType = lookupIndex.getType();
1318:
1319: if (type != null && lookupType != null) {
1320: // Array lookup can only work on objects.
1321: type = type.toNonPrimitive();
1322: expr.convertTo(type);
1323:
1324: // Now check if expression type class supports array lookup.
1325: // If so, check that lookup index is correct type
1326:
1327: Type elementType;
1328: try {
1329: elementType = type.getArrayElementType();
1330: } catch (IntrospectionException e) {
1331: error(e.toString(), node);
1332: return null;
1333: }
1334:
1335: if (elementType == null) {
1336: error("arraylookup.unsupported", type
1337: .getSimpleName(), node.getLookupToken());
1338:
1339: return null;
1340: }
1341:
1342: checkAccess(elementType.getObjectClass(), node);
1343:
1344: // Look for the best array access method.
1345:
1346: Method[] methods;
1347: try {
1348: methods = type.getArrayAccessMethods();
1349: } catch (IntrospectionException e) {
1350: error(e.toString(), node);
1351: return null;
1352: }
1353:
1354: boolean good = false;
1355:
1356: if (methods.length == 0) {
1357: // Must be an actual Java array.
1358: if (type.getObjectClass().isArray()) {
1359: Class lookupClass = lookupType.getObjectClass();
1360: if (Number.class.isAssignableFrom(lookupClass)) {
1361: lookupIndex.convertTo(Type.INT_TYPE);
1362: node.setType(elementType);
1363: good = true;
1364: }
1365: }
1366: } else {
1367: int count = MethodMatcher.match(methods, null,
1368: new Type[] { lookupType });
1369: if (count >= 1) {
1370: Method m = methods[0];
1371: lookupType = new Type(m.getParameterTypes()[0]);
1372: lookupIndex.convertTo(lookupType);
1373: node.setReadMethod(m);
1374: node.setType(new Type(m.getReturnType()));
1375: node.convertTo(elementType);
1376: good = true;
1377: }
1378: }
1379:
1380: if (good) {
1381: if (elementType.getObjectClass() == Character.class) {
1382: // Convert any returned character to a String.
1383: node.convertTo(Type.STRING_TYPE);
1384: }
1385: } else {
1386: error("arraylookup.unsupported.for", type
1387: .getSimpleName(), lookupType
1388: .getSimpleName(), lookupIndex);
1389: }
1390: }
1391:
1392: return null;
1393: }
1394:
1395: public Object visit(NegateExpression node) {
1396: Expression expr = node.getExpression();
1397: check(expr);
1398:
1399: Type type = expr.getType();
1400: if (type == null) {
1401: node.setType(Type.INT_TYPE);
1402: } else if (Number.class.isAssignableFrom(type
1403: .getObjectClass())) {
1404: type = type.toPrimitive();
1405: expr.convertTo(type);
1406: node.setType(type);
1407: } else {
1408: error("negateexpression.type", node);
1409: }
1410:
1411: return null;
1412: }
1413:
1414: public Object visit(NotExpression node) {
1415: Expression expr = node.getExpression();
1416: check(expr);
1417:
1418: Type type = expr.getType();
1419: if (type == null) {
1420: node.setType(Type.BOOLEAN_TYPE);
1421: } else if (type.getObjectClass() == Boolean.class) {
1422: type = type.toPrimitive();
1423: expr.convertTo(type);
1424: node.setType(type);
1425: } else {
1426: error("notexpression.type", node);
1427: }
1428:
1429: return null;
1430: }
1431:
1432: public Object visit(ConcatenateExpression node) {
1433: Expression left = node.getLeftExpression();
1434: Expression right = node.getRightExpression();
1435:
1436: check(left);
1437: check(right);
1438:
1439: left.convertTo(Type.NON_NULL_STRING_TYPE, false);
1440: right.convertTo(Type.NON_NULL_STRING_TYPE, false);
1441: node.setType(Type.NON_NULL_STRING_TYPE);
1442:
1443: return null;
1444: }
1445:
1446: public Object visit(ArithmeticExpression node) {
1447: Expression left = node.getLeftExpression();
1448: Expression right = node.getRightExpression();
1449:
1450: check(left);
1451: check(right);
1452:
1453: Type leftType = left.getType();
1454: Type rightType = right.getType();
1455:
1456: if (binaryTypeCheck(node, Number.class)) {
1457: Type type = leftType.getCompatibleType(rightType)
1458: .toPrimitive();
1459:
1460: left.convertTo(type);
1461: right.convertTo(type);
1462: node.setType(type);
1463: }
1464:
1465: return null;
1466: }
1467:
1468: private boolean binaryTypeCheck(BinaryExpression expr,
1469: Class clazz) {
1470: Expression left = expr.getLeftExpression();
1471: Expression right = expr.getRightExpression();
1472:
1473: Type leftType = left.getType();
1474: Type rightType = right.getType();
1475:
1476: if (leftType == null || rightType == null) {
1477: return false;
1478: }
1479:
1480: if (!clazz.isAssignableFrom(leftType.getObjectClass())) {
1481: if (!clazz.isAssignableFrom(rightType.getObjectClass())) {
1482: String name = new Type(clazz).getSimpleName();
1483: error("binaryexpression.type.both", expr
1484: .getOperator().getImage(), name, expr);
1485: } else {
1486: String name = new Type(clazz).getSimpleName();
1487: error("binaryexpression.type.left", expr
1488: .getOperator().getImage(), name, left);
1489: }
1490: } else if (!clazz.isAssignableFrom(rightType
1491: .getObjectClass())) {
1492: String name = new Type(clazz).getSimpleName();
1493: error("binaryexpression.type.right", expr.getOperator()
1494: .getImage(), name, right);
1495: } else {
1496: return true;
1497: }
1498:
1499: return false;
1500: }
1501:
1502: public Object visit(RelationalExpression node) {
1503: node.setType(Type.BOOLEAN_TYPE);
1504:
1505: Token token = node.getOperator();
1506: int ID = token.getID();
1507:
1508: if (ID == Token.ISA) {
1509: return visitIsa(node);
1510: }
1511:
1512: Expression left = node.getLeftExpression();
1513: Expression right = node.getRightExpression();
1514:
1515: check(left);
1516: check(right);
1517:
1518: Type leftType = left.getType();
1519: Type rightType = right.getType();
1520:
1521: if (leftType != null && rightType != null) {
1522: Class leftClass = leftType.getNaturalClass();
1523: Class rightClass = rightType.getNaturalClass();
1524:
1525: Type type;
1526: if (ID == Token.EQ || ID == Token.NE) {
1527: if (String.class.isAssignableFrom(leftClass)) {
1528: type = leftType.toNullable();
1529: } else if (String.class
1530: .isAssignableFrom(rightClass)) {
1531: type = rightType.toNullable();
1532: } else {
1533: type = leftType.getCompatibleType(rightType);
1534: }
1535: } else {
1536: type = leftType.getCompatibleType(rightType);
1537: }
1538:
1539: if (type == null) {
1540: type = Type.NULL_TYPE;
1541: }
1542:
1543: Class clazz = type.getObjectClass();
1544:
1545: if (type.hasPrimitivePeer()
1546: && leftType.isNonNull()
1547: && rightType.isNonNull()
1548: && (leftType.isPrimitive() || leftType
1549: .hasPrimitivePeer())
1550: && (rightType.isPrimitive() || rightType
1551: .hasPrimitivePeer())) {
1552: leftType = rightType = type.toPrimitive();
1553: } else {
1554: if (leftType.isNonNull()) {
1555: leftType = type.toNonNull();
1556: if (rightType.isNonNull()) {
1557: rightType = leftType;
1558: } else {
1559: rightType = type;
1560: }
1561: } else {
1562: leftType = type;
1563: if (rightType.isNonNull()) {
1564: rightType = type.toNonNull();
1565: } else {
1566: rightType = type;
1567: }
1568: }
1569: }
1570:
1571: if (ID == Token.EQ || ID == Token.NE
1572: || Comparable.class.isAssignableFrom(clazz)
1573: || String.class.isAssignableFrom(clazz)
1574: || Number.class.isAssignableFrom(clazz)) {
1575:
1576: // Don't prefer cast; possibly perform string conversion.
1577: left.convertTo(leftType, false);
1578: right.convertTo(rightType, false);
1579: } else {
1580: error("relationalexpression.type.mismatch", token
1581: .getImage(),
1582: left.getType().getSimpleName(), right
1583: .getType().getSimpleName(), node);
1584: }
1585: }
1586:
1587: node.setType(Type.BOOLEAN_TYPE);
1588:
1589: return null;
1590: }
1591:
1592: private Object visitIsa(RelationalExpression node) {
1593: Token token = node.getOperator();
1594:
1595: Expression left = node.getLeftExpression();
1596: TypeName typeName = node.getIsaTypeName();
1597:
1598: check(left);
1599: check(typeName);
1600:
1601: if (!(left instanceof VariableRef)) {
1602: error("relationalexpression.isa.left",
1603: token.getImage(), left);
1604: }
1605:
1606: Type leftType = left.getType();
1607: Type rightType = typeName.getType();
1608:
1609: if (leftType != null && rightType != null) {
1610: // Ensure the left type is an object.
1611: leftType = leftType.toNonPrimitive();
1612: left.convertTo(leftType);
1613:
1614: Class leftClass = leftType.getObjectClass();
1615: Class rightClass = rightType.getObjectClass();
1616:
1617: if (rightClass.isAssignableFrom(leftClass)) {
1618: // Widening case. i.e. (5 isa Number) is always true.
1619: } else if (leftClass.isAssignableFrom(rightClass)) {
1620: // Narrowing case. i.e. (n isa Integer) might be true.
1621:
1622: // For this case, a cast operation needs to be inserted
1623: // in an if statement.
1624: } else {
1625: SourceInfo info = new SourceDetailedInfo(node
1626: .getSourceInfo(), typeName.getSourceInfo()
1627: .getDetailPosition());
1628:
1629: error("relationalexpression.isa.impossible",
1630: leftType.getSimpleName(), rightType
1631: .getSimpleName(), info);
1632: }
1633: }
1634:
1635: return null;
1636: }
1637:
1638: public Object visit(AndExpression node) {
1639: Expression left = node.getLeftExpression();
1640: Expression right = node.getRightExpression();
1641:
1642: check(left);
1643: check(right);
1644:
1645: Type type = Type.BOOLEAN_TYPE;
1646:
1647: if (binaryTypeCheck(node, Boolean.class)) {
1648: left.convertTo(type);
1649: right.convertTo(type);
1650: }
1651:
1652: node.setType(type);
1653:
1654: return null;
1655: }
1656:
1657: public Object visit(OrExpression node) {
1658: Expression left = node.getLeftExpression();
1659: Expression right = node.getRightExpression();
1660:
1661: check(left);
1662: check(right);
1663:
1664: Type type = Type.BOOLEAN_TYPE;
1665:
1666: if (binaryTypeCheck(node, Boolean.class)) {
1667: left.convertTo(type);
1668: right.convertTo(type);
1669: }
1670:
1671: node.setType(type);
1672:
1673: return null;
1674: }
1675:
1676: public Object visit(NullLiteral node) {
1677: return null;
1678: }
1679:
1680: public Object visit(BooleanLiteral node) {
1681: return null;
1682: }
1683:
1684: public Object visit(StringLiteral node) {
1685: return null;
1686: }
1687:
1688: public Object visit(NumberLiteral node) {
1689: return null;
1690: }
1691: }
1692:
1693: /**
1694: * Detects relational expressions that refine variable types for an if
1695: * statement's then and else scopes.
1696: */
1697: private static class IsaDetector extends TreeWalker {
1698: private static final char NOT_OP = 'n';
1699: private static final char AND_OP = 'a';
1700: private static final char OR_OP = 'o';
1701:
1702: private StringBuffer mOpStack;
1703:
1704: private Collection mThenCasts;
1705: private Collection mElseCasts;
1706:
1707: public Variable[] getThenCasts() {
1708: if (mThenCasts == null) {
1709: return null;
1710: } else {
1711: Variable[] vars = new Variable[mThenCasts.size()];
1712: return (Variable[]) mThenCasts.toArray(vars);
1713: }
1714: }
1715:
1716: public Variable[] getElseCasts() {
1717: if (mElseCasts == null) {
1718: return null;
1719: } else {
1720: Variable[] vars = new Variable[mElseCasts.size()];
1721: return (Variable[]) mElseCasts.toArray(vars);
1722: }
1723: }
1724:
1725: public Object visit(RelationalExpression node) {
1726: boolean typeKnown = true;
1727: boolean forThenPart = true;
1728:
1729: if (mOpStack != null) {
1730: int length = mOpStack.length();
1731: for (int i = 0; i < length; i++) {
1732: switch (mOpStack.charAt(i)) {
1733: case NOT_OP:
1734: forThenPart = !forThenPart;
1735: break;
1736: case AND_OP:
1737: if (!forThenPart) {
1738: typeKnown = false;
1739: }
1740: break;
1741: case OR_OP:
1742: if (forThenPart) {
1743: typeKnown = false;
1744: }
1745: break;
1746: }
1747: }
1748: }
1749:
1750: if (!typeKnown) {
1751: return super .visit(node);
1752: }
1753:
1754: int operator = node.getOperator().getID();
1755:
1756: if (operator == Token.ISA) {
1757: TypeName typeName = node.getIsaTypeName();
1758: if (typeName != null) {
1759: Expression left = node.getLeftExpression();
1760: if (left instanceof VariableRef) {
1761: Type type = typeName.getType();
1762: if (type != null) {
1763: addCast((VariableRef) left, type
1764: .toNonNull(), forThenPart);
1765: }
1766: }
1767: }
1768: } else if (operator == Token.EQ || operator == Token.NE) {
1769: if (operator == Token.EQ) {
1770: forThenPart = !forThenPart;
1771: }
1772:
1773: VariableRef ref;
1774: Expression test;
1775:
1776: Expression left = node.getLeftExpression();
1777: Expression right = node.getRightExpression();
1778:
1779: if (left instanceof VariableRef) {
1780: ref = (VariableRef) left;
1781: test = right;
1782: } else if (right instanceof VariableRef) {
1783: ref = (VariableRef) right;
1784: test = left;
1785: } else {
1786: ref = null;
1787: test = null;
1788: }
1789:
1790: if (test != null && test.isValueKnown()
1791: && test.getValue() == null) {
1792:
1793: Type type = ref.getType();
1794: if (type != null) {
1795: // If the expression "var != null" is true, then
1796: // the the type becomes non-null.
1797:
1798: if (!type.isNonNull()) {
1799: addCast(ref, type.toNonNull(), forThenPart);
1800: }
1801: }
1802: }
1803: }
1804:
1805: return super .visit(node);
1806: }
1807:
1808: public Object visit(NotExpression node) {
1809: pushOp(NOT_OP);
1810: super .visit(node);
1811: popOp();
1812: return null;
1813: }
1814:
1815: public Object visit(AndExpression node) {
1816: pushOp(AND_OP);
1817: super .visit(node);
1818: popOp();
1819: return null;
1820: }
1821:
1822: public Object visit(OrExpression node) {
1823: pushOp(OR_OP);
1824: super .visit(node);
1825: popOp();
1826: return null;
1827: }
1828:
1829: private void pushOp(char op) {
1830: if (mOpStack == null) {
1831: mOpStack = new StringBuffer(4);
1832: }
1833: mOpStack.append(op);
1834: }
1835:
1836: private void popOp() {
1837: if (mOpStack != null) {
1838: int length = mOpStack.length();
1839: if (length > 0) {
1840: mOpStack.setLength(length - 1);
1841: }
1842: }
1843: }
1844:
1845: private void addCast(VariableRef ref, Type type,
1846: boolean forThenPart) {
1847: Variable oldVar = ref.getVariable();
1848: if (oldVar != null) {
1849: Variable newVar = (Variable) oldVar.clone();
1850: newVar.setType(type);
1851: newVar.setField(false);
1852:
1853: if (forThenPart) {
1854: if (mThenCasts == null) {
1855: mThenCasts = new ArrayList(2);
1856: }
1857: mThenCasts.add(newVar);
1858: } else {
1859: if (mElseCasts == null) {
1860: mElseCasts = new ArrayList(2);
1861: }
1862: mElseCasts.add(newVar);
1863: }
1864: }
1865: }
1866: }
1867:
1868: /**
1869: * Ensures that the template ends in a ReturnStatement. If the final
1870: * Statement in the template is an ExpressionStatement, then it is
1871: * converted to a ReturnStatement. Otherwise, a void ReturnStatement is
1872: * added to the end.
1873: */
1874: private static class ReturnConvertor extends TreeMutator {
1875: private boolean mReturnAdded;
1876:
1877: public Object visit(Template node) {
1878: Statement stmt = node.getStatement();
1879: if (stmt != null) {
1880: stmt = (Statement) stmt.accept(this );
1881: if (!mReturnAdded) {
1882: Statement[] stmts = new Statement[] { stmt,
1883: new ReturnStatement(stmt.getSourceInfo()) };
1884: stmt = new StatementList(stmt.getSourceInfo(),
1885: stmts);
1886: }
1887: node.setStatement(stmt);
1888: } else {
1889: node.setStatement(new ReturnStatement(node
1890: .getSourceInfo()));
1891: }
1892:
1893: return node;
1894: }
1895:
1896: public Object visit(Statement node) {
1897: return node;
1898: }
1899:
1900: public Object visit(StatementList node) {
1901: // Just traverse the last statement in the list.
1902: Statement[] statements = node.getStatements();
1903: for (int i = statements.length - 1; i >= 0; i--) {
1904: Statement stmt = statements[i];
1905: if (stmt != null) {
1906: statements[i] = (Statement) stmt.accept(this );
1907: break;
1908: }
1909: }
1910: return node;
1911: }
1912:
1913: public Object visit(Block node) {
1914: return visit((StatementList) node);
1915: }
1916:
1917: public Object visit(ExpressionStatement node) {
1918: mReturnAdded = true;
1919: Expression expr = node.getExpression();
1920: if (expr instanceof CallExpression) {
1921: ((CallExpression) expr).setVoidPermitted(true);
1922: }
1923: return new ReturnStatement(expr);
1924: }
1925:
1926: public Object visit(AssignmentStatement node) {
1927: // Skip traversing this node altogether.
1928: return node;
1929: }
1930:
1931: public Object visit(BreakStatement node) {
1932: // Skip traversing this node altogether.
1933: return node;
1934: }
1935:
1936: public Object visit(ForeachStatement node) {
1937: // Skip traversing this node altogether.
1938: return node;
1939: }
1940:
1941: public Object visit(IfStatement node) {
1942: // Skip traversing this node altogether.
1943: return node;
1944: }
1945:
1946: public Object visit(SubstitutionStatement node) {
1947: // Skip traversing this node altogether.
1948: return node;
1949: }
1950:
1951: public Object visit(ReturnStatement node) {
1952: // Skip traversing this node altogether.
1953: return node;
1954: }
1955: }
1956:
1957: /**
1958: * Wraps all statements that can throw an exception with an
1959: * ExceptionGuardStatement.
1960: */
1961: private static class ExceptionGuardian extends TreeMutator {
1962: public Object visit(AssignmentStatement node) {
1963: if (!node.getRValue().isExceptionPossible()) {
1964: return node;
1965: }
1966:
1967: SourceInfo info = node.getSourceInfo();
1968: VariableRef lvalue = node.getLValue();
1969:
1970: // Replacement assigns null to lvalue.
1971: Statement replacement = new AssignmentStatement(info,
1972: lvalue, new NullLiteral(info));
1973:
1974: return new ExceptionGuardStatement(node, replacement);
1975: }
1976:
1977: public Object visit(ForeachStatement node) {
1978: Block body = node.getBody();
1979: if (body != null) {
1980: node.setBody(visitBlock(body));
1981: }
1982:
1983: boolean guard = false;
1984: test: {
1985: Expression range = node.getRange();
1986: if (range != null) {
1987: if (range.isExceptionPossible()) {
1988: guard = true;
1989: break test;
1990: }
1991: Type type = range.getType();
1992: if (type != null && type.isNullable()) {
1993: guard = true;
1994: break test;
1995: }
1996: }
1997:
1998: range = node.getEndRange();
1999: if (range != null) {
2000: if (range.isExceptionPossible()) {
2001: guard = true;
2002: break test;
2003: }
2004: Type type = range.getType();
2005: if (type != null && type.isNullable()) {
2006: guard = true;
2007: break test;
2008: }
2009: }
2010: }
2011:
2012: if (!guard) {
2013: return node;
2014: }
2015:
2016: return new ExceptionGuardStatement(node, node
2017: .getInitializer());
2018: }
2019:
2020: public Object visit(IfStatement node) {
2021: Block block = node.getThenPart();
2022: if (block != null) {
2023: node.setThenPart(visitBlock(block));
2024: }
2025:
2026: block = node.getElsePart();
2027: if (block != null) {
2028: node.setElsePart(visitBlock(block));
2029: }
2030:
2031: if (!node.getCondition().isExceptionPossible()) {
2032: return node;
2033: }
2034:
2035: Variable[] vars = node.getMergedVariables();
2036: int length;
2037:
2038: if (vars == null || (length = vars.length) == 0) {
2039: return new ExceptionGuardStatement(node, null);
2040: }
2041:
2042: // Create replacement assignments to ensure merged variables get
2043: // assigned null.
2044: Statement[] replacements = new Statement[length];
2045: SourceInfo info = node.getSourceInfo();
2046:
2047: for (int i = 0; i < length; i++) {
2048: Variable v = vars[i];
2049: VariableRef lvalue = new VariableRef(v.getSourceInfo(),
2050: v.getName());
2051: lvalue.setVariable(v);
2052:
2053: replacements[i] = new AssignmentStatement(info, lvalue,
2054: new NullLiteral(info));
2055: }
2056:
2057: Statement replacement = new StatementList(info,
2058: replacements);
2059: return new ExceptionGuardStatement(node, replacement);
2060: }
2061:
2062: public Object visit(SubstitutionStatement node) {
2063: return new ExceptionGuardStatement(node, null);
2064: }
2065:
2066: public Object visit(ExpressionStatement node) {
2067: Expression expr = node.getExpression();
2068: if (expr != null) {
2069: if (expr instanceof CallExpression) {
2070: expr = (CallExpression) expr.accept(this );
2071: node.setExpression(expr);
2072: }
2073: if (expr.isExceptionPossible()) {
2074: return new ExceptionGuardStatement(node, null);
2075: }
2076: }
2077: return node;
2078: }
2079:
2080: public Object visit(ReturnStatement node) {
2081: Expression expr = node.getExpression();
2082: if (expr instanceof CallExpression) {
2083: node.setExpression((CallExpression) expr.accept(this ));
2084: }
2085: return node;
2086: }
2087:
2088: public Object visit(FunctionCallExpression node) {
2089: return visit((CallExpression) node);
2090: }
2091:
2092: public Object visit(TemplateCallExpression node) {
2093: return visit((CallExpression) node);
2094: }
2095:
2096: private Object visit(CallExpression node) {
2097: Block subParam = node.getSubstitutionParam();
2098: if (subParam != null) {
2099: node.setSubstitutionParam(visitBlock(subParam));
2100: }
2101: return node;
2102: }
2103: }
2104:
2105: /**
2106: * Reduces the amount of string concatenation operations performed by
2107: * breaking up ExpressionStatements that encapsulate ConcatenateExpressions
2108: * into several individual ExpressionStatements. Concatenations that are
2109: * enclosed in parenthesis are not broken up.
2110: */
2111: private static class ConcatenationReducer extends TreeMutator {
2112: public Object visit(ExpressionStatement node) {
2113: // Recurse into node.
2114: super .visit(node);
2115:
2116: Expression expr = node.getExpression();
2117: if (!(expr instanceof ConcatenateExpression)) {
2118: return node;
2119: }
2120:
2121: Collection statements = new ArrayList();
2122: breakup(statements, (ConcatenateExpression) expr);
2123:
2124: Statement[] stmts = new Statement[statements.size()];
2125: stmts = (Statement[]) statements.toArray(stmts);
2126:
2127: return new StatementList(node.getSourceInfo(), stmts);
2128: }
2129:
2130: private void breakup(Collection statements,
2131: ConcatenateExpression concat) {
2132:
2133: Expression left = concat.getLeftExpression();
2134: if (left instanceof ConcatenateExpression) {
2135: breakup(statements, ((ConcatenateExpression) left));
2136: } else {
2137: statements.add(new ExpressionStatement(left));
2138: }
2139:
2140: Expression right = concat.getRightExpression();
2141: if (right instanceof ConcatenateExpression) {
2142: breakup(statements, ((ConcatenateExpression) right));
2143: } else {
2144: statements.add(new ExpressionStatement(right));
2145: }
2146: }
2147: }
2148:
2149: /**
2150: * Increases the amount of string concatenation operations by finding
2151: * consecutive ExpressionStatements and merging them together into a
2152: * single ExpressionStatement that operates on the concatenated results.
2153: */
2154: private static class ConcatenationIncreaser extends TreeMutator {
2155: public Object visit(StatementList node) {
2156: // Recurse into node.
2157: super .visit(node);
2158:
2159: return new StatementList(node.getSourceInfo(), visit(node
2160: .getStatements()));
2161: }
2162:
2163: public Object visit(Block node) {
2164: // Recurse into node.
2165: super .visit(node);
2166:
2167: return new Block(node.getSourceInfo(), visit(node
2168: .getStatements()));
2169: }
2170:
2171: private Statement[] visit(Statement[] stmts) {
2172: int length = stmts.length;
2173:
2174: Collection statements = new ArrayList();
2175: List expressionStatements = new ArrayList();
2176:
2177: for (int i = 0; i < length; i++) {
2178: Statement stmt = stmts[i];
2179: if (stmt instanceof ExpressionStatement) {
2180: Expression expr = ((ExpressionStatement) stmt)
2181: .getExpression();
2182:
2183: if (!(expr instanceof CallExpression)) {
2184: expressionStatements.add(stmt);
2185: continue;
2186: }
2187: }
2188:
2189: merge(statements, expressionStatements);
2190: expressionStatements.clear();
2191: statements.add(stmt);
2192: }
2193:
2194: merge(statements, expressionStatements);
2195:
2196: stmts = new Statement[statements.size()];
2197: return (Statement[]) statements.toArray(stmts);
2198: }
2199:
2200: private void merge(Collection statements,
2201: List expressionStatements) {
2202: int size = expressionStatements.size();
2203:
2204: if (size == 0) {
2205: } else if (size == 1) {
2206: statements.add(expressionStatements.get(0));
2207: } else {
2208: List expressions = new ArrayList();
2209:
2210: for (int i = 0; i < size; i++) {
2211: ExpressionStatement stmt = (ExpressionStatement) expressionStatements
2212: .get(i);
2213: gatherExpressions(expressions, stmt.getExpression());
2214: }
2215:
2216: size = expressions.size();
2217: Expression concat = (Expression) expressions.get(0);
2218:
2219: for (int i = 1; i < size; i++) {
2220: Expression right = (Expression) expressions.get(i);
2221: SourceInfo rightInfo = right.getSourceInfo();
2222:
2223: SourceInfo info = concat.getSourceInfo()
2224: .setEndPosition(rightInfo);
2225:
2226: Token token = new Token(rightInfo, Token.CONCAT);
2227:
2228: concat = new ConcatenateExpression(info, token,
2229: concat, right);
2230: }
2231:
2232: statements.add(new ExpressionStatement(concat));
2233: }
2234: }
2235:
2236: private void gatherExpressions(Collection expressions,
2237: Expression expr) {
2238: if (expr instanceof ConcatenateExpression) {
2239: ConcatenateExpression concat = (ConcatenateExpression) expr;
2240: gatherExpressions(expressions, concat
2241: .getLeftExpression());
2242: gatherExpressions(expressions, concat
2243: .getRightExpression());
2244: } else {
2245: expressions.add(expr);
2246: }
2247: }
2248: }
2249: }
|