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.io.OutputStream;
0056: import java.io.IOException;
0057: import java.lang.reflect.*;
0058: import java.util.Map;
0059: import java.util.HashMap;
0060: import java.util.Collection;
0061: import java.util.Iterator;
0062: import java.util.ListIterator;
0063: import java.util.List;
0064: import java.util.ArrayList;
0065: import java.util.Stack;
0066: import java.beans.IntrospectionException;
0067: import java.math.BigInteger;
0068: import com.go.trove.classfile.*;
0069: import com.go.tea.parsetree.*;
0070: import com.go.tea.runtime.Substitution;
0071: import com.go.tea.runtime.SubstitutionId;
0072: import com.go.tea.runtime.Context;
0073:
0074: /******************************************************************************
0075: * The JavaClassGenerator compiles a template into a single Java class file.
0076: * A template is compiled such that it has two static methods, execute and
0077: * getTemplateParameterNames. The signatures are:
0078: *
0079: * <pre>
0080: * public static void execute(RuntimeContext, params ...) throws Exception;
0081: *
0082: * public static String[] getTemplateParameterNames();
0083: * </pre>
0084: *
0085: * @author Brian S O'Neill
0086: * @version
0087: * <!--$$Revision:--> 105 <!-- $-->, <!--$$JustDate:--> 01/05/31 <!-- $-->
0088: */
0089: public class JavaClassGenerator extends CodeGenerator {
0090: public static final String EXECUTE_METHOD_NAME = "execute";
0091: public static final String PARAMETER_METHOD_NAME = "getTemplateParameterNames";
0092:
0093: private static final String CONTEXT_PARAM_NAME = new String(
0094: "context");
0095: private static final String SUB_PARAM_NAME = new String("sub");
0096:
0097: // Length estimate of unknown concatenation elements.
0098: private static final int LENGTH_ESTIMATE = 16;
0099:
0100: // These should be final, but a bug in the JDK1.1.6 compiler is
0101: // preventing me from doing so. It is because they are accessed from
0102: // an inner class.
0103: private static TypeDescriptor cObjectDesc;
0104: private static TypeDescriptor[] cObjectParam;
0105: private static TypeDescriptor cStringDesc;
0106: private static TypeDescriptor[] cStringParam;
0107: private static TypeDescriptor cIntDesc;
0108: private static TypeDescriptor[] cIntParam;
0109: private static TypeDescriptor cStringBufferDesc;
0110:
0111: static {
0112: cObjectDesc = new TypeDescriptor(Object.class);
0113: cObjectParam = new TypeDescriptor[] { cObjectDesc };
0114:
0115: cStringDesc = new TypeDescriptor(String.class);
0116: cStringParam = new TypeDescriptor[] { cStringDesc };
0117:
0118: cIntDesc = new TypeDescriptor(int.class);
0119: cIntParam = new TypeDescriptor[] { cIntDesc };
0120:
0121: cStringBufferDesc = new TypeDescriptor(StringBuffer.class);
0122: }
0123:
0124: private CompilationUnit mUnit;
0125:
0126: /**
0127: * If a template has any substitution blocks that it passes in a
0128: * call expression, then the generated code is in a different format.
0129: *
0130: * Ordinarily, the generated class looks like this:
0131: *
0132: * public final class a.b.c.Template {
0133: * public static void execute(Context, params ...)
0134: * throws Exception {
0135: * ...
0136: * }
0137: *
0138: * private Template() {
0139: * super();
0140: * }
0141: * }
0142: *
0143: * When mSubBlockCount is greater than zero, the generated class looks
0144: * like this:
0145: *
0146: * public final class a.b.c.Template implements Substitution {
0147: * public static void execute(Context, params ...)
0148: * throws Exception {
0149: * new Template(Context, params ...).substitute();
0150: * }
0151: *
0152: * private int blockId;
0153: * private Context context;
0154: *
0155: * private Template(Context, params ...) {
0156: * super();
0157: * this.context = Context;
0158: * this.params = params;
0159: * }
0160: *
0161: * public void substitute() throws Exception {
0162: * if (this.context == null) {
0163: * throw new UnsupportedOperationException();
0164: * }
0165: * substitute(this.context);
0166: * }
0167: *
0168: * public void substitute(Context) throws Exception {
0169: * switch (this.blockId) {
0170: * case 0:
0171: * ...
0172: * case 1:
0173: * ...
0174: * case n:
0175: * ...
0176: * }
0177: * }
0178: *
0179: * public Object getIdentifier() {
0180: * return new SubstitutionId(this, blockId);
0181: * }
0182: *
0183: * public Substitution detach() {
0184: * Substitution sub = (Substitution)clone();
0185: * sub.context = null;
0186: * return sub;
0187: * }
0188: * }
0189: */
0190: private int mSubBlockCount;
0191:
0192: // Maps Variable names to variable object fields that need to be defined.
0193: private Map mFields = new HashMap();
0194:
0195: // A list of Statements that need to be put into a static initializer.
0196: private List mInitializerStatements = new ArrayList();
0197:
0198: public JavaClassGenerator(CompilationUnit unit) {
0199: super (unit.getParseTree());
0200: mUnit = unit;
0201: }
0202:
0203: public void writeTo(OutputStream out) throws IOException {
0204: String className = mUnit.getName();
0205: String targetPackage = mUnit.getTargetPackage();
0206: if (targetPackage != null) {
0207: className = targetPackage + '.' + className;
0208: }
0209:
0210: ClassFile classFile = new ClassFile(className);
0211: classFile.getAccessFlags().setFinal(true);
0212:
0213: String sourceFile = mUnit.getSourceFileName();
0214: if (sourceFile != null) {
0215: classFile.setSourceFile(sourceFile);
0216: }
0217:
0218: final Template t = getParseTree();
0219:
0220: t.accept(new TreeWalker() {
0221: public Object visit(FunctionCallExpression node) {
0222: if (node.getSubstitutionParam() != null) {
0223: mSubBlockCount++;
0224: }
0225: return super .visit(node);
0226: }
0227:
0228: public Object visit(TemplateCallExpression node) {
0229: if (node.getSubstitutionParam() != null) {
0230: mSubBlockCount++;
0231: }
0232: return super .visit(node);
0233: }
0234: });
0235:
0236: Class subClass;
0237: Method subMethod, subContextMethod, subIdMethod, subDetachMethod;
0238: if (mSubBlockCount > 0) {
0239: subClass = Substitution.class;
0240: classFile.addInterface(subClass);
0241: classFile.addInterface(Cloneable.class);
0242: classFile.addInterface(java.io.Serializable.class);
0243: try {
0244: subMethod = subClass.getMethod("substitute", null);
0245: subContextMethod = subClass.getMethod("substitute",
0246: new Class[] { Context.class });
0247: subIdMethod = subClass.getMethod("getIdentifier", null);
0248: subDetachMethod = subClass.getMethod("detach", null);
0249: } catch (NoSuchMethodException e) {
0250: throw new InternalError(e.toString());
0251: }
0252: } else {
0253: subClass = null;
0254: subMethod = null;
0255: subContextMethod = null;
0256: subIdMethod = null;
0257: subDetachMethod = null;
0258: }
0259:
0260: // Build the static getTemplateParameterNames method.
0261:
0262: Variable[] params = t.getParams();
0263: int paramCount = params.length;
0264:
0265: AccessFlags pubstat = new AccessFlags();
0266: pubstat.setPublic(true);
0267: pubstat.setStatic(true);
0268:
0269: TypeDescriptor stringArrayDesc = new TypeDescriptor(
0270: String[].class);
0271:
0272: MethodInfo mi = classFile.addMethod(pubstat,
0273: PARAMETER_METHOD_NAME, stringArrayDesc,
0274: new TypeDescriptor[0]);
0275:
0276: CodeBuilder builder = new CodeBuilder(mi);
0277: builder.loadConstant(paramCount);
0278: builder.newObject(stringArrayDesc);
0279:
0280: // Populate the array.
0281: for (int i = 0; i < paramCount; i++) {
0282: builder.dup();
0283: builder.loadConstant(i);
0284: builder.loadConstant(params[i].getName());
0285: builder.storeToArray(String.class);
0286: }
0287:
0288: builder.returnValue(String[].class);
0289:
0290: // Done building the static getTemplateParameterNames method.
0291:
0292: // Build the static execute method.
0293:
0294: Variable[] allParams;
0295: if (t.hasSubstitutionParam()) {
0296: allParams = new Variable[paramCount + 2];
0297: Type type = new Type(Substitution.class);
0298: Variable subParam = new Variable(null, SUB_PARAM_NAME, type);
0299: allParams[paramCount + 1] = subParam;
0300: } else {
0301: allParams = new Variable[paramCount + 1];
0302: }
0303:
0304: Type type = new Type(mUnit.getCompiler().getRuntimeContext());
0305: allParams[0] = new Variable(null, CONTEXT_PARAM_NAME, type);
0306: allParams[0].setTransient(true);
0307:
0308: for (int i = 0; i < paramCount; i++) {
0309: allParams[i + 1] = params[i];
0310: }
0311:
0312: TypeDescriptor[] paramTypes = new TypeDescriptor[allParams.length];
0313:
0314: for (int i = 0; i < allParams.length; i++) {
0315: if (mSubBlockCount > 0) {
0316: allParams[i].setField(true);
0317: }
0318: paramTypes[i] = new TypeDescriptor(allParams[i].getType()
0319: .getNaturalClass());
0320: }
0321:
0322: Type returnType = t.getReturnType();
0323: TypeDescriptor returnTypeDescriptor;
0324: if (returnType == null) {
0325: returnTypeDescriptor = null;
0326: } else {
0327: returnTypeDescriptor = new TypeDescriptor(returnType
0328: .getNaturalClass());
0329: }
0330:
0331: mi = classFile.addMethod(pubstat, EXECUTE_METHOD_NAME,
0332: returnTypeDescriptor, paramTypes);
0333:
0334: mi.addException("java.lang.Exception");
0335: builder = new CodeBuilder(mi);
0336:
0337: LocalVariable[] localVars = builder.getParameters();
0338: // Apply names to the passed in parameters, even though this step
0339: // is not required.
0340: for (int i = 0; i < localVars.length; i++) {
0341: localVars[i].setName(allParams[i].getName());
0342: }
0343:
0344: if (mSubBlockCount <= 0) {
0345: Visitor gen = new Visitor(allParams, builder
0346: .getParameters());
0347: gen.allowInitializerStatements();
0348: gen.generateNormalFormat(builder, t);
0349: } else {
0350: boolean doReturnValue = !(Type.VOID_TYPE.equals(returnType));
0351:
0352: // new <this>(params ...).substitute();
0353: TypeDescriptor this Type = new TypeDescriptor(className);
0354: builder.newObject(this Type);
0355: builder.dup();
0356: for (int i = 0; i < localVars.length; i++) {
0357: builder.loadLocal(localVars[i]);
0358: }
0359: builder.invokeConstructor(paramTypes);
0360:
0361: if (doReturnValue) {
0362: // We'll need the object again to load the field.
0363: builder.dup();
0364: }
0365:
0366: builder.invoke(subMethod);
0367:
0368: Visitor gen = new Visitor(allParams);
0369: gen.allowInitializerStatements();
0370:
0371: // Generate Substitution.substitute().
0372: mi = classFile.addMethod(subMethod);
0373: CodeBuilder subBuilder = new CodeBuilder(mi);
0374: gen.generateContext(subBuilder);
0375: Label gotContext = subBuilder.createLabel();
0376: subBuilder.ifNullBranch(gotContext, false);
0377: String unSupExName = UnsupportedOperationException.class
0378: .getName();
0379: subBuilder.newObject(new TypeDescriptor(unSupExName));
0380: subBuilder.dup();
0381: subBuilder.invokeConstructor(unSupExName, null);
0382: subBuilder.throwObject();
0383: gotContext.setLocation();
0384: subBuilder.loadThis();
0385: gen.generateContext(subBuilder);
0386: subBuilder.invoke(subContextMethod);
0387: subBuilder.returnVoid();
0388:
0389: // Generate Substitution.substitute(Context).
0390: mi = classFile.addMethod(subContextMethod);
0391: subBuilder = new CodeBuilder(mi);
0392:
0393: String[] subFieldNames = gen.generateSubFormat(subBuilder,
0394: t);
0395:
0396: // Finish execute method.
0397: if (doReturnValue) {
0398: // If template returns something, return it here by reading
0399: // from a special field.
0400: builder.loadField(subFieldNames[1],
0401: returnTypeDescriptor);
0402: builder.returnValue(returnType.getNaturalClass());
0403: } else {
0404: builder.returnVoid();
0405: }
0406:
0407: // Generate getIdentifier method.
0408: mi = classFile.addMethod(subIdMethod);
0409: subBuilder = new CodeBuilder(mi);
0410:
0411: TypeDescriptor td = new TypeDescriptor(SubstitutionId.class);
0412: subBuilder.newObject(td);
0413: subBuilder.dup();
0414: subBuilder.loadThis();
0415: subBuilder.loadThis();
0416: subBuilder.loadField(subFieldNames[0], cIntDesc);
0417: subBuilder.invokeConstructor(td.getClassName(),
0418: new TypeDescriptor[] { cObjectDesc, cIntDesc });
0419: subBuilder.returnValue(td.getClassArg());
0420:
0421: // Generate detach method.
0422: mi = classFile.addMethod(subDetachMethod);
0423: subBuilder = new CodeBuilder(mi);
0424: subBuilder.loadThis();
0425: subBuilder.invokeVirtual("clone", cObjectDesc, null);
0426: subBuilder.checkCast(this Type);
0427: LocalVariable sub = subBuilder.createLocalVariable(null,
0428: this Type);
0429: subBuilder.storeLocal(sub);
0430: subBuilder.loadLocal(sub);
0431: subBuilder.loadConstant(null);
0432: subBuilder.storeField(gen.mContextParam.getVariable()
0433: .getName(), paramTypes[0]);
0434: if (t.hasSubstitutionParam()) {
0435: // Also detach internal substitution parameter.
0436: subBuilder.loadLocal(sub);
0437: subBuilder.loadLocal(sub);
0438: String subFieldName = gen.mSubParam.getVariable()
0439: .getName();
0440: TypeDescriptor subType = new TypeDescriptor(subClass);
0441: subBuilder.loadField(subFieldName, subType);
0442: subBuilder.invoke(subDetachMethod);
0443: subBuilder.storeField(subFieldName, subType);
0444: }
0445: subBuilder.loadLocal(sub);
0446: subBuilder.returnValue(subClass);
0447: }
0448:
0449: // Done building the static execute method.
0450:
0451: // Build the private constructor.
0452:
0453: AccessFlags pvt = new AccessFlags();
0454: pvt.setPrivate(true);
0455:
0456: if (mSubBlockCount > 0) {
0457: mi = classFile.addConstructor(pvt, paramTypes);
0458: } else {
0459: mi = classFile.addConstructor(pvt, null);
0460: }
0461:
0462: builder = new CodeBuilder(mi);
0463: builder.loadThis();
0464: builder.invokeSuperConstructor(null);
0465:
0466: if (mSubBlockCount > 0) {
0467: // Copy all the params to fields.
0468:
0469: localVars = builder.getParameters();
0470:
0471: for (int i = 0; i < localVars.length; i++) {
0472: builder.loadThis();
0473: builder.loadLocal(localVars[i]);
0474: builder.storeField(allParams[i].getName(),
0475: paramTypes[i]);
0476: }
0477: }
0478:
0479: builder.returnVoid();
0480:
0481: // Build static initializer, if required.
0482:
0483: if (mInitializerStatements.size() > 0) {
0484: mi = classFile.addInitializer();
0485: builder = new CodeBuilder(mi);
0486:
0487: Visitor gen = new Visitor(new Variable[0]);
0488:
0489: for (int i = 0; i < mInitializerStatements.size(); i++) {
0490: Statement stmt = (Statement) mInitializerStatements
0491: .get(i);
0492: gen.generateNormalFormat(builder, stmt);
0493: }
0494:
0495: builder.returnVoid();
0496: }
0497:
0498: // Done building static initializer, if required.
0499:
0500: // Create any necessary private fields, after all methods have been
0501: // defined.
0502: Iterator it = mFields.values().iterator();
0503: AccessFlags flags = new AccessFlags();
0504: flags.setPrivate(true);
0505: while (it.hasNext()) {
0506: Variable v = (Variable) it.next();
0507: flags.setStatic(v.isStatic());
0508: flags.setTransient(v.isTransient());
0509: TypeDescriptor td = new TypeDescriptor(v.getType()
0510: .getNaturalClass());
0511: classFile.addField(flags, v.getName(), td).markSynthetic();
0512: }
0513:
0514: classFile.writeTo(out);
0515: out.flush();
0516: }
0517:
0518: private class Visitor implements NodeVisitor {
0519: private CodeBuilder mBuilder;
0520: private int mLastLine = -1;
0521:
0522: // Maps Variable objects to classfile.LocalVariable objects or
0523: // to null if they are fields.
0524: private Map mVariableMap = new HashMap();
0525:
0526: private boolean mAllowInitializerStatements;
0527:
0528: private VariableRef mContextParam;
0529: private VariableRef mSubParam;
0530:
0531: private VariableRef mBlockId;
0532: private VariableRef mReturnValue;
0533:
0534: private LocalVariable mTempObject;
0535:
0536: // used by break to determine what the next Statement will follow it.
0537: private Stack mForeachNestStack;
0538:
0539: // Is used when generating the substitution format to gather up
0540: // switch cases.
0541: private List mCaseNodes;
0542:
0543: // Exception guard handlers that need to be generated at the end.
0544: private List mExceptionGuardHandlers;
0545:
0546: /**
0547: * Construct a Vistor for generating code that can only read template
0548: * parameters from fields.
0549: */
0550: public Visitor(Variable[] params) {
0551: this (params, null);
0552: }
0553:
0554: /**
0555: * Construct a Vistor for generating code that assumes some (or all) of
0556: * the template parameters are available as local variables.
0557: */
0558: public Visitor(Variable[] params, LocalVariable[] localVars) {
0559: mForeachNestStack = new Stack();
0560: // Declare parameter variables.
0561: for (int i = 0; i < params.length; i++) {
0562: Variable param = params[i];
0563:
0564: if (param.isField()) {
0565: declareVariable(param);
0566: } else {
0567: declareVariable(param, localVars[i]);
0568: }
0569: }
0570: }
0571:
0572: /**
0573: * Calling this grants the ability to create or move certain
0574: * statements to a static initializer. The only time this is usually
0575: * not called is when constructing the static initializer itself.
0576: */
0577: public void allowInitializerStatements() {
0578: mAllowInitializerStatements = true;
0579: }
0580:
0581: /**
0582: * Invoked to generate static execute method for template with no
0583: * internal substitution blocks. Also use to generate static
0584: * initializer statements.
0585: */
0586: public void generateNormalFormat(CodeBuilder builder, Node node) {
0587: mBuilder = builder;
0588: generate(node);
0589: generateExceptionHandlers();
0590: }
0591:
0592: /**
0593: * Invoked to generate Substitution.substitute(Context) method.
0594: */
0595: public String[] generateSubFormat(CodeBuilder builder,
0596: Template node) {
0597: mBuilder = builder;
0598:
0599: // Acquire input parameter for use as context.
0600: final LocalVariable param = builder.getParameters()[0];
0601: Variable contextVar = mContextParam.getVariable();
0602: Variable newLocalContext = new Variable(null, contextVar
0603: .getName(), contextVar.getType());
0604: declareVariable(newLocalContext, param);
0605:
0606: // Cast local context parameter to ensure its the right type.
0607: mBuilder.loadLocal(param);
0608: mBuilder.checkCast(new TypeDescriptor(contextVar.getType()
0609: .getNaturalClass()));
0610: mBuilder.storeLocal(param);
0611:
0612: // Store context in field in case a substitution is exported from
0613: // this template.
0614: storeToVariable(contextVar, new Runnable() {
0615: public void run() {
0616: mBuilder.loadLocal(param);
0617: }
0618: });
0619: contextVar.setField(false);
0620:
0621: // Create ReturnValue field if template returns a value.
0622: Type returnType = node.getReturnType();
0623: if (!(Type.VOID_TYPE.equals(returnType))) {
0624: Variable returnValue = new Variable(null,
0625: "returnValue", returnType);
0626: returnValue.setField(true);
0627: declareVariable(returnValue);
0628:
0629: mReturnValue = new VariableRef(null, returnValue
0630: .getName());
0631: mReturnValue.setVariable(returnValue);
0632: }
0633:
0634: // Create blockId field.
0635: Type blockIdType = new Type(int.class);
0636: Variable blockId = new Variable(null, "blockId",
0637: blockIdType);
0638: blockId.setField(true);
0639: declareVariable(blockId);
0640:
0641: mBlockId = new VariableRef(null, blockId.getName());
0642: mBlockId.setVariable(blockId);
0643:
0644: // switch (blockId) {...
0645: mBuilder.loadThis();
0646: mBuilder.loadField(blockId.getName(), cIntDesc);
0647:
0648: // Create case labels.
0649: int[] cases = new int[mSubBlockCount + 1];
0650: Label[] switchLabels = new Label[mSubBlockCount + 1];
0651: mCaseNodes = new ArrayList(mSubBlockCount + 1);
0652:
0653: for (int i = 0; i <= mSubBlockCount; i++) {
0654: cases[i] = i;
0655: switchLabels[i] = mBuilder.createLabel();
0656: }
0657:
0658: Label defaultLabel = mBuilder.createLabel();
0659:
0660: mBuilder.switchBranch(cases, switchLabels, defaultLabel);
0661:
0662: int size = 0;
0663: int newSize;
0664: mCaseNodes.add(node);
0665: while ((newSize = mCaseNodes.size()) > size) {
0666: for (; size < newSize; size++) {
0667: if (size > 0) {
0668: mBuilder.returnVoid();
0669: }
0670: switchLabels[size].setLocation();
0671: generate((Node) mCaseNodes.get(size));
0672: }
0673: }
0674:
0675: defaultLabel.setLocation();
0676: mBuilder.returnVoid();
0677:
0678: generateExceptionHandlers();
0679:
0680: return new String[] {
0681: mBlockId.getName(),
0682: (mReturnValue != null) ? mReturnValue.getName()
0683: : null };
0684: }
0685:
0686: /**
0687: * Just pushes the context object reference onto the stack.
0688: */
0689: public void generateContext(CodeBuilder builder) {
0690: mBuilder = builder;
0691: if (mContextParam == null) {
0692: throw new NullPointerException(
0693: "Context parameter is null");
0694: } else {
0695: generate(mContextParam);
0696: }
0697: }
0698:
0699: private void generate(Node node) {
0700: try {
0701: setLineNumber(node.getSourceInfo());
0702: if (!(node instanceof Expression)) {
0703: node.accept(this );
0704: return;
0705: }
0706:
0707: // Expressions require special attention for type conversion.
0708:
0709: Expression expr = (Expression) node;
0710: List conversions = expr.getConversionChain();
0711:
0712: // Iterate conversion chain backwards at first.
0713: ListIterator it = conversions.listIterator(conversions
0714: .size());
0715:
0716: while (it.hasPrevious()) {
0717: typeConvertBegin((Expression.Conversion) it
0718: .previous());
0719: }
0720:
0721: expr.accept(this );
0722:
0723: while (it.hasNext()) {
0724: typeConvertEnd((Expression.Conversion) it.next());
0725: }
0726: } catch (DetailException e) {
0727: throw e;
0728: } catch (RuntimeException e) {
0729: throw new DetailException(e, "(near line " + mLastLine
0730: + ')');
0731: }
0732: }
0733:
0734: private void generateExceptionHandlers() {
0735: if (mExceptionGuardHandlers == null) {
0736: return;
0737: }
0738:
0739: int size = mExceptionGuardHandlers.size();
0740: if (size == 0) {
0741: return;
0742: }
0743:
0744: Label dumpException = mBuilder.createLabel();
0745:
0746: for (int i = 0; i < size; i++) {
0747: GuardHandler gh = (GuardHandler) mExceptionGuardHandlers
0748: .get(i);
0749: mBuilder.exceptionHandler(gh.tryStart, gh.tryEnd,
0750: "java.lang.RuntimeException");
0751: // Subroutine will pop exception object off stack.
0752: mBuilder.jsr(dumpException);
0753:
0754: if (gh.replacement != null) {
0755: generate(gh.replacement);
0756: }
0757:
0758: mBuilder.branch(gh.tryEnd);
0759: }
0760:
0761: dumpException.setLocation();
0762:
0763: // Generate code to pass caught exception to
0764: // ThreadGroup.uncaughtException.
0765:
0766: TypeDescriptor throwableDesc = new TypeDescriptor(
0767: Throwable.class);
0768: TypeDescriptor threadDesc = new TypeDescriptor(Thread.class);
0769: TypeDescriptor threadGroupDesc = new TypeDescriptor(
0770: ThreadGroup.class);
0771:
0772: LocalVariable exception = mBuilder.createLocalVariable("e",
0773: throwableDesc);
0774: LocalVariable retAddr = mBuilder.createLocalVariable(
0775: "addr", cObjectDesc);
0776: LocalVariable thread = mBuilder.createLocalVariable("t",
0777: threadDesc);
0778:
0779: // Capture return address.
0780: mBuilder.storeLocal(retAddr);
0781:
0782: // Assume caller has placed exception on stack.
0783: mBuilder.storeLocal(exception);
0784:
0785: mBuilder.invokeStatic("java.lang.Thread", "currentThread",
0786: threadDesc, null);
0787: mBuilder.storeLocal(thread);
0788: mBuilder.loadLocal(thread);
0789: mBuilder.invokeVirtual("java.lang.Thread",
0790: "getThreadGroup", threadGroupDesc, null);
0791: mBuilder.loadLocal(thread);
0792: mBuilder.loadLocal(exception);
0793: mBuilder.invokeVirtual("java.lang.ThreadGroup",
0794: "uncaughtException", null, new TypeDescriptor[] {
0795: threadDesc, throwableDesc });
0796:
0797: mBuilder.ret(retAddr);
0798: }
0799:
0800: //
0801: // Begin implementation of Visitor interface.
0802: //
0803:
0804: public Object visit(Template node) {
0805: // Generate the template body.
0806: Statement stmt = node.getStatement();
0807: if (stmt != null) {
0808: generate(stmt);
0809: }
0810:
0811: return null;
0812: }
0813:
0814: public Object visit(Name node) {
0815: return null;
0816: }
0817:
0818: public Object visit(TypeName node) {
0819: return null;
0820: }
0821:
0822: public Object visit(Variable node) {
0823: declareVariable(node);
0824: return null;
0825: }
0826:
0827: public Object visit(ExpressionList node) {
0828: return null;
0829: }
0830:
0831: public Object visit(Statement node) {
0832: return null;
0833: }
0834:
0835: public Object visit(StatementList node) {
0836: Statement[] list = node.getStatements();
0837: if (list == null) {
0838: return null;
0839: }
0840:
0841: for (int i = 0; i < list.length; i++) {
0842: generate(list[i]);
0843: }
0844:
0845: return null;
0846: }
0847:
0848: public Object visit(Block node) {
0849: Statement init = node.getInitializer();
0850: if (init != null) {
0851: generate(init);
0852: }
0853:
0854: visit((StatementList) node);
0855:
0856: Statement fin = node.getFinalizer();
0857: if (fin != null) {
0858: generate(fin);
0859: }
0860:
0861: return null;
0862: }
0863:
0864: public Object visit(AssignmentStatement node) {
0865: VariableRef lvalue = node.getLValue();
0866: Type ltype = lvalue.getType();
0867: Variable v = lvalue.getVariable();
0868: LocalVariable local = getLocalVariable(v);
0869:
0870: final Expression rvalue = node.getRValue();
0871: Type rtype = rvalue.getType();
0872:
0873: // Special optimization to convert a = a + n to a += n
0874: if (local != null && ltype.getNaturalClass() == int.class
0875: && rtype.getNaturalClass() == int.class
0876: && rvalue instanceof ArithmeticExpression) {
0877:
0878: ArithmeticExpression arith = (ArithmeticExpression) rvalue;
0879: int ID = arith.getOperator().getID();
0880: if (ID == Token.PLUS || ID == Token.MINUS) {
0881: Expression left = arith.getLeftExpression();
0882: Expression right = arith.getRightExpression();
0883: Integer amount = null;
0884: if (left instanceof VariableRef
0885: && right.isValueKnown()) {
0886: if (((VariableRef) left).getVariable() == v) {
0887: amount = (Integer) right.getValue();
0888: }
0889: } else if (right instanceof VariableRef
0890: && left.isValueKnown() && ID != Token.MINUS) {
0891: if (((VariableRef) right).getVariable() == v) {
0892: amount = (Integer) left.getValue();
0893: }
0894: }
0895:
0896: if (amount != null) {
0897: int i = amount.intValue();
0898: if (ID == Token.PLUS) {
0899: mBuilder.integerIncrement(local, i);
0900: } else {
0901: mBuilder.integerIncrement(local, -i);
0902: }
0903: return null;
0904: }
0905: }
0906: }
0907:
0908: storeToVariable(v, new Runnable() {
0909: public void run() {
0910: generate(rvalue);
0911: }
0912: });
0913:
0914: return null;
0915: }
0916:
0917: public Object visit(ForeachStatement node) {
0918:
0919: // generate a label for this foreach loop
0920: mForeachNestStack.push(mBuilder.createLabel());
0921:
0922: if (node.getEndRange() == null) {
0923: if (node.getRange().getType().getObjectClass()
0924: .isArray()) {
0925: generateForeachArray(node);
0926: } else {
0927: generateForeachIterator(node);
0928: }
0929: } else {
0930: generateForeachRange(node);
0931: }
0932: // pop the label
0933: Label l = (Label) mForeachNestStack.pop();
0934: l.setLocation();
0935: return null;
0936: }
0937:
0938: public Object visit(BreakStatement node) {
0939: Location l = (Location) mForeachNestStack.peek();
0940: mBuilder.branch(l);
0941: return null;
0942: }
0943:
0944: public Object visit(IfStatement node) {
0945: Statement thenPart = node.getThenPart();
0946: Statement elsePart = node.getElsePart();
0947:
0948: Label elseLabel = mBuilder.createLabel();
0949: Label endLabel = mBuilder.createLabel();
0950:
0951: if (thenPart == null) {
0952: generateBranch(node.getCondition(), endLabel, true);
0953: } else {
0954: generateBranch(node.getCondition(), elseLabel, false);
0955: }
0956:
0957: if (thenPart != null) {
0958: generate(thenPart);
0959: if (elsePart != null) {
0960: mBuilder.branch(endLabel);
0961: }
0962: }
0963:
0964: elseLabel.setLocation();
0965:
0966: if (elsePart != null) {
0967: generate(elsePart);
0968: }
0969:
0970: endLabel.setLocation();
0971:
0972: return null;
0973: }
0974:
0975: public Object visit(SubstitutionStatement node) {
0976: Class subClass = Substitution.class;
0977: Method subMethod;
0978: try {
0979: // Always pass context into substitution in order for this
0980: // template's own substitutions to be safely detached.
0981: subMethod = subClass.getMethod("substitute",
0982: new Class[] { Context.class });
0983: } catch (NoSuchMethodException e) {
0984: throw new RuntimeException(e.toString());
0985: }
0986:
0987: generate(mSubParam);
0988: generateContext();
0989: mBuilder.invoke(subMethod);
0990:
0991: return null;
0992: }
0993:
0994: public Object visit(ExpressionStatement node) {
0995: Method receiver = node.getReceiverMethod();
0996:
0997: if (receiver != null
0998: && !Modifier.isStatic(receiver.getModifiers())) {
0999: generateContext();
1000: }
1001:
1002: generate(node.getExpression());
1003:
1004: if (receiver != null) {
1005: mBuilder.invoke(receiver);
1006:
1007: Class retType = receiver.getReturnType();
1008: if (retType != null && retType != void.class) {
1009: if (retType == long.class
1010: || retType == double.class) {
1011: mBuilder.pop2();
1012: } else {
1013: mBuilder.pop();
1014: }
1015: }
1016: }
1017:
1018: return null;
1019: }
1020:
1021: public Object visit(ReturnStatement node) {
1022: Expression expr = node.getExpression();
1023:
1024: // If generating sub-format...
1025: if (mCaseNodes != null) {
1026: if (expr != null) {
1027: Type type = expr.getType();
1028: if (!(Type.VOID_TYPE.equals(type))) {
1029: // Generating sub-format and returning non-void, so
1030: // store result in a field for later retrieval.
1031: mBuilder.loadThis();
1032: generate(node.getExpression());
1033: TypeDescriptor td = makeDesc(mReturnValue
1034: .getType().getNaturalClass());
1035: mBuilder.storeField(mReturnValue.getName(), td);
1036: } else {
1037: generate(node.getExpression());
1038: }
1039: }
1040: } else if (expr != null) {
1041: generate(node.getExpression());
1042: mBuilder.returnValue(expr.getType().getNaturalClass());
1043: } else {
1044: mBuilder.returnVoid();
1045: }
1046:
1047: return null;
1048: }
1049:
1050: public Object visit(ExceptionGuardStatement node) {
1051: Statement guarded = node.getGuarded();
1052: if (guarded == null) {
1053: return null;
1054: }
1055: Statement replacement = node.getReplacement();
1056:
1057: if (guarded.isReturn()) {
1058: if (replacement == null || !replacement.isReturn()) {
1059: // No replacement return statement, so exception shouldn't
1060: // be caught.
1061: generate(guarded);
1062: return null;
1063: }
1064: }
1065:
1066: Label tryStart = mBuilder.createLabel().setLocation();
1067: generate(guarded);
1068: Label tryEnd = mBuilder.createLabel().setLocation();
1069:
1070: if (mExceptionGuardHandlers == null) {
1071: mExceptionGuardHandlers = new ArrayList();
1072: }
1073:
1074: mExceptionGuardHandlers.add(new GuardHandler(tryStart,
1075: tryEnd, replacement));
1076:
1077: return null;
1078: }
1079:
1080: public Object visit(Expression node) {
1081: return null;
1082: }
1083:
1084: public Object visit(ParenExpression node) {
1085: generate(node.getExpression());
1086: return null;
1087: }
1088:
1089: public Object visit(NewArrayExpression node) {
1090: ExpressionList list = node.getExpressionList();
1091: Expression[] exprs = list.getExpressions();
1092:
1093: Type initialType = node.getInitialType();
1094: Class initialClass = initialType.getObjectClass();
1095:
1096: // Check if this array is composed entirely of constants,
1097: // and if so, make the array a static field, and populate
1098: // it in a static initializer.
1099: if (mAllowInitializerStatements && node.isAllConstant()) {
1100: SourceInfo info = node.getSourceInfo();
1101:
1102: // Create a variable...
1103: Variable var = new Variable(info, "array", initialType);
1104: var.setStatic(true);
1105: generate(var);
1106: // Create an assignment statement...
1107: VariableRef ref = new VariableRef(info, "array");
1108: ref.setVariable(var);
1109:
1110: // Clone the NewArrayExpression so that the type can be
1111: // changed back to the underlying array type. This prevents
1112: // unecessary casting in the assignment statement.
1113: Expression clonedNode = (Expression) node.clone();
1114: clonedNode.setType(initialType);
1115:
1116: AssignmentStatement assn = new AssignmentStatement(
1117: info, ref, clonedNode);
1118:
1119: // Move this statement into the static initializer
1120: mInitializerStatements.add(assn);
1121:
1122: // Substitute a field access for a NewArrayExpression
1123: TypeDescriptor td = makeDesc(initialClass);
1124: mBuilder.loadStaticField(var.getName(), td);
1125: } else if (node.isAssociative()) {
1126: // Constuct HashMap to support associative array.
1127: TypeDescriptor td = makeDesc(HashMap.class);
1128: mBuilder.newObject(td);
1129: mBuilder.dup();
1130:
1131: int capacity = exprs.length;
1132: if (capacity == 0) {
1133: // Should probably use a JDK1.3 EMPTY_MAP.
1134: } else if (capacity == 2) {
1135: // Should probably use a JDK1.3 singletonMap.
1136: capacity = 1;
1137: } else {
1138: // Initial capacity at least 2n + 1.
1139: BigInteger cap = BigInteger.valueOf(capacity + 1);
1140: // Ensure capacity is prime to reduce hash collisions.
1141: while (!cap.isProbablePrime(100)) {
1142: cap = cap.add(BigInteger.valueOf(2));
1143: }
1144: capacity = cap.intValue();
1145: }
1146:
1147: mBuilder.loadConstant(capacity);
1148: mBuilder.invokeConstructor(HashMap.class.getName(),
1149: new TypeDescriptor[] { cIntDesc });
1150:
1151: Method putMethod;
1152: try {
1153: putMethod = HashMap.class.getMethod("put",
1154: new Class[] { Object.class, Object.class });
1155: } catch (NoSuchMethodException e) {
1156: throw new RuntimeException(e.toString());
1157: }
1158:
1159: boolean doPop = (putMethod.getReturnType() != void.class);
1160:
1161: // Perform put operations to populate the Map.
1162: for (int i = 0; i < exprs.length; i += 2) {
1163: mBuilder.dup(); // duplicate the Map
1164: generate(exprs[i]); // generate the key
1165: generate(exprs[i + 1]); // generate the value
1166: mBuilder.invoke(putMethod);
1167: if (doPop) {
1168: mBuilder.pop();
1169: }
1170: }
1171: } else {
1172: Class componentClass = initialClass.getComponentType();
1173: TypeDescriptor componentType = makeDesc(componentClass);
1174:
1175: mBuilder.loadConstant(exprs.length);
1176: mBuilder
1177: .newObject(new TypeDescriptor(componentType, 1));
1178:
1179: // Populate the array.
1180: for (int i = 0; i < exprs.length; i++) {
1181: Expression expr = exprs[i];
1182: if (expr.isValueKnown()) {
1183: // Check if expression value is the same as the default
1184: // for values in a newly initialized array.
1185:
1186: Object value = expr.getValue();
1187: Type type = expr.getType();
1188: if (!type.isPrimitive()) {
1189: if (value == null) {
1190: continue;
1191: }
1192: } else {
1193: if (value instanceof Number) {
1194: if (((Number) value).doubleValue() == 0) {
1195: continue;
1196: }
1197: } else if (value instanceof Boolean) {
1198: if (((Boolean) value).booleanValue() == false) {
1199: continue;
1200: }
1201: }
1202: }
1203: }
1204:
1205: mBuilder.dup();
1206: mBuilder.loadConstant(i);
1207: generate(expr);
1208: mBuilder.storeToArray(componentClass);
1209: }
1210: }
1211:
1212: return null;
1213: }
1214:
1215: public Object visit(FunctionCallExpression node) {
1216: generateCallExpression(node);
1217: return null;
1218: }
1219:
1220: public Object visit(TemplateCallExpression node) {
1221: generateCallExpression(node);
1222: return null;
1223: }
1224:
1225: public Object visit(VariableRef node) {
1226: loadFromVariable(node.getVariable());
1227: return null;
1228: }
1229:
1230: public Object visit(Lookup node) {
1231: Expression expr = node.getExpression();
1232: String lookupName = node.getLookupName().getName();
1233: Method readMethod = node.getReadMethod();
1234:
1235: generate(expr);
1236: setLineNumber(node.getDot().getSourceInfo());
1237:
1238: if (expr.getType().getObjectClass().isArray()
1239: && lookupName.equals("length")) {
1240:
1241: mBuilder.arrayLength();
1242: } else {
1243: if (Modifier.isStatic(readMethod.getModifiers())) {
1244: // Discard the object to call method on.
1245: mBuilder.pop();
1246: }
1247:
1248: mBuilder.invoke(readMethod);
1249: }
1250:
1251: setLineNumber(node.getLookupName().getSourceInfo());
1252: return null;
1253: }
1254:
1255: public Object visit(ArrayLookup node) {
1256: Expression expr = node.getExpression();
1257: Expression lookup = node.getLookupIndex();
1258: Method readMethod = node.getReadMethod();
1259:
1260: Type type = expr.getType();
1261: boolean doArrayLookup;
1262: Class lookupClass = type.getObjectClass();
1263:
1264: if (lookupClass.isArray()) {
1265: doArrayLookup = true;
1266: } else {
1267: doArrayLookup = false;
1268: }
1269:
1270: generate(expr);
1271:
1272: if (!doArrayLookup
1273: && Modifier.isStatic(readMethod.getModifiers())) {
1274:
1275: // Discard the object to call method on.
1276: mBuilder.pop();
1277: }
1278:
1279: generate(lookup);
1280:
1281: setLineNumber(node.getLookupToken().getSourceInfo());
1282:
1283: if (!doArrayLookup) {
1284: mBuilder.invoke(readMethod);
1285: } else {
1286: Class elementClass;
1287: try {
1288: elementClass = expr.getInitialType()
1289: .getArrayElementType().getNaturalClass();
1290: } catch (IntrospectionException e) {
1291: throw new RuntimeException(e.toString());
1292: }
1293:
1294: mBuilder.loadFromArray(elementClass);
1295: }
1296:
1297: return null;
1298: }
1299:
1300: public Object visit(NegateExpression node) {
1301: Expression expr = node.getExpression();
1302: Class exprClass = expr.getType().getNaturalClass();
1303:
1304: generate(expr);
1305:
1306: byte opcode;
1307: if (exprClass == int.class) {
1308: opcode = Opcode.INEG;
1309: } else if (exprClass == float.class) {
1310: opcode = Opcode.FNEG;
1311: } else if (exprClass == long.class) {
1312: opcode = Opcode.LNEG;
1313: } else if (exprClass == double.class) {
1314: opcode = Opcode.DNEG;
1315: } else {
1316: opcode = Opcode.INEG;
1317: }
1318:
1319: mBuilder.math(opcode);
1320:
1321: return null;
1322: }
1323:
1324: public Object visit(ConcatenateExpression node) {
1325: Expression left = node.getLeftExpression();
1326: Expression right = node.getRightExpression();
1327: Type leftType = left.getType();
1328: Type rightType = right.getType();
1329:
1330: // If concatenation requires a chain of more than two append calls,
1331: // generate a StringBuffer append chain.
1332: if (left instanceof ConcatenateExpression
1333: ||
1334: // Note: These are just sanity checks since the type checker is
1335: // supposed to ensure that sub nodes are converted to non-null
1336: // strings.
1337: leftType.isNullable()
1338: || leftType.getObjectClass() != String.class
1339: || rightType.isNullable()
1340: || rightType.getObjectClass() != String.class) {
1341:
1342: generateAppend(node, estimateBufferRequirement(node));
1343: mBuilder.invokeVirtual("java.lang.StringBuffer",
1344: "toString", cStringDesc, null);
1345: } else {
1346: // Concatenation is of the form 'a & b' or 'a & (b & c)' so
1347: // call String.concat as an optimization. It allocates less
1348: // objects, and it does no synchronization.
1349: generate(left);
1350: generate(right);
1351: mBuilder.invokeVirtual("java.lang.String", "concat",
1352: cStringDesc, cStringParam);
1353: }
1354:
1355: return null;
1356: }
1357:
1358: public Object visit(ArithmeticExpression node) {
1359: Expression left = node.getLeftExpression();
1360: Expression right = node.getRightExpression();
1361:
1362: Type type = left.getType();
1363: if (type == null || !type.equals(right.getType())) {
1364: throw new RuntimeException(
1365: "ArithmeticExpression types don't match: "
1366: + type + ", " + right.getType());
1367: }
1368:
1369: generate(node.getLeftExpression());
1370: generate(node.getRightExpression());
1371:
1372: setLineNumber(node.getOperator().getSourceInfo());
1373:
1374: byte opcode = Opcode.NOP;
1375: int ID = node.getOperator().getID();
1376:
1377: Class clazz = type.getNaturalClass();
1378: if (clazz == int.class) {
1379: switch (ID) {
1380: case Token.PLUS:
1381: opcode = Opcode.IADD;
1382: break;
1383: case Token.MINUS:
1384: opcode = Opcode.ISUB;
1385: break;
1386: case Token.MULT:
1387: opcode = Opcode.IMUL;
1388: break;
1389: case Token.DIV:
1390: opcode = Opcode.IDIV;
1391: break;
1392: case Token.MOD:
1393: opcode = Opcode.IREM;
1394: break;
1395: }
1396: } else if (clazz == float.class) {
1397: switch (ID) {
1398: case Token.PLUS:
1399: opcode = Opcode.FADD;
1400: break;
1401: case Token.MINUS:
1402: opcode = Opcode.FSUB;
1403: break;
1404: case Token.MULT:
1405: opcode = Opcode.FMUL;
1406: break;
1407: case Token.DIV:
1408: opcode = Opcode.FDIV;
1409: break;
1410: case Token.MOD:
1411: opcode = Opcode.FREM;
1412: break;
1413: }
1414: } else if (clazz == long.class) {
1415: switch (ID) {
1416: case Token.PLUS:
1417: opcode = Opcode.LADD;
1418: break;
1419: case Token.MINUS:
1420: opcode = Opcode.LSUB;
1421: break;
1422: case Token.MULT:
1423: opcode = Opcode.LMUL;
1424: break;
1425: case Token.DIV:
1426: opcode = Opcode.LDIV;
1427: break;
1428: case Token.MOD:
1429: opcode = Opcode.LREM;
1430: break;
1431: }
1432: } else if (clazz == double.class) {
1433: switch (ID) {
1434: case Token.PLUS:
1435: opcode = Opcode.DADD;
1436: break;
1437: case Token.MINUS:
1438: opcode = Opcode.DSUB;
1439: break;
1440: case Token.MULT:
1441: opcode = Opcode.DMUL;
1442: break;
1443: case Token.DIV:
1444: opcode = Opcode.DDIV;
1445: break;
1446: case Token.MOD:
1447: opcode = Opcode.DREM;
1448: break;
1449: }
1450: }
1451:
1452: mBuilder.math(opcode);
1453:
1454: return null;
1455: }
1456:
1457: public Object visit(RelationalExpression node) {
1458: generateLogical((Expression) node);
1459: return null;
1460: }
1461:
1462: public Object visit(NotExpression node) {
1463: generateLogical((Expression) node);
1464: return null;
1465: }
1466:
1467: public Object visit(AndExpression node) {
1468: generateLogical((Expression) node);
1469: return null;
1470: }
1471:
1472: public Object visit(OrExpression node) {
1473: generateLogical((Expression) node);
1474: return null;
1475: }
1476:
1477: // Code generation methods for literals.
1478:
1479: public Object visit(NullLiteral node) {
1480: mBuilder.loadConstant(null);
1481: return null;
1482: }
1483:
1484: public Object visit(BooleanLiteral node) {
1485: boolean value = ((Boolean) node.getValue()).booleanValue();
1486: Type type = node.getType();
1487: Class clazz = type.getNaturalClass();
1488: if (clazz == boolean.class) {
1489: mBuilder.loadConstant(value);
1490: } else if (clazz.isAssignableFrom(Boolean.class)) {
1491: TypeDescriptor td = makeDesc(Boolean.class);
1492:
1493: if (value) {
1494: mBuilder.loadStaticField("java.lang.Boolean",
1495: "TRUE", td);
1496: } else {
1497: mBuilder.loadStaticField("java.lang.Boolean",
1498: "FALSE", td);
1499: }
1500: } else if (clazz.isAssignableFrom(String.class)) {
1501: mBuilder.loadConstant(String.valueOf(value));
1502: } else {
1503: typeError(new Type(boolean.class), type);
1504: }
1505:
1506: return null;
1507: }
1508:
1509: public Object visit(StringLiteral node) {
1510: mBuilder.loadConstant((String) node.getValue());
1511: return null;
1512: }
1513:
1514: public Object visit(NumberLiteral node) {
1515: Number value = (Number) node.getValue();
1516: Type toType = node.getType();
1517: Type fromType;
1518:
1519: if (Number.class.isAssignableFrom(toType.getObjectClass())) {
1520: fromType = toType.toPrimitive();
1521: } else {
1522: fromType = new Type(value.getClass()).toPrimitive();
1523: }
1524:
1525: Class fromClass = fromType.getObjectClass();
1526:
1527: if (Integer.class.isAssignableFrom(fromClass)) {
1528: mBuilder.loadConstant(value.intValue());
1529: } else if (Float.class.isAssignableFrom(fromClass)) {
1530: mBuilder.loadConstant(value.floatValue());
1531: } else if (Long.class.isAssignableFrom(fromClass)) {
1532: mBuilder.loadConstant(value.longValue());
1533: } else if (Double.class.isAssignableFrom(fromClass)) {
1534: mBuilder.loadConstant(value.doubleValue());
1535: } else {
1536: typeError(fromType, toType);
1537: }
1538:
1539: return null;
1540: }
1541:
1542: //
1543: // End implementation of Visitor interface.
1544: //
1545:
1546: private void generateContext() {
1547: if (mContextParam == null) {
1548: throw new NullPointerException(
1549: "Context parameter is null");
1550: } else {
1551: generate(mContextParam);
1552: }
1553: }
1554:
1555: private void generateBranch(Expression expr, Label label,
1556: boolean whenTrue) {
1557: if (expr instanceof Logical) {
1558: // What follows is something that the visitor design pattern
1559: // solves, but I would need to make a special visitor
1560: // interface that operates only on logicals that also accepts
1561: // labels and "whenTrue" flags.
1562:
1563: if (expr instanceof RelationalExpression) {
1564: generateBranch((RelationalExpression) expr, label,
1565: whenTrue);
1566: } else if (expr instanceof NotExpression) {
1567: generateBranch((NotExpression) expr, label,
1568: whenTrue);
1569: } else if (expr instanceof AndExpression) {
1570: generateBranch((AndExpression) expr, label,
1571: whenTrue);
1572: } else if (expr instanceof OrExpression) {
1573: generateBranch((OrExpression) expr, label, whenTrue);
1574: }
1575: } else {
1576: // Generate branch in the same special way for all
1577: // non-logical expressions.
1578:
1579: // Generate code to push value on the stack
1580: generate(expr);
1581:
1582: // Generate branch that acts on expression value
1583: mBuilder.ifZeroComparisonBranch(label, whenTrue ? "!="
1584: : "==");
1585: }
1586: }
1587:
1588: private void generateBranch(RelationalExpression expr,
1589: Label label, boolean whenTrue) {
1590: // RelationalExpressions produce different comparision instructions
1591: // based on the type of the expression.
1592:
1593: Token operator = expr.getOperator();
1594: Expression left = expr.getLeftExpression();
1595:
1596: if (operator.getID() == Token.ISA) {
1597: TypeName typeName = expr.getIsaTypeName();
1598: Class typeClass = typeName.getType().getObjectClass();
1599:
1600: generate(left);
1601: setLineNumber(operator.getSourceInfo());
1602: mBuilder.instanceOf(makeDesc(typeClass));
1603: mBuilder.ifZeroComparisonBranch(label, whenTrue ? "!="
1604: : "==");
1605:
1606: return;
1607: }
1608:
1609: Expression right = expr.getRightExpression();
1610: String choice = getChoice(operator, whenTrue);
1611:
1612: Type leftType = left.getType();
1613: Type rightType = right.getType();
1614:
1615: Class clazz = Type.findCommonBaseClass(leftType
1616: .getNaturalClass(), rightType.getNaturalClass());
1617:
1618: if (clazz == null) {
1619: throw new RuntimeException("Relational type mismatch: "
1620: + leftType + ", " + rightType);
1621: }
1622:
1623: if (!leftType.isPrimitive()) {
1624: String className;
1625: String methodName;
1626: TypeDescriptor ret;
1627:
1628: if (choice == "==" || choice == "!=") {
1629: if (right.isValueKnown()
1630: && right.getValue() == null) {
1631: generate(left);
1632: mBuilder.ifNullBranch(label, choice == "==");
1633: } else if (left.isValueKnown()
1634: && left.getValue() == null) {
1635: generate(right);
1636: mBuilder.ifNullBranch(label, choice == "==");
1637: } else {
1638: className = "java.lang.Object";
1639: methodName = "equals";
1640: ret = makeDesc(boolean.class);
1641:
1642: if (leftType.isNonNull()
1643: || right.isValueKnown()) {
1644: if (leftType.isNonNull()) {
1645: generate(left);
1646: generate(right);
1647: } else {
1648: // Reversing the order of generation is
1649: // safe because the right expression is
1650: // constant.
1651: generate(right);
1652: generate(left);
1653: }
1654: setLineNumber(operator.getSourceInfo());
1655: mBuilder.invokeVirtual(className,
1656: methodName, ret, cObjectParam);
1657: mBuilder.ifZeroComparisonBranch(label,
1658: (choice == "==") ? "!=" : "==");
1659: } else {
1660: // This is a little bit complicated. The results
1661: // of the left expression are tested against null
1662: // before the equals method is invoked on it.
1663:
1664: generate(left);
1665: mBuilder.dup();
1666: Label leftNotNull = mBuilder.createLabel();
1667: mBuilder.ifNullBranch(leftNotNull, false);
1668: mBuilder.pop(); // discard left expression result
1669:
1670: Label fallThrough = mBuilder.createLabel();
1671:
1672: if (rightType.isNonNull()) {
1673: if (choice == "==") {
1674: mBuilder.branch(fallThrough);
1675: } else {
1676: mBuilder.branch(label);
1677: }
1678: } else {
1679: generate(right);
1680: mBuilder.ifNullBranch(label,
1681: choice == "==");
1682: mBuilder.branch(fallThrough);
1683: }
1684:
1685: leftNotNull.setLocation();
1686: generate(right);
1687: setLineNumber(operator.getSourceInfo());
1688: mBuilder.invokeVirtual(className,
1689: methodName, ret, cObjectParam);
1690: mBuilder.ifZeroComparisonBranch(label,
1691: (choice == "==") ? "!=" : "==");
1692:
1693: fallThrough.setLocation();
1694: }
1695: }
1696: } else if (String.class.isAssignableFrom(clazz)) {
1697: // The compareTo method is called only for <, >, <= or >=
1698: // relational operators.
1699: generate(left);
1700: generate(right);
1701: setLineNumber(operator.getSourceInfo());
1702:
1703: mBuilder.invokeVirtual("java.lang.String",
1704: "compareTo", cIntDesc, cStringParam);
1705: mBuilder.ifZeroComparisonBranch(label, choice);
1706: } else if (Comparable.class.isAssignableFrom(clazz)) {
1707: // The compareTo method is called only for <, >, <= or >=
1708: // relational operators.
1709: generate(left);
1710: generate(right);
1711: setLineNumber(operator.getSourceInfo());
1712:
1713: mBuilder.invokeInterface("java.lang.Comparable",
1714: "compareTo", cIntDesc, cObjectParam);
1715: mBuilder.ifZeroComparisonBranch(label, choice);
1716: } else {
1717: throw new RuntimeException("Can't do " + choice
1718: + " for type " + leftType);
1719: }
1720: } else {
1721: // Deal with expressions that evaluate to primitive types...
1722: if (clazz == int.class) {
1723: if (right.isValueKnown()) {
1724: int value = ((Integer) right.getValue())
1725: .intValue();
1726: if (value == 0) {
1727: generate(left);
1728: mBuilder.ifZeroComparisonBranch(label,
1729: choice);
1730: return;
1731: }
1732: } else if (left.isValueKnown()) {
1733: int value = ((Integer) left.getValue())
1734: .intValue();
1735: if (value == 0) {
1736: generate(right);
1737: choice = getChoice(operator, !whenTrue);
1738: mBuilder.ifZeroComparisonBranch(label,
1739: choice);
1740: return;
1741: }
1742: }
1743:
1744: generate(left);
1745: generate(right);
1746: mBuilder.ifComparisonBranch(label, choice);
1747: } else if (clazz == boolean.class) {
1748: // An optimizer should be able to detect and reduce this
1749: // relational expression if the left or right side
1750: // is constant.
1751:
1752: generate(left);
1753: generate(right);
1754: mBuilder.ifComparisonBranch(label, choice);
1755: } else {
1756: generate(left);
1757: generate(right);
1758:
1759: byte op;
1760:
1761: if (clazz == long.class) {
1762: op = Opcode.LCMP;
1763: } else if (clazz == float.class) {
1764: int ID = operator.getID();
1765: if (ID == Token.LT || ID == Token.LE
1766: || ID == Token.EQ) {
1767: op = Opcode.FCMPG;
1768: } else {
1769: op = Opcode.FCMPL;
1770: }
1771: } else if (clazz == double.class) {
1772: int ID = operator.getID();
1773: if (ID == Token.LT || ID == Token.LE
1774: || ID == Token.EQ) {
1775: op = Opcode.DCMPG;
1776: } else {
1777: op = Opcode.DCMPL;
1778: }
1779: } else {
1780: throw new RuntimeException(
1781: "Unsupported comparison " + "type: "
1782: + leftType);
1783: }
1784:
1785: mBuilder.math(op);
1786: mBuilder.ifZeroComparisonBranch(label, choice);
1787: }
1788: }
1789: }
1790:
1791: // "not", "and" and "or" have short circuit semantics.
1792:
1793: private void generateBranch(NotExpression expr, Label label,
1794: boolean whenTrue) {
1795: // If someone is branching based on the result of this not
1796: // expression, just invert the branch condition.
1797: generateBranch(expr.getExpression(), label, !whenTrue);
1798: }
1799:
1800: private void generateBranch(AndExpression expr, Label label,
1801: boolean whenTrue) {
1802: if (whenTrue) {
1803: Label falseLabel = mBuilder.createLabel();
1804: generateBranch(expr.getLeftExpression(), falseLabel,
1805: false);
1806: generateBranch(expr.getRightExpression(), label, true);
1807: falseLabel.setLocation();
1808: } else {
1809: generateBranch(expr.getLeftExpression(), label, false);
1810: generateBranch(expr.getRightExpression(), label, false);
1811: }
1812: }
1813:
1814: private void generateBranch(OrExpression expr, Label label,
1815: boolean whenTrue) {
1816: if (whenTrue) {
1817: generateBranch(expr.getLeftExpression(), label, true);
1818: generateBranch(expr.getRightExpression(), label, true);
1819: } else {
1820: Label trueLabel = mBuilder.createLabel();
1821: generateBranch(expr.getLeftExpression(), trueLabel,
1822: true);
1823: generateBranch(expr.getRightExpression(), label, false);
1824: trueLabel.setLocation();
1825: }
1826: }
1827:
1828: private String getChoice(Token operator, boolean whenTrue) {
1829: if (whenTrue) {
1830: switch (operator.getID()) {
1831: case Token.EQ:
1832: return "==";
1833: case Token.NE:
1834: return "!=";
1835: case Token.LT:
1836: return "<";
1837: case Token.GT:
1838: return ">";
1839: case Token.LE:
1840: return "<=";
1841: case Token.GE:
1842: return ">=";
1843: }
1844: } else {
1845: switch (operator.getID()) {
1846: case Token.EQ:
1847: return "!=";
1848: case Token.NE:
1849: return "==";
1850: case Token.LT:
1851: return ">=";
1852: case Token.GT:
1853: return "<=";
1854: case Token.LE:
1855: return ">";
1856: case Token.GE:
1857: return "<";
1858: }
1859: }
1860:
1861: throw new RuntimeException("Unknown relational operator: "
1862: + operator.getImage());
1863: }
1864:
1865: /**
1866: * Is called by visit(ConcatenateExpression) and generates code
1867: * that performs string concatenation in a manner similar to the way a
1868: * Java program does it. The generated code tries to do a better job
1869: * of estimating the buffer size requirements.
1870: *
1871: * <p>The expression "a" & 4 & "c" gets translated (loosly) into
1872: * new StringBuffer("a").append(4).append("c").toString().
1873: * The expression x & y, where x could be null, gets translated into
1874: * new StringBuffer().append(x).append(y).toString().
1875: */
1876: private void generateAppend(Expression expr, int estimate) {
1877: if (!(expr instanceof ConcatenateExpression)) {
1878: generate(expr);
1879: return;
1880: }
1881:
1882: ConcatenateExpression concat = (ConcatenateExpression) expr;
1883: Expression left = concat.getLeftExpression();
1884: Expression right = concat.getRightExpression();
1885:
1886: Type leftType = left.getType();
1887: Type rightType = right.getType();
1888:
1889: if (left instanceof ConcatenateExpression) {
1890: generateAppend(left, estimate);
1891: } else {
1892: // Construct the StringBuffer, but be smart with respect to
1893: // its initial capacity.
1894: mBuilder.newObject(cStringBufferDesc);
1895: mBuilder.dup();
1896:
1897: if (left.isValueKnown()) {
1898: // If the value of left is known, don't adjust estimate
1899: // at runtime. Construct the StringBuffer immediately.
1900: mBuilder.loadConstant(estimate);
1901: mBuilder.invokeConstructor(
1902: "java.lang.StringBuffer", cIntParam);
1903:
1904: if (left instanceof StringLiteral) {
1905: String val = (String) ((StringLiteral) left)
1906: .getValue();
1907: if (val.length() == 1) {
1908: mBuilder.loadConstant(val.charAt(0));
1909: leftType = new Type(char.class);
1910: } else {
1911: generate(left);
1912: }
1913: } else {
1914: generate(left);
1915: }
1916: } else {
1917: // Increase the size of the estimate at runtime, based on
1918: // the length of the left result.
1919:
1920: // Subtract the length estimate of the unknown left result.
1921: if ((estimate -= LENGTH_ESTIMATE) < 0) {
1922: estimate += LENGTH_ESTIMATE;
1923: }
1924:
1925: mBuilder.loadConstant(estimate);
1926:
1927: LocalVariable leftResult = mBuilder
1928: .createLocalVariable("left", cStringDesc);
1929: generate(left);
1930: mBuilder.storeLocal(leftResult);
1931:
1932: Label ctor = mBuilder.createLabel();
1933:
1934: if (leftType.isNullable()) {
1935: // If left could be null, test for it. If it is null,
1936: // don't adjust estimate.
1937: mBuilder.loadLocal(leftResult);
1938: mBuilder.ifNullBranch(ctor, true);
1939: }
1940:
1941: mBuilder.loadLocal(leftResult);
1942: mBuilder.invokeVirtual("java.lang.String",
1943: "length", cIntDesc, null);
1944: mBuilder.math(Opcode.IADD);
1945:
1946: ctor.setLocation();
1947: mBuilder.invokeConstructor(
1948: "java.lang.StringBuffer", cIntParam);
1949:
1950: mBuilder.loadLocal(leftResult);
1951: }
1952:
1953: // Constructed StringBuffer and first append argument is on
1954: // the stack, so perform first append operation.
1955: append(leftType);
1956: }
1957:
1958: if (right instanceof StringLiteral) {
1959: String val = (String) ((StringLiteral) right)
1960: .getValue();
1961: if (val.length() == 1) {
1962: mBuilder.loadConstant(val.charAt(0));
1963: rightType = new Type(char.class);
1964: } else {
1965: generate(right);
1966: }
1967: } else {
1968: generate(right);
1969: }
1970:
1971: setLineNumber(concat.getOperator().getSourceInfo());
1972:
1973: append(rightType);
1974: }
1975:
1976: private void append(Type type) {
1977: Class clazz = type.getNaturalClass();
1978:
1979: TypeDescriptor[] param;
1980: if (type.isPrimitive()) {
1981: if (clazz == byte.class || clazz == short.class) {
1982: clazz = int.class;
1983: }
1984: param = new TypeDescriptor[] { makeDesc(clazz) };
1985: } else if (clazz == String.class) {
1986: param = cStringParam;
1987: } else {
1988: param = cObjectParam;
1989: }
1990:
1991: mBuilder.invokeVirtual("java.lang.StringBuffer", "append",
1992: cStringBufferDesc, param);
1993: }
1994:
1995: private int estimateBufferRequirement(
1996: ConcatenateExpression concat) {
1997: return estimateBufferRequirement(concat.getLeftExpression())
1998: + estimateBufferRequirement(concat
1999: .getRightExpression());
2000: }
2001:
2002: private int estimateBufferRequirement(Expression expr) {
2003: Object value;
2004:
2005: if (expr.isValueKnown()
2006: && (value = expr.getValue()) instanceof String) {
2007:
2008: return ((String) value).length();
2009: } else if (expr instanceof ConcatenateExpression) {
2010: return estimateBufferRequirement((ConcatenateExpression) expr);
2011: } else {
2012: return LENGTH_ESTIMATE;
2013: }
2014: }
2015:
2016: // Type conversion only applies to expressions.
2017:
2018: private void typeConvertBegin(Expression.Conversion conversion) {
2019: Type from = conversion.getFromType();
2020: if (from == null) {
2021: return;
2022: }
2023: Type to = conversion.getToType();
2024: typeConvertBegin(from, to, conversion.isCastPreferred());
2025: }
2026:
2027: private void typeConvertBegin(final Type from, final Type to,
2028: boolean castPreferred) {
2029: Class fromNat = from.getNaturalClass();
2030: Class toNat = to.getNaturalClass();
2031:
2032: if (fromNat.isArray() && toNat.isArray()) {
2033: // Nothing to do at beginning of conversion.
2034: return;
2035: }
2036:
2037: if (from.isPrimitive()) {
2038: if (to.isPrimitive()) {
2039: // Nothing to do at beginning of conversion.
2040: return;
2041: } else {
2042: // Assume using object peer for primitive type.
2043: Class fromObj = from.getObjectClass();
2044: Class toObj = to.getObjectClass();
2045:
2046: if (fromObj == toObj) {
2047: if (fromObj != Boolean.class) {
2048: mBuilder.newObject(makeDesc(fromObj));
2049: mBuilder.dup();
2050: }
2051: return;
2052: }
2053: }
2054: } else {
2055: if (to.isPrimitive()) {
2056: // Assume using primive peer for object.
2057: if (from.hasPrimitivePeer()
2058: || (Number.class.isAssignableFrom(to
2059: .getObjectClass()) && Number.class
2060: .isAssignableFrom(from
2061: .getObjectClass()))) {
2062: // Nothing to do at beginning.
2063: return;
2064: }
2065: } else {
2066: if (Number.class.isAssignableFrom(fromNat)
2067: && Number.class.isAssignableFrom(toNat)) {
2068:
2069: // Nothing to do at beginning.
2070: return;
2071: }
2072: }
2073: }
2074:
2075: boolean convertStringToNonNull = from.isNullable()
2076: && to.isNonNull()
2077: && String.class.isAssignableFrom(fromNat)
2078: && String.class.isAssignableFrom(toNat);
2079:
2080: if (!convertStringToNonNull
2081: && toNat.isAssignableFrom(fromNat)) {
2082: // Do nothing at all for upcast.
2083: return;
2084: }
2085:
2086: boolean canCast = fromNat.isAssignableFrom(toNat);
2087: boolean canConvertToString = toNat
2088: .isAssignableFrom(String.class);
2089:
2090: if (canConvertToString && (!canCast || !castPreferred)) {
2091: // String conversion.
2092: if (String.class.isAssignableFrom(fromNat)) {
2093: // Converting from a String to a non-null String.
2094: // Do nothing at the beginning.
2095: } else if (to.isNullable()
2096: && (from.isNullable() || fromNat
2097: .isAssignableFrom(String.class))) {
2098: // Converting to a nullable String from a nullable object
2099: // or an object that might already be a string, so perform
2100: // special logic. Do nothing at the beginning.
2101: } else {
2102: Method converter = stringConversionMethod(from);
2103: if (!Modifier.isStatic(converter.getModifiers())) {
2104: // Push instance of Context onto stack.
2105: generateContext();
2106: }
2107: }
2108: return;
2109: } else if (canCast) {
2110: // Nothing to do at beginning for downcast.
2111: return;
2112: }
2113:
2114: typeError(from, to);
2115: }
2116:
2117: private void typeConvertEnd(Expression.Conversion conversion) {
2118: Type from = conversion.getFromType();
2119: if (from == null) {
2120: return;
2121: }
2122: Type to = conversion.getToType();
2123: typeConvertEnd(from, to, conversion.isCastPreferred());
2124: }
2125:
2126: private void typeConvertEnd(final Type from, final Type to,
2127: boolean castPreferred) {
2128: Class fromNat = from.getNaturalClass();
2129: Class toNat = to.getNaturalClass();
2130:
2131: if (fromNat.isArray() && toNat.isArray()) {
2132: if (fromNat != toNat) {
2133: convertArray(from, to);
2134: }
2135: return;
2136: }
2137:
2138: if (from.isPrimitive()) {
2139: if (to.isPrimitive()) {
2140: mBuilder.convert(fromNat, toNat);
2141: return;
2142: } else {
2143: // Assume using object peer for primitive type.
2144: Class fromObj = from.getObjectClass();
2145: Class toObj = to.getObjectClass();
2146:
2147: if (fromObj == toObj) {
2148: if (fromObj != Boolean.class) {
2149: TypeDescriptor[] param = new TypeDescriptor[1];
2150: param[0] = makeDesc(from.getNaturalClass());
2151: mBuilder.invokeConstructor(toObj.getName(),
2152: param);
2153: return;
2154: } else {
2155: TypeDescriptor td = makeDesc(Boolean.class);
2156:
2157: Label falseLabel = mBuilder.createLabel();
2158: Label endLabel = mBuilder.createLabel();
2159: mBuilder.ifZeroComparisonBranch(falseLabel,
2160: "==");
2161: mBuilder.loadStaticField(
2162: "java.lang.Boolean", "TRUE", td);
2163: mBuilder.branch(endLabel);
2164: falseLabel.setLocation();
2165: mBuilder.loadStaticField(
2166: "java.lang.Boolean", "FALSE", td);
2167: endLabel.setLocation();
2168: return;
2169: }
2170: }
2171: }
2172: } else {
2173: if (to.isPrimitive()) {
2174: // Assume using primive peer for object.
2175: if (Number.class.isAssignableFrom(from
2176: .getObjectClass())
2177: && Number.class.isAssignableFrom(to
2178: .getObjectClass())) {
2179:
2180: String methodName = null;
2181: if (toNat == int.class) {
2182: methodName = "intValue";
2183: } else if (toNat == float.class) {
2184: methodName = "floatValue";
2185: } else if (toNat == long.class) {
2186: methodName = "longValue";
2187: } else if (toNat == double.class) {
2188: methodName = "doubleValue";
2189: }
2190:
2191: if (methodName != null) {
2192: mBuilder.invokeVirtual("java.lang.Number",
2193: methodName, makeDesc(toNat), null);
2194: return;
2195: }
2196: } else if (from.getObjectClass() == Boolean.class
2197: && toNat == boolean.class) {
2198:
2199: mBuilder.invokeVirtual("java.lang.Boolean",
2200: "booleanValue", makeDesc(toNat), null);
2201: return;
2202: } else if (from.getObjectClass() == Character.class
2203: && toNat == char.class) {
2204:
2205: mBuilder.invokeVirtual("java.lang.Character",
2206: "charValue", makeDesc(toNat), null);
2207: return;
2208: }
2209: } else {
2210: if (Number.class.isAssignableFrom(fromNat)
2211: && Number.class.isAssignableFrom(toNat)) {
2212:
2213: if (fromNat == toNat) {
2214: return;
2215: }
2216:
2217: // TODO: if I can detect if a local variable is
2218: // currently on the top of the stack, then I can use
2219: // that instead of a temp variable. Variable and
2220: // peephole optimization in the code builder should
2221: // help this, but I don't have that yet.
2222:
2223: LocalVariable temp = getTempObjectVariable();
2224: mBuilder.storeLocal(temp);
2225: mBuilder.loadLocal(temp);
2226:
2227: Label nonNullLabel = mBuilder.createLabel();
2228: Label endLabel = mBuilder.createLabel();
2229:
2230: mBuilder.ifNullBranch(nonNullLabel, false);
2231:
2232: mBuilder.loadConstant(null);
2233: mBuilder.branch(endLabel);
2234:
2235: nonNullLabel.setLocation();
2236: mBuilder.newObject(makeDesc(toNat));
2237: mBuilder.dup();
2238: mBuilder.loadLocal(temp);
2239: Type fromPrim = from.toPrimitive();
2240: Type toPrim = to.toPrimitive();
2241: typeConvertEnd(from, fromPrim, false);
2242: typeConvertEnd(fromPrim, toPrim, false);
2243: typeConvertEnd(toPrim, to, false);
2244:
2245: endLabel.setLocation();
2246: return;
2247: }
2248: }
2249: }
2250:
2251: boolean convertStringToNonNull = from.isNullable()
2252: && to.isNonNull()
2253: && String.class.isAssignableFrom(fromNat)
2254: && String.class.isAssignableFrom(toNat);
2255:
2256: if (!convertStringToNonNull
2257: && toNat.isAssignableFrom(fromNat)) {
2258: // Do nothing at all for upcast.
2259: return;
2260: }
2261:
2262: boolean canCast = fromNat.isAssignableFrom(toNat);
2263: boolean canConvertToString = toNat
2264: .isAssignableFrom(String.class);
2265:
2266: if (canConvertToString && (!canCast || !castPreferred)) {
2267: // String conversion.
2268: Method converter = stringConversionMethod(from);
2269:
2270: if (String.class.isAssignableFrom(fromNat)) {
2271: // Converting from a String to a non-null String.
2272: // Test against null before calling converter.
2273:
2274: // TODO: if I can detect if a local variable is currently
2275: // on the top of the stack, then I can use that instead of
2276: // doing dup operations.
2277:
2278: mBuilder.dup();
2279: Label nonNullLabel = mBuilder.createLabel();
2280: mBuilder.ifNullBranch(nonNullLabel, false);
2281:
2282: if (!Modifier.isStatic(converter.getModifiers())) {
2283: // Push instance of Context onto stack and
2284: // swap it into the correct place.
2285: generateContext();
2286: mBuilder.swap();
2287: }
2288: mBuilder.invoke(converter);
2289:
2290: nonNullLabel.setLocation();
2291: } else if (to.isNullable()
2292: && (from.isNullable() || fromNat
2293: .isAssignableFrom(String.class))) {
2294: // Converting to a nullable String from a nullable object
2295: // or an object that might already be a string, so perform
2296: // special logic.
2297:
2298: Label castLabel = mBuilder.createLabel();
2299:
2300: // TODO: if I can detect if a local variable is currently
2301: // on the top of the stack, then I can use that instead of
2302: // doing dup operations.
2303:
2304: if (from.isNullable()) {
2305: mBuilder.dup();
2306: mBuilder.ifNullBranch(castLabel, true);
2307: }
2308:
2309: if (fromNat.isAssignableFrom(String.class)) {
2310: mBuilder.dup();
2311: mBuilder.instanceOf(cStringDesc);
2312: mBuilder
2313: .ifZeroComparisonBranch(castLabel, "!=");
2314: }
2315:
2316: if (!Modifier.isStatic(converter.getModifiers())) {
2317: // Push instance of Context onto stack and
2318: // swap it into the correct place.
2319: generateContext();
2320: mBuilder.swap();
2321: }
2322: mBuilder.invoke(converter);
2323: Label continueLabel = mBuilder.createLabel();
2324: mBuilder.branch(continueLabel);
2325:
2326: castLabel.setLocation();
2327: mBuilder.checkCast(cStringDesc);
2328:
2329: continueLabel.setLocation();
2330: } else {
2331: mBuilder.invoke(converter);
2332: }
2333: return;
2334: } else if (canCast) {
2335: if (from != Type.NULL_TYPE) {
2336: mBuilder.checkCast(makeDesc(toNat));
2337: }
2338: return;
2339: }
2340:
2341: typeError(from, to);
2342: }
2343:
2344: private Method stringConversionMethod(Type from) {
2345: Compiler c = mUnit.getCompiler();
2346:
2347: Method[] methods = c.getStringConverterMethods();
2348: Type[] param = new Type[] { from };
2349:
2350: int cnt = MethodMatcher.match(methods, null, param);
2351:
2352: if (cnt >= 1) {
2353: return methods[0];
2354: } else {
2355: throw new RuntimeException("Couldn't convert " + from
2356: + " to String");
2357: }
2358: }
2359:
2360: private void typeError(Type from, Type to) {
2361: throw new RuntimeException("Can't convert " + from + " to "
2362: + to);
2363: }
2364:
2365: private void convertArray(Type from, Type to) {
2366: // Generate code to do a runtime conversion of an array.
2367: // This involves creating a new array and populating it with
2368: // converted elements from the original array.
2369: //
2370: // <from type>[] originalArray = stackVar;
2371: // int length = originalArray.length;
2372: // push (new <to type>[length]);
2373: // for (length--; length >= 0; length--) {
2374: // dup; // dup the new array
2375: // load local (length);
2376: // store to array (convert (originalArray[length]));
2377: // }
2378:
2379: Type fromElement;
2380: Type toElement;
2381:
2382: try {
2383: fromElement = from.getArrayElementType();
2384: toElement = to.getArrayElementType();
2385: } catch (IntrospectionException e) {
2386: throw new RuntimeException(e.toString());
2387: }
2388:
2389: TypeDescriptor originalType = makeDesc(from
2390: .getNaturalClass());
2391:
2392: LocalVariable originalArray = mBuilder.createLocalVariable(
2393: "originalArray", originalType);
2394:
2395: LocalVariable length = mBuilder.createLocalVariable(
2396: "length", cIntDesc);
2397:
2398: mBuilder.storeLocal(originalArray);
2399:
2400: Label endLabel;
2401: if (from.isNonNull()) {
2402: endLabel = null;
2403: } else {
2404: // If source array could be null, test it first.
2405: mBuilder.loadLocal(originalArray);
2406: Label startLabel = mBuilder.createLabel();
2407: mBuilder.ifNullBranch(startLabel, false);
2408: mBuilder.loadConstant(null);
2409: endLabel = mBuilder.createLabel();
2410: mBuilder.branch(endLabel);
2411: startLabel.setLocation();
2412: }
2413:
2414: mBuilder.loadLocal(originalArray);
2415: mBuilder.arrayLength();
2416: mBuilder.storeLocal(length);
2417: mBuilder.loadLocal(length);
2418: mBuilder.newObject(makeDesc(to.getNaturalClass()));
2419: Label testLabel = mBuilder.createLabel();
2420: mBuilder.branch(testLabel);
2421: Label loopLabel = mBuilder.createLabel().setLocation();
2422: mBuilder.dup();
2423: mBuilder.loadLocal(length);
2424: typeConvertBegin(fromElement, toElement, false);
2425: mBuilder.loadLocal(originalArray);
2426: mBuilder.loadLocal(length);
2427: mBuilder.loadFromArray(fromElement.getNaturalClass());
2428: typeConvertEnd(fromElement, toElement, false);
2429: mBuilder.storeToArray(toElement.getNaturalClass());
2430: testLabel.setLocation();
2431: mBuilder.integerIncrement(length, -1);
2432: mBuilder.loadLocal(length);
2433: mBuilder.ifZeroComparisonBranch(loopLabel, ">=");
2434:
2435: if (endLabel != null) {
2436: endLabel.setLocation();
2437: }
2438: }
2439:
2440: private void generateForeachArray(ForeachStatement node) {
2441: Expression range = node.getRange();
2442: Statement init = node.getInitializer();
2443: Statement body = node.getBody();
2444:
2445: // Holds the loop index value.
2446: final LocalVariable indexLocal = mBuilder
2447: .createLocalVariable(null, cIntDesc);
2448:
2449: TypeDescriptor rangeDesc = makeDesc(range.getType()
2450: .getObjectClass());
2451: // Holds the array to extract from.
2452: final LocalVariable rangeLocal = mBuilder
2453: .createLocalVariable(null, rangeDesc);
2454: generate(range);
2455: mBuilder.storeLocal(rangeLocal);
2456:
2457: // Generate init right after the range is evaluated.
2458: if (init != null) {
2459: generate(init);
2460: }
2461:
2462: Label endLabel = mBuilder.createLabel();
2463:
2464: if (range.getType().isNullable()) {
2465: // If range is null, just skip past the loop, avoiding a
2466: // NullPointerException.
2467: mBuilder.loadLocal(rangeLocal);
2468: mBuilder.ifNullBranch(endLabel, true);
2469: }
2470:
2471: mBuilder.loadLocal(rangeLocal);
2472:
2473: // Put the end index value onto the stack.
2474: mBuilder.arrayLength();
2475:
2476: // Holds the value to compare against for when the index has
2477: // reached the end and looping should stop.
2478: LocalVariable endIndexLocal = null;
2479:
2480: if (!node.isReverse()) {
2481: endIndexLocal = mBuilder.createLocalVariable(null,
2482: cIntDesc);
2483: mBuilder.storeLocal(endIndexLocal);
2484: mBuilder.loadConstant(0);
2485: mBuilder.storeLocal(indexLocal);
2486: } else {
2487: // endIndexLocal is not needed because its value is zero.
2488: mBuilder.storeLocal(indexLocal);
2489: }
2490:
2491: Label checkLabel = mBuilder.createLabel();
2492: mBuilder.branch(checkLabel);
2493:
2494: // Loop body begins here.
2495:
2496: Label startLabel = mBuilder.createLabel().setLocation();
2497:
2498: // Feed the loop variable with a value.
2499: final VariableRef loopVarRef = node.getLoopVariable();
2500:
2501: storeToVariable(loopVarRef.getVariable(), new Runnable() {
2502: public void run() {
2503: mBuilder.loadLocal(rangeLocal);
2504: mBuilder.loadLocal(indexLocal);
2505: mBuilder.loadFromArray(loopVarRef.getType()
2506: .getNaturalClass());
2507: }
2508: });
2509:
2510: if (body != null) {
2511: generate(body);
2512: }
2513:
2514: if (!node.isReverse()) {
2515: // Build check label location and index adjustment
2516: mBuilder.integerIncrement(indexLocal, 1);
2517: checkLabel.setLocation();
2518:
2519: mBuilder.loadLocal(indexLocal);
2520:
2521: mBuilder.loadLocal(endIndexLocal);
2522: mBuilder.ifComparisonBranch(startLabel, "<");
2523: } else {
2524: // Build check label location and index adjustment
2525: checkLabel.setLocation();
2526: mBuilder.integerIncrement(indexLocal, -1);
2527:
2528: mBuilder.loadLocal(indexLocal);
2529:
2530: mBuilder.ifZeroComparisonBranch(startLabel, ">=");
2531: }
2532:
2533: endLabel.setLocation();
2534: }
2535:
2536: private void generateForeachIterator(final ForeachStatement node) {
2537: Expression range = node.getRange();
2538: Statement init = node.getInitializer();
2539: Statement body = node.getBody();
2540:
2541: // Get the iterator from the collection and store in the iterator
2542: // local variable.
2543:
2544: generate(range);
2545:
2546: // Generate init right after the range is evaluated.
2547: if (init != null) {
2548: generate(init);
2549: }
2550:
2551: Label endLabel = mBuilder.createLabel();
2552: Label notNullLabel = mBuilder.createLabel();
2553:
2554: if (range.getType().isNullable()) {
2555: // If range is null, just skip past the loop, avoiding a
2556: // NullPointerException.
2557: mBuilder.dup();
2558: mBuilder.ifNullBranch(notNullLabel, false);
2559: mBuilder.pop();
2560: mBuilder.branch(endLabel);
2561: }
2562:
2563: notNullLabel.setLocation();
2564:
2565: TypeDescriptor td;
2566: if (!node.isReverse()) {
2567: td = makeDesc(Iterator.class);
2568: mBuilder.invokeInterface("java.util.Collection",
2569: "iterator", td, null);
2570: } else {
2571: mBuilder.dup();
2572: mBuilder.invokeInterface("java.util.Collection",
2573: "size", cIntDesc, null);
2574: td = makeDesc(ListIterator.class);
2575: mBuilder.invokeInterface("java.util.List",
2576: "listIterator", td,
2577: new TypeDescriptor[] { cIntDesc });
2578: }
2579:
2580: final LocalVariable iteratorLocal = mBuilder
2581: .createLocalVariable(null, td);
2582: mBuilder.storeLocal(iteratorLocal);
2583:
2584: Label checkLabel = mBuilder.createLabel();
2585: mBuilder.branch(checkLabel);
2586:
2587: // Loop body begins here.
2588: Label startLabel = mBuilder.createLabel().setLocation();
2589:
2590: // Feed the loop variable with a value.
2591: VariableRef loopVarRef = node.getLoopVariable();
2592: final Class loopVarClass = loopVarRef.getType()
2593: .getNaturalClass();
2594:
2595: storeToVariable(loopVarRef.getVariable(), new Runnable() {
2596: public void run() {
2597: mBuilder.loadLocal(iteratorLocal);
2598: if (!node.isReverse()) {
2599: mBuilder.invokeInterface("java.util.Iterator",
2600: "next", cObjectDesc, null);
2601: } else {
2602: mBuilder.invokeInterface(
2603: "java.util.ListIterator", "previous",
2604: cObjectDesc, null);
2605: }
2606: if (loopVarClass != Object.class) {
2607: mBuilder.checkCast(makeDesc(loopVarClass));
2608: }
2609: }
2610: });
2611:
2612: if (body != null) {
2613: generate(body);
2614: }
2615:
2616: checkLabel.setLocation();
2617: mBuilder.loadLocal(iteratorLocal);
2618:
2619: td = makeDesc(boolean.class);
2620:
2621: if (!node.isReverse()) {
2622: mBuilder.invokeInterface("java.util.Iterator",
2623: "hasNext", td, null);
2624: } else {
2625: mBuilder.invokeInterface("java.util.ListIterator",
2626: "hasPrevious", td, null);
2627: }
2628:
2629: mBuilder.ifZeroComparisonBranch(startLabel, "!=");
2630:
2631: endLabel.setLocation();
2632: }
2633:
2634: private void generateForeachRange(ForeachStatement node) {
2635: Expression range = node.getRange();
2636: Expression endRange = node.getEndRange();
2637: Statement init = node.getInitializer();
2638: Statement body = node.getBody();
2639:
2640: // Holds the value to compare against for when the index has
2641: // reached the end and looping should stop. Isn't used if the
2642: // end index has a known value.
2643: LocalVariable endIndexLocal = null;
2644: // Only valid if endIndexLocal is null.
2645: long endIndexValue = 0;
2646:
2647: // Initialize the index and end index local variables.
2648:
2649: final Expression indexExpr;
2650: Expression endIndexExpr;
2651:
2652: if (!node.isReverse()) {
2653: indexExpr = range;
2654: endIndexExpr = endRange;
2655: } else {
2656: indexExpr = endRange;
2657: endIndexExpr = range;
2658: }
2659:
2660: // Feed the loop variable with a value.
2661: VariableRef loopVarRef = node.getLoopVariable();
2662: Variable loopVar = loopVarRef.getVariable();
2663: storeToVariable(loopVar, new Runnable() {
2664: public void run() {
2665: generate(indexExpr);
2666: }
2667: });
2668:
2669: boolean longRange = Type.LONG_TYPE
2670: .equals(loopVar.getType());
2671:
2672: if (endIndexExpr.isValueKnown()) {
2673: // End index is known, so don't use local variable to hold it.
2674: endIndexValue = ((Number) endIndexExpr.getValue())
2675: .longValue();
2676: } else {
2677: generate(endIndexExpr);
2678: if (longRange) {
2679: endIndexLocal = mBuilder.createLocalVariable(null,
2680: makeDesc(long.class));
2681: } else {
2682: endIndexLocal = mBuilder.createLocalVariable(null,
2683: cIntDesc);
2684: }
2685: mBuilder.storeLocal(endIndexLocal);
2686: }
2687:
2688: // Generate init right before the loop entry point.
2689: if (init != null) {
2690: generate(init);
2691: }
2692:
2693: Label checkLabel = mBuilder.createLabel();
2694: mBuilder.branch(checkLabel);
2695:
2696: // Loop body begins here.
2697:
2698: Label startLabel = mBuilder.createLabel().setLocation();
2699:
2700: if (body != null) {
2701: generate(body);
2702: }
2703:
2704: // Build index adjustment and determine choice comparison.
2705: String choice;
2706: if (!node.isReverse()) {
2707: incrementIntVariable(loopVar, 1);
2708: choice = "<=";
2709: } else {
2710: incrementIntVariable(loopVar, -1);
2711: choice = ">=";
2712: }
2713:
2714: // Build check.
2715: checkLabel.setLocation();
2716: loadFromVariable(loopVar);
2717:
2718: if (endIndexLocal != null) {
2719: mBuilder.loadLocal(endIndexLocal);
2720: if (longRange) {
2721: mBuilder.math(Opcode.LCMP);
2722: mBuilder.ifZeroComparisonBranch(startLabel, choice);
2723: } else {
2724: mBuilder.ifComparisonBranch(startLabel, choice);
2725: }
2726: } else if (longRange) {
2727: mBuilder.loadConstant((long) endIndexValue);
2728: mBuilder.math(Opcode.LCMP);
2729: mBuilder.ifZeroComparisonBranch(startLabel, choice);
2730: } else if (endIndexValue != 0) {
2731: mBuilder.loadConstant((int) endIndexValue);
2732: mBuilder.ifComparisonBranch(startLabel, choice);
2733: } else {
2734: mBuilder.ifZeroComparisonBranch(startLabel, choice);
2735: }
2736: }
2737:
2738: private void generateCallExpression(CallExpression node) {
2739: Expression[] exprs = node.getParams().getExpressions();
2740: Statement init = node.getInitializer();
2741: Statement subParam = node.getSubstitutionParam();
2742:
2743: int blockNum;
2744: if (subParam != null) {
2745: blockNum = mCaseNodes.size() - 1;
2746: mBuilder.loadThis();
2747: mBuilder.loadConstant(blockNum + 1);
2748: mBuilder.storeField(mBlockId.getName(), cIntDesc);
2749: mCaseNodes.add(subParam);
2750: } else {
2751: blockNum = 0;
2752: }
2753:
2754: if (node instanceof FunctionCallExpression) {
2755: Method call = ((FunctionCallExpression) node)
2756: .getCalledMethod();
2757:
2758: if (!Modifier.isStatic(call.getModifiers())) {
2759: // Push instance of Context onto stack.
2760: generateContext();
2761: }
2762:
2763: for (int i = 0; i < exprs.length; i++) {
2764: generate(exprs[i]);
2765: }
2766:
2767: if (subParam != null) {
2768: // Put this onto the stack as a substitution parameter.
2769: mBuilder.loadThis();
2770: }
2771:
2772: // Generate init right before the call.
2773: if (init != null) {
2774: generate(init);
2775: }
2776:
2777: mBuilder.invoke(call);
2778: } else if (node instanceof TemplateCallExpression) {
2779: CompilationUnit unit = ((TemplateCallExpression) node)
2780: .getCalledTemplate();
2781:
2782: // Push instance of Context onto stack as first parameter.
2783: generateContext();
2784:
2785: for (int i = 0; i < exprs.length; i++) {
2786: generate(exprs[i]);
2787: }
2788:
2789: // Generate init right before the call.
2790: if (init != null) {
2791: generate(init);
2792: }
2793:
2794: String className = unit.getTargetPackage();
2795: if (className == null) {
2796: className = unit.getName();
2797: } else {
2798: className = className + '.' + unit.getName();
2799: }
2800:
2801: Template tree = unit.getParseTree();
2802:
2803: Variable[] formals = tree.getParams();
2804: int length = formals.length;
2805:
2806: TypeDescriptor[] params;
2807: if (subParam == null) {
2808: params = new TypeDescriptor[length + 1];
2809: } else {
2810: params = new TypeDescriptor[length + 2];
2811: params[params.length - 1] = makeDesc(Substitution.class);
2812: // Put this onto the stack as a substitution parameter.
2813: mBuilder.loadThis();
2814: }
2815:
2816: Compiler c = mUnit.getCompiler();
2817: params[0] = makeDesc(c.getRuntimeContext());
2818:
2819: for (int i = 0; i < length; i++) {
2820: Type type = formals[i].getType();
2821: params[i + 1] = makeDesc(type.getNaturalClass());
2822: }
2823:
2824: TypeDescriptor returnTypeDescriptor;
2825: if (tree.getReturnType() == null) {
2826: returnTypeDescriptor = null;
2827: } else {
2828: returnTypeDescriptor = makeDesc(tree
2829: .getReturnType().getNaturalClass());
2830: }
2831:
2832: mBuilder.invokeStatic(className, EXECUTE_METHOD_NAME,
2833: returnTypeDescriptor, params);
2834: }
2835:
2836: if (subParam != null) {
2837: mBuilder.loadThis();
2838: mBuilder.loadConstant(blockNum);
2839: mBuilder.storeField(mBlockId.getName(), cIntDesc);
2840: }
2841: }
2842:
2843: /*
2844: * The code generated by the logical expressions manipulate labels
2845: * and branches. To generate a value, they all branch to instructions
2846: * that push a boolean literal onto the stack.
2847: */
2848: private void generateLogical(Expression expr) {
2849: Label trueLabel = mBuilder.createLabel();
2850: Label endLabel = mBuilder.createLabel();
2851:
2852: generateBranch(expr, trueLabel, true);
2853:
2854: Type type = expr.getInitialType();
2855: Class clazz = type.getNaturalClass();
2856:
2857: if (clazz == boolean.class) {
2858: mBuilder.loadConstant(false);
2859: mBuilder.branch(endLabel);
2860: trueLabel.setLocation();
2861: mBuilder.loadConstant(true);
2862: endLabel.setLocation();
2863: } else if (clazz.isAssignableFrom(Boolean.class)) {
2864: TypeDescriptor td = makeDesc(Boolean.class);
2865:
2866: mBuilder.loadStaticField("java.lang.Boolean", "FALSE",
2867: td);
2868: mBuilder.branch(endLabel);
2869: trueLabel.setLocation();
2870: mBuilder.loadStaticField("java.lang.Boolean", "TRUE",
2871: td);
2872: endLabel.setLocation();
2873: } else if (clazz.isAssignableFrom(String.class)) {
2874: mBuilder.loadConstant("false");
2875: mBuilder.branch(endLabel);
2876: trueLabel.setLocation();
2877: mBuilder.loadConstant("true");
2878: endLabel.setLocation();
2879: } else {
2880: typeError(new Type(boolean.class), type);
2881: }
2882: }
2883:
2884: private void declareVariable(Variable node) {
2885: declareVariable(node, null);
2886: }
2887:
2888: private void declareVariable(Variable var,
2889: LocalVariable localVar) {
2890: String name = var.getName();
2891:
2892: if (name == CONTEXT_PARAM_NAME) {
2893: mContextParam = new VariableRef(null, name);
2894: mContextParam.setVariable(var);
2895: } else if (name == SUB_PARAM_NAME) {
2896: mSubParam = new VariableRef(null, name);
2897: mSubParam.setVariable(var);
2898: }
2899:
2900: if (var.isField()) {
2901: if (mFields.get(name) != var) {
2902: // Ensure field names are unique
2903: int i = 0;
2904: do {
2905: name = var.getName() + '$' + i++;
2906: } while (mFields.get(name) != null);
2907:
2908: mFields.put(name, var);
2909: var.setName(name);
2910: }
2911: mVariableMap.put(var, null);
2912: } else {
2913: if (localVar == null) {
2914: TypeDescriptor desc = makeDesc(var.getType()
2915: .getNaturalClass());
2916: localVar = mBuilder.createLocalVariable(var
2917: .getName(), desc);
2918: }
2919: mVariableMap.put(var, localVar);
2920: }
2921: }
2922:
2923: /**
2924: * Retrieves an already declared local variable. If variable is not
2925: * yet declared, it is declared. If variable is actually a field, it
2926: * is still declared, but null is returned.
2927: */
2928: private LocalVariable getLocalVariable(Variable var) {
2929: if (!mVariableMap.containsKey(var)) {
2930: declareVariable(var, null);
2931: }
2932: return (LocalVariable) mVariableMap.get(var);
2933: }
2934:
2935: private void loadFromVariable(Variable var) {
2936: if (var.isField()) {
2937: if (!mVariableMap.containsKey(var)) {
2938: declareVariable(var, null);
2939: }
2940:
2941: TypeDescriptor td = makeDesc(var.getType()
2942: .getNaturalClass());
2943:
2944: if (var.isStatic()) {
2945: mBuilder.loadStaticField(var.getName(), td);
2946: } else {
2947: mBuilder.loadThis();
2948: mBuilder.loadField(var.getName(), td);
2949: }
2950: } else {
2951: LocalVariable local = (LocalVariable) mVariableMap
2952: .get(var);
2953: if (local == null) {
2954: throw new RuntimeException(
2955: "Attempting to read from uninitialized local "
2956: + "variable: " + var);
2957: }
2958: mBuilder.loadLocal(local);
2959: }
2960: }
2961:
2962: private void storeToVariable(Variable var, Runnable callback) {
2963: if (var.isField() && !var.isStatic()) {
2964: mBuilder.loadThis();
2965: }
2966:
2967: callback.run();
2968:
2969: if (var.isField()) {
2970: if (!mVariableMap.containsKey(var)) {
2971: declareVariable(var, null);
2972: }
2973:
2974: TypeDescriptor td = makeDesc(var.getType()
2975: .getNaturalClass());
2976:
2977: if (var.isStatic()) {
2978: mBuilder.storeStaticField(var.getName(), td);
2979: } else {
2980: mBuilder.storeField(var.getName(), td);
2981: }
2982: } else {
2983: mBuilder.storeLocal(getLocalVariable(var));
2984: }
2985: }
2986:
2987: private void incrementIntVariable(final Variable var,
2988: final int amount) {
2989: if (amount == 0) {
2990: return;
2991: }
2992:
2993: final Class clazz = var.getType().getNaturalClass();
2994:
2995: LocalVariable local = getLocalVariable(var);
2996: if (local != null && clazz == int.class) {
2997: mBuilder.integerIncrement(local, amount);
2998: } else {
2999: storeToVariable(var, new Runnable() {
3000: public void run() {
3001: loadFromVariable(var);
3002:
3003: if (clazz == int.class) {
3004: if (amount >= 0) {
3005: mBuilder.loadConstant((int) amount);
3006: mBuilder.math(Opcode.IADD);
3007: } else {
3008: mBuilder.loadConstant((int) -amount);
3009: mBuilder.math(Opcode.ISUB);
3010: }
3011: } else if (clazz == long.class) {
3012: if (amount >= 0) {
3013: mBuilder.loadConstant((long) amount);
3014: mBuilder.math(Opcode.LADD);
3015: } else {
3016: mBuilder.loadConstant((long) -amount);
3017: mBuilder.math(Opcode.LSUB);
3018: }
3019: }
3020: }
3021: });
3022: }
3023: }
3024:
3025: private LocalVariable getTempObjectVariable() {
3026: if (mTempObject == null) {
3027: mTempObject = mBuilder.createLocalVariable("temp",
3028: cObjectDesc);
3029: }
3030: return mTempObject;
3031: }
3032:
3033: private void setLineNumber(SourceInfo info) {
3034: if (info != null) {
3035: int line = info.getLine();
3036: if (line != mLastLine) {
3037: mLastLine = line;
3038: mBuilder.mapLineNumber(line);
3039: }
3040: }
3041: }
3042:
3043: private TypeDescriptor makeDesc(Class clazz) {
3044: // Note: Caching TypeDescriptors may improve performance slightly.
3045: return new TypeDescriptor(clazz);
3046: }
3047: }
3048:
3049: private static class GuardHandler {
3050: final Label tryStart;
3051: final Label tryEnd;
3052: final Statement replacement;
3053:
3054: public GuardHandler(Label tryStart, Label tryEnd,
3055: Statement replacement) {
3056: this .tryStart = tryStart;
3057: this .tryEnd = tryEnd;
3058: this .replacement = replacement;
3059: }
3060: }
3061:
3062: private static class DetailException extends RuntimeException {
3063: private Exception mException;
3064:
3065: public DetailException(Exception e, String detail) {
3066: super (e.getMessage() + ' ' + detail);
3067: mException = e;
3068: }
3069:
3070: public String toString() {
3071: return mException.getClass().getName() + ": "
3072: + getMessage();
3073: }
3074:
3075: public void printStackTrace() {
3076: mException.printStackTrace();
3077: }
3078:
3079: public void printStackTrace(java.io.PrintStream ps) {
3080: mException.printStackTrace(ps);
3081: }
3082:
3083: public void printStackTrace(java.io.PrintWriter pw) {
3084: mException.printStackTrace(pw);
3085: }
3086: }
3087: }
|