Source Code Cross Referenced for JavaClassGenerator.java in  » Template-Engine » Tea » com » go » tea » compiler » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Template Engine » Tea » com.go.tea.compiler 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.