Source Code Cross Referenced for JsInliner.java in  » Ajax » GWT » com » google » gwt » dev » js » 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 » Ajax » GWT » com.google.gwt.dev.js 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright 2007 Google Inc.
0003:         * 
0004:         * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0005:         * use this file except in compliance with the License. You may obtain a copy of
0006:         * the License at
0007:         * 
0008:         * http://www.apache.org/licenses/LICENSE-2.0
0009:         * 
0010:         * Unless required by applicable law or agreed to in writing, software
0011:         * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0012:         * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0013:         * License for the specific language governing permissions and limitations under
0014:         * the License.
0015:         */
0016:        package com.google.gwt.dev.js;
0017:
0018:        import com.google.gwt.dev.jjs.InternalCompilerException;
0019:        import com.google.gwt.dev.js.ast.JsArrayAccess;
0020:        import com.google.gwt.dev.js.ast.JsArrayLiteral;
0021:        import com.google.gwt.dev.js.ast.JsBinaryOperation;
0022:        import com.google.gwt.dev.js.ast.JsBinaryOperator;
0023:        import com.google.gwt.dev.js.ast.JsBlock;
0024:        import com.google.gwt.dev.js.ast.JsBooleanLiteral;
0025:        import com.google.gwt.dev.js.ast.JsCase;
0026:        import com.google.gwt.dev.js.ast.JsConditional;
0027:        import com.google.gwt.dev.js.ast.JsContext;
0028:        import com.google.gwt.dev.js.ast.JsDecimalLiteral;
0029:        import com.google.gwt.dev.js.ast.JsDefault;
0030:        import com.google.gwt.dev.js.ast.JsExprStmt;
0031:        import com.google.gwt.dev.js.ast.JsExpression;
0032:        import com.google.gwt.dev.js.ast.JsFor;
0033:        import com.google.gwt.dev.js.ast.JsForIn;
0034:        import com.google.gwt.dev.js.ast.JsFunction;
0035:        import com.google.gwt.dev.js.ast.JsIf;
0036:        import com.google.gwt.dev.js.ast.JsIntegralLiteral;
0037:        import com.google.gwt.dev.js.ast.JsInvocation;
0038:        import com.google.gwt.dev.js.ast.JsModVisitor;
0039:        import com.google.gwt.dev.js.ast.JsName;
0040:        import com.google.gwt.dev.js.ast.JsNameRef;
0041:        import com.google.gwt.dev.js.ast.JsNew;
0042:        import com.google.gwt.dev.js.ast.JsNode;
0043:        import com.google.gwt.dev.js.ast.JsNullLiteral;
0044:        import com.google.gwt.dev.js.ast.JsObjectLiteral;
0045:        import com.google.gwt.dev.js.ast.JsParameter;
0046:        import com.google.gwt.dev.js.ast.JsPostfixOperation;
0047:        import com.google.gwt.dev.js.ast.JsPrefixOperation;
0048:        import com.google.gwt.dev.js.ast.JsProgram;
0049:        import com.google.gwt.dev.js.ast.JsRegExp;
0050:        import com.google.gwt.dev.js.ast.JsReturn;
0051:        import com.google.gwt.dev.js.ast.JsScope;
0052:        import com.google.gwt.dev.js.ast.JsStatement;
0053:        import com.google.gwt.dev.js.ast.JsStringLiteral;
0054:        import com.google.gwt.dev.js.ast.JsSwitchMember;
0055:        import com.google.gwt.dev.js.ast.JsThisRef;
0056:        import com.google.gwt.dev.js.ast.JsThrow;
0057:        import com.google.gwt.dev.js.ast.JsUnaryOperation;
0058:        import com.google.gwt.dev.js.ast.JsVisitable;
0059:        import com.google.gwt.dev.js.ast.JsVisitor;
0060:        import com.google.gwt.dev.js.ast.JsWhile;
0061:
0062:        import java.util.ArrayList;
0063:        import java.util.Arrays;
0064:        import java.util.Collection;
0065:        import java.util.HashMap;
0066:        import java.util.HashSet;
0067:        import java.util.IdentityHashMap;
0068:        import java.util.List;
0069:        import java.util.ListIterator;
0070:        import java.util.Map;
0071:        import java.util.Set;
0072:        import java.util.Stack;
0073:
0074:        /**
0075:         * Perform inlining optimizations on the JavaScript AST.
0076:         */
0077:        public class JsInliner {
0078:
0079:            /**
0080:             * Make comma binary operations left-nested since commas are naturally
0081:             * left-associative. We will define the comma-normal form such that a comma
0082:             * expression should never have a comma expression as its RHS. This has a nice
0083:             * side-effect of minimizing the number of generated parentheses.
0084:             * 
0085:             * <pre>
0086:             * (X, b) is unchanged
0087:             * (X, (b, c) becomes ((X, b), c)
0088:             * (X, ((b, c), d)) becomes (((X, b), c), d)
0089:             * </pre>
0090:             */
0091:            private static class CommaNormalizer extends JsModVisitor {
0092:
0093:                /**
0094:                 * Returns an expression as a JsBinaryOperation if it is a comma expression.
0095:                 */
0096:                private static JsBinaryOperation isComma(JsExpression x) {
0097:                    if (!(x instanceof  JsBinaryOperation)) {
0098:                        return null;
0099:                    }
0100:                    JsBinaryOperation op = (JsBinaryOperation) x;
0101:
0102:                    return op.getOperator().equals(JsBinaryOperator.COMMA) ? op
0103:                            : null;
0104:                }
0105:
0106:                @Override
0107:                public void endVisit(JsBinaryOperation x,
0108:                        JsContext<JsExpression> ctx) {
0109:                    if (isComma(x) == null) {
0110:                        return;
0111:                    }
0112:
0113:                    JsBinaryOperation toUpdate = isComma(x.getArg2());
0114:                    if (toUpdate == null) {
0115:                        return;
0116:                    }
0117:
0118:                    // Find the left-most, nested comma expression
0119:                    while (isComma(toUpdate.getArg1()) != null) {
0120:                        toUpdate = (JsBinaryOperation) toUpdate.getArg1();
0121:                    }
0122:
0123:                    /*
0124:                     * Create a new comma expression with the original LHS and the LHS of the
0125:                     * nested comma expression.
0126:                     */
0127:                    JsBinaryOperation newOp = new JsBinaryOperation(
0128:                            JsBinaryOperator.COMMA);
0129:                    newOp.setArg1(x.getArg1());
0130:                    newOp.setArg2(toUpdate.getArg1());
0131:
0132:                    // Set the LHS of the nested comma expression to the new comma expression
0133:                    toUpdate.setArg1(newOp);
0134:
0135:                    // Replace the original node with its updated RHS
0136:                    ctx.replaceMe(x.getArg2());
0137:                }
0138:            }
0139:
0140:            /**
0141:             * Provides a relative metric by which the syntactic complexity of a
0142:             * JsExpression can be gauged.
0143:             */
0144:            private static class ComplexityEstimator extends JsVisitor {
0145:                /**
0146:                 * The current measure of complexity. This measures the number of
0147:                 * expressions that have been encountered by the visitor.
0148:                 */
0149:                private int complexity = 0;
0150:
0151:                @Override
0152:                public void endVisit(JsArrayAccess x,
0153:                        JsContext<JsExpression> ctx) {
0154:                    complexity++;
0155:                }
0156:
0157:                @Override
0158:                public void endVisit(JsArrayLiteral x,
0159:                        JsContext<JsExpression> ctx) {
0160:                    complexity++;
0161:                }
0162:
0163:                @Override
0164:                public void endVisit(JsBinaryOperation x,
0165:                        JsContext<JsExpression> ctx) {
0166:                    complexity++;
0167:                }
0168:
0169:                @Override
0170:                public void endVisit(JsBooleanLiteral x,
0171:                        JsContext<JsExpression> ctx) {
0172:                    complexity++;
0173:                }
0174:
0175:                @Override
0176:                public void endVisit(JsConditional x,
0177:                        JsContext<JsExpression> ctx) {
0178:                    complexity++;
0179:                }
0180:
0181:                @Override
0182:                public void endVisit(JsDecimalLiteral x,
0183:                        JsContext<JsExpression> ctx) {
0184:                    complexity++;
0185:                }
0186:
0187:                @Override
0188:                public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
0189:                    complexity++;
0190:                }
0191:
0192:                @Override
0193:                public void endVisit(JsIntegralLiteral x,
0194:                        JsContext<JsExpression> ctx) {
0195:                    complexity++;
0196:                }
0197:
0198:                @Override
0199:                public void endVisit(JsInvocation x, JsContext<JsExpression> ctx) {
0200:                    complexity++;
0201:                }
0202:
0203:                @Override
0204:                public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
0205:                    complexity++;
0206:                }
0207:
0208:                @Override
0209:                public void endVisit(JsNew x, JsContext<JsExpression> ctx) {
0210:                    complexity++;
0211:                }
0212:
0213:                @Override
0214:                public void endVisit(JsNullLiteral x,
0215:                        JsContext<JsExpression> ctx) {
0216:                    complexity++;
0217:                }
0218:
0219:                @Override
0220:                public void endVisit(JsObjectLiteral x,
0221:                        JsContext<JsExpression> ctx) {
0222:                    complexity++;
0223:                }
0224:
0225:                @Override
0226:                public void endVisit(JsPostfixOperation x,
0227:                        JsContext<JsExpression> ctx) {
0228:                    complexity++;
0229:                }
0230:
0231:                @Override
0232:                public void endVisit(JsPrefixOperation x,
0233:                        JsContext<JsExpression> ctx) {
0234:                    complexity++;
0235:                }
0236:
0237:                @Override
0238:                public void endVisit(JsRegExp x, JsContext<JsExpression> ctx) {
0239:                    complexity++;
0240:                }
0241:
0242:                @Override
0243:                public void endVisit(JsStringLiteral x,
0244:                        JsContext<JsExpression> ctx) {
0245:                    complexity++;
0246:                }
0247:
0248:                @Override
0249:                public void endVisit(JsThisRef x, JsContext<JsExpression> ctx) {
0250:                    complexity++;
0251:                }
0252:
0253:                public int getComplexity() {
0254:                    return complexity;
0255:                }
0256:            }
0257:
0258:            /**
0259:             * This is used to clean up duplication invocations of functions that should
0260:             * only be executed once, such as clinit functions. Whenever there is a
0261:             * possible branch in program flow, the remover will create a new instance of
0262:             * itself to handle the possible branches.
0263:             * 
0264:             * We don't look at combining branch choices. This will not produce the most
0265:             * efficient elimination of duplicated calls, but it handles the general case
0266:             * and is simple to verify.
0267:             */
0268:            private static class DuplicateXORemover extends JsModVisitor {
0269:                /*
0270:                 * TODO: Most of the special casing below can be removed if complex
0271:                 * statements always use blocks, rather than plain statements.
0272:                 */
0273:
0274:                /**
0275:                 * Retains the the functions that we know have been called.
0276:                 */
0277:                private final Set<JsFunction> called;
0278:                private final JsProgram program;
0279:
0280:                public DuplicateXORemover(JsProgram program) {
0281:                    this .program = program;
0282:                    called = new HashSet<JsFunction>();
0283:                }
0284:
0285:                public DuplicateXORemover(JsProgram program,
0286:                        Set<JsFunction> alreadyCalled) {
0287:                    this .program = program;
0288:                    called = new HashSet<JsFunction>(alreadyCalled);
0289:                }
0290:
0291:                /**
0292:                 * Look for comma expressions that contain duplicate calls and handle the
0293:                 * conditional-evaluation case of logical and/or operations.
0294:                 */
0295:                @Override
0296:                public boolean visit(JsBinaryOperation x,
0297:                        JsContext<JsExpression> ctx) {
0298:                    if (x.getOperator() == JsBinaryOperator.COMMA) {
0299:
0300:                        boolean left = isDuplicateCall(x.getArg1());
0301:                        boolean right = isDuplicateCall(x.getArg2());
0302:
0303:                        if (left && right) {
0304:                            /*
0305:                             * (clinit(), clinit()) --> delete or null.
0306:                             * 
0307:                             * This construct is very unlikely since the InliningVisitor builds
0308:                             * the comma expressions in a right-nested manner.
0309:                             */
0310:                            if (ctx.canRemove()) {
0311:                                ctx.removeMe();
0312:                                return false;
0313:                            } else {
0314:                                // The return value from an XO function is never used
0315:                                ctx.replaceMe(program.getNullLiteral());
0316:                                return false;
0317:                            }
0318:
0319:                        } else if (left) {
0320:                            // (clinit(), xyz) --> xyz
0321:                            // This is the common case
0322:                            ctx.replaceMe(accept(x.getArg2()));
0323:                            return false;
0324:
0325:                        } else if (right) {
0326:                            // (xyz, clinit()) --> xyz
0327:                            // Possible if a clinit() were the last element
0328:                            ctx.replaceMe(accept(x.getArg1()));
0329:                            return false;
0330:                        }
0331:
0332:                    } else if (x.getOperator().equals(JsBinaryOperator.AND)
0333:                            || x.getOperator().equals(JsBinaryOperator.OR)) {
0334:                        x.setArg1(accept(x.getArg1()));
0335:                        // Possibility of conditional evaluation of second parameter
0336:                        x.setArg2(branch(x.getArg2()));
0337:                        return false;
0338:                    }
0339:
0340:                    return true;
0341:                }
0342:
0343:                /**
0344:                 * Most of the branching statements (as well as JsFunctions) will visit with
0345:                 * a JsBlock, so we don't need to explicitly enumerate all JsStatement
0346:                 * subtypes.
0347:                 */
0348:                @Override
0349:                public boolean visit(JsBlock x, JsContext<JsStatement> ctx) {
0350:                    branch(x.getStatements());
0351:                    return false;
0352:                }
0353:
0354:                @Override
0355:                public boolean visit(JsCase x, JsContext<JsSwitchMember> ctx) {
0356:                    x.setCaseExpr(accept(x.getCaseExpr()));
0357:                    branch(x.getStmts());
0358:                    return false;
0359:                }
0360:
0361:                @Override
0362:                public boolean visit(JsConditional x,
0363:                        JsContext<JsExpression> ctx) {
0364:                    x.setTestExpression(accept(x.getTestExpression()));
0365:                    x.setThenExpression(branch(x.getThenExpression()));
0366:                    x.setElseExpression(branch(x.getElseExpression()));
0367:                    return false;
0368:                }
0369:
0370:                @Override
0371:                public boolean visit(JsDefault x, JsContext<JsSwitchMember> ctx) {
0372:                    branch(x.getStmts());
0373:                    return false;
0374:                }
0375:
0376:                @Override
0377:                public boolean visit(JsExprStmt x, JsContext<JsStatement> ctx) {
0378:                    if (isDuplicateCall(x.getExpression())) {
0379:                        if (ctx.canRemove()) {
0380:                            ctx.removeMe();
0381:                        } else {
0382:                            ctx.replaceMe(program.getEmptyStmt());
0383:                        }
0384:                        return false;
0385:
0386:                    } else {
0387:                        return true;
0388:                    }
0389:                }
0390:
0391:                @Override
0392:                public boolean visit(JsFor x, JsContext<JsStatement> ctx) {
0393:                    // The JsFor may have an expression xor a variable declaration.
0394:                    if (x.getInitExpr() != null) {
0395:                        x.setInitExpr(accept(x.getInitExpr()));
0396:                    } else if (x.getInitVars() != null) {
0397:                        x.setInitVars(accept(x.getInitVars()));
0398:                    }
0399:
0400:                    // The condition is optional
0401:                    if (x.getCondition() != null) {
0402:                        x.setCondition(accept(x.getCondition()));
0403:                    }
0404:
0405:                    // The increment expression is optional
0406:                    if (x.getIncrExpr() != null) {
0407:                        x.setIncrExpr(branch(x.getIncrExpr()));
0408:                    }
0409:
0410:                    // The body is not guaranteed to be a JsBlock
0411:                    x.setBody(branch(x.getBody()));
0412:                    return false;
0413:                }
0414:
0415:                @Override
0416:                public boolean visit(JsForIn x, JsContext<JsStatement> ctx) {
0417:                    if (x.getIterExpr() != null) {
0418:                        x.setIterExpr(accept(x.getIterExpr()));
0419:                    }
0420:
0421:                    x.setObjExpr(accept(x.getObjExpr()));
0422:
0423:                    // The body is not guaranteed to be a JsBlock
0424:                    x.setBody(branch(x.getBody()));
0425:                    return false;
0426:                }
0427:
0428:                @Override
0429:                public boolean visit(JsIf x, JsContext<JsStatement> ctx) {
0430:                    x.setIfExpr(accept(x.getIfExpr()));
0431:
0432:                    x.setThenStmt(branch(x.getThenStmt()));
0433:                    if (x.getElseStmt() != null) {
0434:                        x.setElseStmt(branch(x.getElseStmt()));
0435:                    }
0436:
0437:                    return false;
0438:                }
0439:
0440:                /**
0441:                 * Possibly record that we've seen a call in the current context.
0442:                 */
0443:                @Override
0444:                public boolean visit(JsInvocation x, JsContext<JsExpression> ctx) {
0445:                    JsFunction func = isExecuteOnce(x);
0446:                    while (func != null) {
0447:                        called.add(func);
0448:                        func = func.getImpliedExecute();
0449:                    }
0450:                    return true;
0451:                }
0452:
0453:                @Override
0454:                public boolean visit(JsWhile x, JsContext<JsStatement> ctx) {
0455:                    x.setCondition(accept(x.getCondition()));
0456:
0457:                    // The body is not guaranteed to be a JsBlock
0458:                    x.setBody(branch(x.getBody()));
0459:                    return false;
0460:                }
0461:
0462:                private <T extends JsNode<T>> void branch(List<T> x) {
0463:                    DuplicateXORemover dup = new DuplicateXORemover(program,
0464:                            called);
0465:                    dup.acceptWithInsertRemove(x);
0466:                    didChange |= dup.didChange();
0467:                }
0468:
0469:                private <T extends JsNode<T>> T branch(T x) {
0470:                    DuplicateXORemover dup = new DuplicateXORemover(program,
0471:                            called);
0472:                    T toReturn = dup.accept(x);
0473:
0474:                    if ((toReturn != x) && !dup.didChange()) {
0475:                        throw new InternalCompilerException(
0476:                                "node replacement should imply didChange()");
0477:                    }
0478:
0479:                    didChange |= dup.didChange();
0480:                    return toReturn;
0481:                }
0482:
0483:                private boolean isDuplicateCall(JsExpression x) {
0484:                    if (!(x instanceof  JsInvocation)) {
0485:                        return false;
0486:                    }
0487:
0488:                    JsFunction func = isExecuteOnce((JsInvocation) x);
0489:                    return (func != null && called.contains(func));
0490:                }
0491:            }
0492:
0493:            /**
0494:             * Determines if a list of names is guaranteed to be evaluated in a particular
0495:             * order.
0496:             */
0497:            private static class EvaluationOrderVisitor extends JsVisitor {
0498:                private boolean maintainsOrder = true;
0499:                private final List<JsName> toEvaluate;
0500:                private final List<JsName> unevaluated;
0501:
0502:                public EvaluationOrderVisitor(List<JsName> toEvaluate) {
0503:                    this .toEvaluate = toEvaluate;
0504:                    this .unevaluated = new ArrayList<JsName>(toEvaluate);
0505:                }
0506:
0507:                @Override
0508:                public void endVisit(JsBinaryOperation x,
0509:                        JsContext<JsExpression> ctx) {
0510:                    JsBinaryOperator op = x.getOperator();
0511:
0512:                    /*
0513:                     * We don't care about the left-hand expression, because it is guaranteed
0514:                     * to be evaluated.
0515:                     */
0516:                    boolean rightStrict = refersToRequiredName(x.getArg2());
0517:                    boolean conditionalEvaluation = JsBinaryOperator.AND
0518:                            .equals(op)
0519:                            || JsBinaryOperator.OR.equals(op);
0520:
0521:                    if (rightStrict && conditionalEvaluation) {
0522:                        maintainsOrder = false;
0523:                    }
0524:                }
0525:
0526:                /**
0527:                 * If the condition would cause conditional evaluation of strict parameters,
0528:                 * don't allow inlining.
0529:                 */
0530:                @Override
0531:                public void endVisit(JsConditional x,
0532:                        JsContext<JsExpression> ctx) {
0533:                    boolean thenStrict = refersToRequiredName(x
0534:                            .getThenExpression());
0535:                    boolean elseStrict = refersToRequiredName(x
0536:                            .getElseExpression());
0537:
0538:                    if (thenStrict || elseStrict) {
0539:                        maintainsOrder = false;
0540:                    }
0541:                }
0542:
0543:                /**
0544:                 * The statement declares a function closure. This makes actual evaluation
0545:                 * order of the parameters difficult or impossible to determine, so we'll
0546:                 * just ignore them.
0547:                 */
0548:                @Override
0549:                public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
0550:                    maintainsOrder = false;
0551:                }
0552:
0553:                /**
0554:                 * The innermost invocation we see must consume all presently unevaluated
0555:                 * parameters to ensure that an exception does not prevent their evaluation.
0556:                 * 
0557:                 * In the case of a nested invocation, such as
0558:                 * <code>F(r1, r2, G(r3, r4), f1);</code> the evaluation order is
0559:                 * guaranteed to be maintained, provided that no required parameters occur
0560:                 * after the nested invocation.
0561:                 */
0562:                @Override
0563:                public void endVisit(JsInvocation x, JsContext<JsExpression> ctx) {
0564:                    /*
0565:                     * The check for isExecuteOnce() is potentially incorrect here, however
0566:                     * the original Java semantics of the clinit would have made the code
0567:                     * incorrect anyway.
0568:                     */
0569:                    if ((isExecuteOnce(x) == null) && unevaluated.size() > 0) {
0570:                        maintainsOrder = false;
0571:                    }
0572:                }
0573:
0574:                @Override
0575:                public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
0576:                    JsName name = x.getName();
0577:
0578:                    if (!toEvaluate.contains(name)) {
0579:                        return;
0580:                    }
0581:
0582:                    if (unevaluated.size() == 0
0583:                            || !unevaluated.remove(0).equals(name)) {
0584:                        maintainsOrder = false;
0585:                    }
0586:                }
0587:
0588:                public boolean maintainsOrder() {
0589:                    return maintainsOrder && unevaluated.size() == 0;
0590:                }
0591:
0592:                /**
0593:                 * Determine if an expression contains a reference to a strict parameter.
0594:                 */
0595:                private boolean refersToRequiredName(JsExpression e) {
0596:                    RefersToNameVisitor v = new RefersToNameVisitor(toEvaluate);
0597:                    v.accept(e);
0598:                    return v.refersToName();
0599:                }
0600:            }
0601:
0602:            /**
0603:             * Collect all of the idents used in an AST node. The collector can be
0604:             * configured to collect idents from qualified xor unqualified JsNameRefs.
0605:             */
0606:            private static class IdentCollector extends JsVisitor {
0607:                private final boolean collectQualified;
0608:                private final Set<String> idents = new HashSet<String>();
0609:
0610:                public IdentCollector(boolean collectQualified) {
0611:                    this .collectQualified = collectQualified;
0612:                }
0613:
0614:                @Override
0615:                public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
0616:                    boolean hasQualifier = x.getQualifier() != null;
0617:
0618:                    if ((collectQualified && !hasQualifier)
0619:                            || (!collectQualified && hasQualifier)) {
0620:                        return;
0621:                    }
0622:
0623:                    assert x.getIdent() != null;
0624:                    idents.add(x.getIdent());
0625:                }
0626:
0627:                public Set<String> getIdents() {
0628:                    return idents;
0629:                }
0630:            }
0631:
0632:            /**
0633:             * This class looks for function invocations that can be inlined and performs
0634:             * the replacement by replacing the JsInvocation with a comma expression
0635:             * consisting of the expressions evaluated by the target function. A second
0636:             * step may convert the expressions in the comma expression back to multiple
0637:             * statements if the context of the invocation would allow this.
0638:             */
0639:            private static class InliningVisitor extends JsModVisitor {
0640:                private final Set<JsFunction> blacklist = new HashSet<JsFunction>();
0641:                private final Stack<JsFunction> functionStack = new Stack<JsFunction>();
0642:                private final JsProgram program;
0643:
0644:                public InliningVisitor(JsProgram program) {
0645:                    this .program = program;
0646:                }
0647:
0648:                /**
0649:                 * Add to the list of JsFunctions that should not be inlined, regardless of
0650:                 * whether or not they would normally be inlinable.
0651:                 */
0652:                public void blacklist(Collection<JsFunction> functions) {
0653:                    blacklist.addAll(functions);
0654:                }
0655:
0656:                /**
0657:                 * This normalizes the comma expressions into multiple statements and
0658:                 * removes statements with no side-effects.
0659:                 */
0660:                @Override
0661:                public void endVisit(JsExprStmt x, JsContext<JsStatement> ctx) {
0662:                    JsExpression e = x.getExpression();
0663:
0664:                    // We will occasionally create JsExprStmts that have no side-effects.
0665:                    if (ctx.canRemove() && !hasSideEffects(x)) {
0666:                        ctx.removeMe();
0667:                        return;
0668:                    }
0669:
0670:                    List<JsExprStmt> statements = new ArrayList<JsExprStmt>();
0671:
0672:                    /*
0673:                     * Assemble the expressions back into a list of JsExprStmts. We will
0674:                     * iteratively disassemble the nested comma expressions, stopping when the
0675:                     * LHS is not a comma expression.
0676:                     */
0677:                    while (e instanceof  JsBinaryOperation) {
0678:                        JsBinaryOperation op = (JsBinaryOperation) e;
0679:
0680:                        if (!op.getOperator().equals(JsBinaryOperator.COMMA)) {
0681:                            break;
0682:                        }
0683:
0684:                        /*
0685:                         * We can ignore intermediate expressions as long as they have no
0686:                         * side-effects.
0687:                         */
0688:                        if (hasSideEffects(op.getArg2())) {
0689:                            statements.add(0, new JsExprStmt(op.getArg2()));
0690:                        }
0691:
0692:                        e = op.getArg1();
0693:                    }
0694:
0695:                    /*
0696:                     * We know the return value from the original invocation was ignored, so
0697:                     * it may be possible to ignore the final expressions as long as it has no
0698:                     * side-effects.
0699:                     */
0700:                    if (hasSideEffects(e)) {
0701:                        statements.add(0, new JsExprStmt(e));
0702:                    }
0703:
0704:                    if (statements.size() == 0) {
0705:                        // The expression contained no side effects at all.
0706:                        if (ctx.canRemove()) {
0707:                            ctx.removeMe();
0708:                        } else {
0709:                            ctx.replaceMe(program.getEmptyStmt());
0710:                        }
0711:
0712:                    } else if (x.getExpression() != statements.get(0)
0713:                            .getExpression()) {
0714:                        // Something has changed
0715:
0716:                        if (!ctx.canInsert()) {
0717:                            /*
0718:                             * This indicates that the function was attached to a clause of a
0719:                             * control function and not into an existing block. We'll replace the
0720:                             * single JsExprStmt with a JsBlock that contains all of the
0721:                             * statements.
0722:                             */
0723:                            JsBlock b = new JsBlock();
0724:                            b.getStatements().addAll(statements);
0725:                            ctx.replaceMe(b);
0726:                            return;
0727:
0728:                        } else {
0729:                            // Insert the new statements into the original context
0730:                            for (JsStatement s : statements) {
0731:                                ctx.insertBefore(s);
0732:                            }
0733:                            ctx.removeMe();
0734:                        }
0735:                    }
0736:                }
0737:
0738:                @Override
0739:                public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
0740:                    if (!functionStack.pop().equals(x)) {
0741:                        throw new InternalCompilerException(
0742:                                "Unexpected function popped");
0743:                    }
0744:                }
0745:
0746:                @Override
0747:                public void endVisit(JsInvocation x, JsContext<JsExpression> ctx) {
0748:                    /*
0749:                     * We only want to look at invocations of things that we statically know
0750:                     * to be functions. Otherwise, we can't know what statements the
0751:                     * invocation would actually invoke. The static reference would be null
0752:                     * when trying operate on references to external functions, or functions
0753:                     * as arguments to another function.
0754:                     */
0755:                    JsFunction f = isFunction(x.getQualifier());
0756:                    if (f == null) {
0757:                        return;
0758:                    }
0759:
0760:                    // Don't inline blacklisted functions
0761:                    if (blacklist.contains(f)) {
0762:                        return;
0763:                    }
0764:
0765:                    List<JsStatement> statements = f.getBody().getStatements();
0766:                    List<JsExpression> hoisted = new ArrayList<JsExpression>(
0767:                            statements.size());
0768:
0769:                    boolean sawReturnStatement = false;
0770:                    for (JsStatement statement : statements) {
0771:                        /*
0772:                         * Create replacement expressions to use in place of the original
0773:                         * statements. It is important that the replacement is newly-minted and
0774:                         * therefore not referenced by any other AST nodes. Consider the case of
0775:                         * a common, delegating function. If the hoisted expressions were not
0776:                         * distinct objects, it would not be possible to substitute different
0777:                         * JsNameRefs at different call sites.
0778:                         */
0779:                        JsExpression h = hoistedExpression(statement);
0780:                        if (h == null) {
0781:                            return;
0782:                        }
0783:
0784:                        hoisted.add(h);
0785:
0786:                        if (isReturnStatement(statement)) {
0787:                            sawReturnStatement = true;
0788:                            break;
0789:                        }
0790:                    }
0791:
0792:                    /*
0793:                     * If the inlined method has no return statement, synthesize an undefined
0794:                     * reference. It will be reclaimed if the method call is from a
0795:                     * JsExprStmt.
0796:                     */
0797:                    if (!sawReturnStatement) {
0798:                        hoisted.add(program.getUndefinedLiteral());
0799:                    }
0800:
0801:                    assert (hoisted.size() > 0);
0802:
0803:                    /*
0804:                     * Build up the new comma expression from right-to-left; building the
0805:                     * rightmost comma expressions first. Bootstrapping with i.previous()
0806:                     * ensures that this logic will function correctly in the case of a single
0807:                     * expression.
0808:                     */
0809:                    ListIterator<JsExpression> i = hoisted.listIterator(hoisted
0810:                            .size());
0811:                    JsExpression op = i.previous();
0812:                    while (i.hasPrevious()) {
0813:                        JsBinaryOperation outerOp = new JsBinaryOperation(
0814:                                JsBinaryOperator.COMMA);
0815:                        outerOp.setArg1(i.previous());
0816:                        outerOp.setArg2(op);
0817:                        op = outerOp;
0818:                    }
0819:
0820:                    // Confirm that the expression conforms to the desired heuristics
0821:                    if (!isInlinable(functionStack.peek(), x, f, op)) {
0822:                        return;
0823:                    }
0824:
0825:                    // Perform the name replacement
0826:                    NameRefReplacerVisitor v = new NameRefReplacerVisitor(x, f);
0827:                    op = v.accept(op);
0828:
0829:                    // Normalize any nested comma expressions that we may have generated.
0830:                    op = (new CommaNormalizer()).accept(op);
0831:
0832:                    /*
0833:                     * Compare the relative complexity of the original invocation versus the
0834:                     * inlined form.
0835:                     */
0836:                    int originalComplexity = complexity(x);
0837:                    int inlinedComplexity = complexity(op);
0838:                    if (((float) inlinedComplexity / originalComplexity) > MAX_COMPLEXITY_INCREASE) {
0839:                        return;
0840:                    }
0841:
0842:                    /*
0843:                     * See if any further inlining can be performed in the current context. By
0844:                     * attempting to maximize the level of inlining now, we can reduce the
0845:                     * total number of passes required to finalize the AST.
0846:                     */
0847:                    op = accept(op);
0848:
0849:                    ctx.replaceMe(op);
0850:                }
0851:
0852:                @Override
0853:                public boolean visit(JsFunction x, JsContext<JsExpression> ctx) {
0854:                    functionStack.push(x);
0855:                    return true;
0856:                }
0857:            }
0858:
0859:            /**
0860:             * Replace references to JsNames with the inlined JsExpression.
0861:             */
0862:            private static class NameRefReplacerVisitor extends JsModVisitor {
0863:                /**
0864:                 * Set up a map of parameter names back to the expressions that will be
0865:                 * passed in from the outer call site.
0866:                 */
0867:                final Map<JsName, JsExpression> paramsToArgsMap = new HashMap<JsName, JsExpression>();
0868:
0869:                /**
0870:                 * Constructor.
0871:                 * 
0872:                 * @param invocation The call site
0873:                 * @param function The function that encloses the inlined statement
0874:                 */
0875:                public NameRefReplacerVisitor(JsInvocation invocation,
0876:                        JsFunction function) {
0877:                    List<JsParameter> parameters = function.getParameters();
0878:                    List<JsExpression> arguments = invocation.getArguments();
0879:
0880:                    if (parameters.size() != arguments.size()) {
0881:                        // This shouldn't happen if the cloned JsInvocation has been properly
0882:                        // configured
0883:                        throw new InternalCompilerException(
0884:                                "Mismatch on parameters and arguments");
0885:                    }
0886:
0887:                    for (int i = 0; i < parameters.size(); i++) {
0888:                        JsParameter p = parameters.get(i);
0889:                        JsExpression e = arguments.get(i);
0890:                        paramsToArgsMap.put(p.getName(), e);
0891:                    }
0892:                }
0893:
0894:                /**
0895:                 * Replace JsNameRefs that refer to parameters with the expression passed
0896:                 * into the function invocation.
0897:                 */
0898:                @Override
0899:                public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
0900:                    if (x.getQualifier() != null) {
0901:                        return;
0902:                    }
0903:
0904:                    /*
0905:                     * TODO if we ever allow mutable JsExpression types to be considered
0906:                     * always flexible, then it would be necessary to clone the expression.
0907:                     */
0908:                    JsExpression original = paramsToArgsMap.get(x.getName());
0909:
0910:                    if (original != null) {
0911:                        ctx.replaceMe(original);
0912:                    }
0913:                }
0914:            }
0915:
0916:            /**
0917:             * Detects uses of parameters that would produce incorrect results if inlined.
0918:             * Generally speaking, we disallow the use of parameters as lvalues.
0919:             */
0920:            private static class ParameterUsageVisitor extends JsVisitor {
0921:                private boolean lvalue = false;
0922:                private final Set<JsName> parameterNames;
0923:
0924:                public ParameterUsageVisitor(Set<JsName> parameterNames) {
0925:                    this .parameterNames = parameterNames;
0926:                }
0927:
0928:                /**
0929:                 * Disallow inlining if the left-hand side of an assignment is a parameter.
0930:                 */
0931:                @Override
0932:                public void endVisit(JsBinaryOperation x,
0933:                        JsContext<JsExpression> ctx) {
0934:                    JsBinaryOperator op = x.getOperator();
0935:
0936:                    // Don't allow assignments to the left-hand side.
0937:                    if (op.isAssignment() && isParameter(x.getArg1())) {
0938:                        lvalue = true;
0939:                    }
0940:                }
0941:
0942:                /**
0943:                 * Delegates to {@link #checkUnaryOperation(JsUnaryOperation)}.
0944:                 */
0945:                @Override
0946:                public void endVisit(JsPostfixOperation x,
0947:                        JsContext<JsExpression> ctx) {
0948:                    checkUnaryOperation(x);
0949:                }
0950:
0951:                /**
0952:                 * Delegates to {@link #checkUnaryOperation(JsUnaryOperation)}.
0953:                 */
0954:                @Override
0955:                public void endVisit(JsPrefixOperation x,
0956:                        JsContext<JsExpression> ctx) {
0957:                    checkUnaryOperation(x);
0958:                }
0959:
0960:                public boolean parameterAsLValue() {
0961:                    return lvalue;
0962:                }
0963:
0964:                /**
0965:                 * Disallow modification of parameters via unary operations.
0966:                 */
0967:                private void checkUnaryOperation(JsUnaryOperation x) {
0968:                    if (x.getOperator().isModifying()
0969:                            && isParameter(x.getArg())) {
0970:                        lvalue = true;
0971:                    }
0972:                }
0973:
0974:                /**
0975:                 * Determine if a JsExpression is a JsNameRef that refers to a parameter.
0976:                 */
0977:                private boolean isParameter(JsExpression e) {
0978:                    if (!(e instanceof  JsNameRef)) {
0979:                        return false;
0980:                    }
0981:
0982:                    JsNameRef ref = (JsNameRef) e;
0983:                    if (ref.getQualifier() != null) {
0984:                        return false;
0985:                    }
0986:
0987:                    JsName name = ref.getName();
0988:                    return parameterNames.contains(name);
0989:                }
0990:            }
0991:
0992:            /**
0993:             * Collect self-recursive functions. This visitor does not look for
0994:             * mutually-recursive functions because inlining one of the functions into the
0995:             * other would make the single resultant function self-recursive and not
0996:             * eligible for inlining in a subsequent pass.
0997:             */
0998:            private static class RecursionCollector extends JsVisitor {
0999:                private final Stack<JsFunction> functionStack = new Stack<JsFunction>();
1000:                private final Set<JsFunction> recursive = new HashSet<JsFunction>();
1001:
1002:                @Override
1003:                public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
1004:                    if (!functionStack.pop().equals(x)) {
1005:                        throw new InternalCompilerException(
1006:                                "Unexpected function popped");
1007:                    }
1008:                }
1009:
1010:                @Override
1011:                public void endVisit(JsInvocation x, JsContext<JsExpression> ctx) {
1012:                    /*
1013:                     * Because functions can encapsulate other functions, we look at the
1014:                     * entire stack and not just the top element. This would prevent inlining
1015:                     * 
1016:                     * function a() { function b() { a(); } b(); }
1017:                     * 
1018:                     * in the case that we generally allow nested functions to be inlinable.
1019:                     */
1020:                    JsFunction f = isFunction(x.getQualifier());
1021:                    if (functionStack.contains(f)) {
1022:                        recursive.add(f);
1023:                    }
1024:                }
1025:
1026:                public Set<JsFunction> getRecursive() {
1027:                    return recursive;
1028:                }
1029:
1030:                @Override
1031:                public boolean visit(JsFunction x, JsContext<JsExpression> ctx) {
1032:                    functionStack.push(x);
1033:                    return true;
1034:                }
1035:            }
1036:
1037:            /**
1038:             * Determine which functions should not be inlined because they are redefined
1039:             * during program execution. This would violate the assumption that the
1040:             * statements to be executed by any given function invocation are stable over
1041:             * the lifetime of the program.
1042:             */
1043:            private static class RedefinedFunctionCollector extends JsVisitor {
1044:                private final Map<JsName, JsFunction> nameMap = new IdentityHashMap<JsName, JsFunction>();
1045:                private final Set<JsFunction> redefined = new HashSet<JsFunction>();
1046:
1047:                /**
1048:                 * Look for assignments to JsNames whose static references are JsFunctions.
1049:                 */
1050:                @Override
1051:                public void endVisit(JsBinaryOperation x,
1052:                        JsContext<JsExpression> ctx) {
1053:
1054:                    if (!x.getOperator().equals(JsBinaryOperator.ASG)) {
1055:                        return;
1056:                    }
1057:
1058:                    JsFunction f = isFunction(x.getArg1());
1059:                    if (f != null) {
1060:                        redefined.add(f);
1061:                    }
1062:                }
1063:
1064:                /**
1065:                 * Look for the case where a function is declared with the same name as an
1066:                 * existing function.
1067:                 */
1068:                @Override
1069:                public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
1070:                    JsName name = x.getName();
1071:
1072:                    if (name == null) {
1073:                        // Ignore anonymous functions
1074:                        return;
1075:
1076:                    } else if (nameMap.containsKey(name)) {
1077:                        /*
1078:                         * We have to add the current function as well as the original
1079:                         * JsFunction that was declared to use that name.
1080:                         */
1081:                        redefined.add(nameMap.get(name));
1082:                        redefined.add(x);
1083:                    } else {
1084:                        nameMap.put(name, x);
1085:                    }
1086:                }
1087:
1088:                public Collection<JsFunction> getRedefined() {
1089:                    return redefined;
1090:                }
1091:            }
1092:
1093:            /**
1094:             * Given a collection of JsNames, determine if an AST node refers to any of
1095:             * those names.
1096:             */
1097:            private static class RefersToNameVisitor extends JsVisitor {
1098:                private final Collection<JsName> names;
1099:                private boolean refersToName;
1100:                private boolean refersToUnbound;
1101:
1102:                public RefersToNameVisitor(Collection<JsName> names) {
1103:                    this .names = names;
1104:                }
1105:
1106:                @Override
1107:                public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
1108:                    JsName name = x.getName();
1109:
1110:                    if (name == null) {
1111:                        refersToUnbound = true;
1112:                    } else {
1113:                        refersToName = refersToName || names.contains(name);
1114:                    }
1115:                }
1116:
1117:                public boolean refersToName() {
1118:                    return refersToName;
1119:                }
1120:
1121:                public boolean refersToUnbound() {
1122:                    return refersToUnbound;
1123:                }
1124:            }
1125:
1126:            /**
1127:             * Examine a node to determine if it might produce side effects.
1128:             */
1129:            private static class SideEffectsVisitor extends JsVisitor {
1130:                private boolean hasSideEffects;
1131:
1132:                @Override
1133:                public void endVisit(JsBinaryOperation x,
1134:                        JsContext<JsExpression> ctx) {
1135:                    hasSideEffects |= (x.getOperator().isAssignment());
1136:                }
1137:
1138:                @Override
1139:                public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
1140:                    /*
1141:                     * Declaring a named function implicitly has an assignment side-effect.
1142:                     */
1143:                    hasSideEffects |= x.getName() != null;
1144:                }
1145:
1146:                @Override
1147:                public void endVisit(JsInvocation x, JsContext<JsExpression> ctx) {
1148:                    /*
1149:                     * We don't actually need to drill-down into other functions to see if
1150:                     * they do or do not have side-effects. The simple, side-effect free
1151:                     * function invocations will naturally be inlined in subsequent
1152:                     * iterations.
1153:                     */
1154:                    hasSideEffects = true;
1155:                }
1156:
1157:                @Override
1158:                public void endVisit(JsNew x, JsContext<JsExpression> ctx) {
1159:                    /*
1160:                     * The typical use of the new keyword in JavaScript generated by GWT is to
1161:                     * create a prototypical object, and then pass it into a Java-derived
1162:                     * constructor. Given that the majority of the uses of new would not
1163:                     * benefit from inlining, it's not worth the extra complexity of worrying
1164:                     * about yet another set of special cases.
1165:                     */
1166:                    hasSideEffects = true;
1167:                }
1168:
1169:                @Override
1170:                public void endVisit(JsPostfixOperation x,
1171:                        JsContext<JsExpression> ctx) {
1172:                    hasSideEffects |= x.getOperator().isModifying();
1173:                }
1174:
1175:                @Override
1176:                public void endVisit(JsPrefixOperation x,
1177:                        JsContext<JsExpression> ctx) {
1178:                    hasSideEffects |= x.getOperator().isModifying();
1179:                }
1180:
1181:                @Override
1182:                public void endVisit(JsThrow x, JsContext<JsStatement> ctx) {
1183:                    hasSideEffects = true;
1184:                }
1185:
1186:                public boolean hasSideEffects() {
1187:                    return hasSideEffects;
1188:                }
1189:            }
1190:
1191:            /**
1192:             * This ensures that changing the scope of an expression from its enclosing
1193:             * function into the scope of the call site will not cause unqualified
1194:             * identifiers to resolve to different values.
1195:             */
1196:            private static class StableNameChecker extends JsVisitor {
1197:                private final JsScope calleeScope;
1198:                private final JsScope callerScope;
1199:                private final Collection<JsName> parameterNames;
1200:                private boolean stable = true;
1201:
1202:                public StableNameChecker(JsScope callerScope,
1203:                        JsScope calleeScope, Collection<JsName> parameterNames) {
1204:                    this .callerScope = callerScope;
1205:                    this .calleeScope = calleeScope;
1206:                    this .parameterNames = parameterNames;
1207:                }
1208:
1209:                @Override
1210:                public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
1211:                    /*
1212:                     * We can ignore qualified reference, since their scope is always that of
1213:                     * the qualifier.
1214:                     */
1215:                    if (x.getQualifier() != null) {
1216:                        return;
1217:                    }
1218:
1219:                    /*
1220:                     * Attempt to resolve the ident in both scopes
1221:                     */
1222:                    JsName callerName = callerScope.findExistingName(x
1223:                            .getIdent());
1224:                    JsName calleeName = calleeScope.findExistingName(x
1225:                            .getIdent());
1226:
1227:                    if (callerName == null && calleeName == null) {
1228:                        // They both reference out-of-module names
1229:
1230:                    } else if (parameterNames.contains(calleeName)) {
1231:                        // A reference to a parameter, which will be replaced by an argument
1232:
1233:                    } else if (callerName != null
1234:                            && callerName.equals(calleeName)) {
1235:                        // The names are known to us and are the same
1236:
1237:                    } else {
1238:                        stable = false;
1239:                    }
1240:                }
1241:
1242:                public boolean isStable() {
1243:                    return stable;
1244:                }
1245:            }
1246:
1247:            /**
1248:             * A List of expression types that are known to never be affected by
1249:             * side-effects. Used by {@link #alwaysFlexible(JsExpression)}.
1250:             */
1251:            private static final List<Class<?>> ALWAYS_FLEXIBLE = Arrays
1252:                    .asList(new Class<?>[] { JsBooleanLiteral.class,
1253:                            JsDecimalLiteral.class, JsIntegralLiteral.class,
1254:                            JsNullLiteral.class, JsRegExp.class,
1255:                            JsStringLiteral.class, JsThisRef.class });
1256:
1257:            /**
1258:             * When attempting to inline an invocation, this constant determines the
1259:             * maximum allowable ratio of potential inlined complexity to initial
1260:             * complexity. This acts as a brake on very large expansions from bloating the
1261:             * the generated output. Increasing this number will allow larger sections of
1262:             * code to be inlined, but at a cost of larger JS output.
1263:             */
1264:            private static final int MAX_COMPLEXITY_INCREASE = 5;
1265:
1266:            /**
1267:             * Static entry point used by JavaToJavaScriptCompiler.
1268:             */
1269:            public static boolean exec(JsProgram program) {
1270:                RedefinedFunctionCollector d = new RedefinedFunctionCollector();
1271:                d.accept(program);
1272:
1273:                RecursionCollector rc = new RecursionCollector();
1274:                rc.accept(program);
1275:
1276:                InliningVisitor v = new InliningVisitor(program);
1277:                v.blacklist(d.getRedefined());
1278:                v.blacklist(rc.getRecursive());
1279:                v.accept(program);
1280:
1281:                DuplicateXORemover r = new DuplicateXORemover(program);
1282:                r.accept(program);
1283:
1284:                return v.didChange() || r.didChange();
1285:            }
1286:
1287:            /**
1288:             * Indicates if an expression can be repeated multiple times in a delegation
1289:             * removal without side-effects.
1290:             */
1291:            private static boolean alwaysFlexible(JsExpression e) {
1292:                if (e instanceof  JsNameRef) {
1293:                    return false;
1294:                } else {
1295:                    return ALWAYS_FLEXIBLE.contains(e.getClass());
1296:                }
1297:            }
1298:
1299:            private static int complexity(JsNode<?> toEstimate) {
1300:                ComplexityEstimator e = new ComplexityEstimator();
1301:                e.accept(toEstimate);
1302:                return e.getComplexity();
1303:            }
1304:
1305:            /**
1306:             * Check to see if the to-be-inlined statement shares any idents with the
1307:             * call-side arguments. Two passes are made: the first one looks for qualified
1308:             * names; the second pass looks for unqualified names, but ignores identifiers
1309:             * that refer to function parameters.
1310:             */
1311:            private static boolean hasCommonIdents(
1312:                    List<JsExpression> arguments, JsNode<?> toInline,
1313:                    Collection<String> parameterIdents) {
1314:
1315:                // This is a fire-twice loop
1316:                boolean checkQualified = false;
1317:                do {
1318:                    checkQualified = !checkQualified;
1319:
1320:                    // Collect the idents used in the arguments and the statement
1321:                    IdentCollector argCollector = new IdentCollector(
1322:                            checkQualified);
1323:                    argCollector.acceptList(arguments);
1324:                    IdentCollector statementCollector = new IdentCollector(
1325:                            checkQualified);
1326:                    statementCollector.accept(toInline);
1327:
1328:                    Set<String> idents = argCollector.getIdents();
1329:
1330:                    // Unqualified idents may be references to parameters, thus ignored
1331:                    if (!checkQualified) {
1332:                        idents.removeAll(parameterIdents);
1333:                    }
1334:
1335:                    // Perform the set difference
1336:                    idents.retainAll(statementCollector.getIdents());
1337:
1338:                    if (idents.size() > 0) {
1339:                        return true;
1340:                    }
1341:                } while (checkQualified);
1342:
1343:                return false;
1344:            }
1345:
1346:            /**
1347:             * Determine whether or not an AST node has side effects.
1348:             */
1349:            private static boolean hasSideEffects(JsVisitable<?> e) {
1350:                SideEffectsVisitor v = new SideEffectsVisitor();
1351:                v.accept(e);
1352:                return v.hasSideEffects();
1353:            }
1354:
1355:            /**
1356:             * Determine whether or not a list of AST nodes have side effects.
1357:             */
1358:            private static <T extends JsVisitable<T>> boolean hasSideEffects(
1359:                    List<T> list) {
1360:                SideEffectsVisitor v = new SideEffectsVisitor();
1361:                v.acceptList(list);
1362:                return v.hasSideEffects();
1363:            }
1364:
1365:            /**
1366:             * Given a delegated JsStatement, construct an expression to hoist into the
1367:             * outer caller. This does not perform any name replacement, but simply
1368:             * constructs a mutable copy of the expression that can be manipulated
1369:             * at-will.
1370:             */
1371:            private static JsExpression hoistedExpression(JsStatement statement) {
1372:                JsExpression expression;
1373:                if (statement instanceof  JsExprStmt) {
1374:                    JsExprStmt exprStmt = (JsExprStmt) statement;
1375:                    expression = exprStmt.getExpression();
1376:                } else if (statement instanceof  JsReturn) {
1377:                    JsReturn ret = (JsReturn) statement;
1378:                    expression = ret.getExpr();
1379:                } else {
1380:                    return null;
1381:                }
1382:
1383:                return JsHoister.hoist(expression);
1384:            }
1385:
1386:            /**
1387:             * Given a JsInvocation, determine if it is invoking a JsFunction that is
1388:             * specified to be executed only once during the program's lifetime.
1389:             */
1390:            private static JsFunction isExecuteOnce(JsInvocation invocation) {
1391:                JsFunction f = isFunction(invocation.getQualifier());
1392:                if (f != null && f.getExecuteOnce()) {
1393:                    return f;
1394:                }
1395:                return null;
1396:            }
1397:
1398:            /**
1399:             * Given an expression, determine if it it is a JsNameRef that refers to a
1400:             * statically-defined JsFunction.
1401:             */
1402:            private static JsFunction isFunction(JsExpression e) {
1403:                if (e instanceof  JsNameRef) {
1404:                    JsNameRef ref = (JsNameRef) e;
1405:                    JsNode staticRef = ref.getName().getStaticRef();
1406:                    if (staticRef instanceof  JsFunction) {
1407:                        return (JsFunction) staticRef;
1408:                    }
1409:                }
1410:
1411:                return null;
1412:            }
1413:
1414:            /**
1415:             * Determine if a statement can be inlined into a call site.
1416:             */
1417:            private static boolean isInlinable(JsFunction caller,
1418:                    JsInvocation invocation, JsFunction callee,
1419:                    JsNode<?> toInline) {
1420:                List<JsExpression> arguments = invocation.getArguments();
1421:
1422:                /*
1423:                 * This will happen with varargs-style JavaScript functions that rely on the
1424:                 * "arguments" array. The reference to arguments would be detected in
1425:                 * BoundedScopeVisitor, but the code below assumes the same number of
1426:                 * parameters and arguments.
1427:                 */
1428:                if (arguments.size() != callee.getParameters().size()) {
1429:                    return false;
1430:                }
1431:
1432:                // Build up a list of all parameter names
1433:                Set<JsName> parameterNames = new HashSet<JsName>();
1434:                Set<String> parameterIdents = new HashSet<String>();
1435:                for (JsParameter param : callee.getParameters()) {
1436:                    parameterNames.add(param.getName());
1437:                    parameterIdents.add(param.getName().getIdent());
1438:                }
1439:
1440:                /*
1441:                 * Make sure that inlining won't change the final name of non-parameter
1442:                 * idents due to the change of scope. The most likely cause would be the use
1443:                 * of an unqualified variable reference in a JSNI block that happened to
1444:                 * conflict with a Java-derived identifier.
1445:                 */
1446:                StableNameChecker detector = new StableNameChecker(caller
1447:                        .getScope(), callee.getScope(), parameterNames);
1448:                detector.accept(toInline);
1449:                if (!detector.isStable()) {
1450:                    return false;
1451:                }
1452:
1453:                /*
1454:                 * Ensure that the names referred to by the argument list and the statement
1455:                 * are disjoint. This prevents inlining of the following:
1456:                 * 
1457:                 * static int i; public void add(int a) { i += a; }; add(i++);
1458:                 * 
1459:                 */
1460:                if (hasCommonIdents(arguments, toInline, parameterIdents)) {
1461:                    return false;
1462:                }
1463:
1464:                /*
1465:                 * Determine if the evaluation of the invocation's arguments may create side
1466:                 * effects. This will determine how aggressively the parameters may be
1467:                 * reordered.
1468:                 */
1469:                if (hasSideEffects(arguments)) {
1470:                    /*
1471:                     * Determine the order in which the parameters must be evaluated. This
1472:                     * will vary between call sites, based on whether or not the invocation's
1473:                     * arguments can be repeated without ill effect.
1474:                     */
1475:                    List<JsName> requiredOrder = new ArrayList<JsName>();
1476:                    for (int i = 0; i < arguments.size(); i++) {
1477:                        JsExpression e = arguments.get(i);
1478:                        JsParameter p = callee.getParameters().get(i);
1479:
1480:                        if (!alwaysFlexible(e)) {
1481:                            requiredOrder.add(p.getName());
1482:                        }
1483:                    }
1484:
1485:                    /*
1486:                     * Verify that the non-reorderable arguments are evaluated in the right
1487:                     * order.
1488:                     */
1489:                    if (requiredOrder.size() > 0) {
1490:                        EvaluationOrderVisitor orderVisitor = new EvaluationOrderVisitor(
1491:                                requiredOrder);
1492:                        orderVisitor.accept(toInline);
1493:                        if (!orderVisitor.maintainsOrder()) {
1494:                            return false;
1495:                        }
1496:                    }
1497:                }
1498:
1499:                // Check that parameters aren't used in such a way as to prohibit inlining
1500:                ParameterUsageVisitor v = new ParameterUsageVisitor(
1501:                        parameterNames);
1502:                v.accept(toInline);
1503:                if (v.parameterAsLValue()) {
1504:                    return false;
1505:                }
1506:
1507:                // Hooray!
1508:                return true;
1509:            }
1510:
1511:            /**
1512:             * This is used in combination with {@link #hoistedExpression(JsStatement)} to
1513:             * indicate if a given statement would terminate the list of hoisted
1514:             * expressions.
1515:             */
1516:            private static boolean isReturnStatement(JsStatement statement) {
1517:                return statement instanceof  JsReturn;
1518:            }
1519:
1520:            /**
1521:             * Utility class.
1522:             */
1523:            private JsInliner() {
1524:            }
1525:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.