Author: b...@google.com Date: Fri Feb 6 06:39:06 2009 New Revision: 4648 Modified: changes/bobv/elide_rpc_type_names_r4602/dev/core/src/com/google/gwt/dev/js/JsInliner.java
Log: Fix runAsync interaction by not expanding top-level function invocations. Modified: changes/bobv/elide_rpc_type_names_r4602/dev/core/src/com/google/gwt/dev/js/JsInliner.java ============================================================================== --- changes/bobv/elide_rpc_type_names_r4602/dev/core/src/com/google/gwt/dev/js/JsInliner.java (original) +++ changes/bobv/elide_rpc_type_names_r4602/dev/core/src/com/google/gwt/dev/js/JsInliner.java Fri Feb 6 06:39:06 2009 @@ -15,6 +15,7 @@ */ package com.google.gwt.dev.js; +import com.google.gwt.dev.jjs.HasSourceInfo; import com.google.gwt.dev.jjs.InternalCompilerException; import com.google.gwt.dev.jjs.SourceInfo; import com.google.gwt.dev.js.ast.JsArrayAccess; @@ -46,6 +47,7 @@ import com.google.gwt.dev.js.ast.JsPostfixOperation; import com.google.gwt.dev.js.ast.JsPrefixOperation; import com.google.gwt.dev.js.ast.JsProgram; +import com.google.gwt.dev.js.ast.JsProgramFragment; import com.google.gwt.dev.js.ast.JsRegExp; import com.google.gwt.dev.js.ast.JsReturn; import com.google.gwt.dev.js.ast.JsScope; @@ -153,6 +155,12 @@ return op.getOperator().equals(JsBinaryOperator.COMMA) ? op : null; } + private final List<JsName> localVariableNames; + + public CommaNormalizer(List<JsName> localVariableNames) { + this.localVariableNames = localVariableNames; + } + @Override public void endVisit(JsBinaryOperation x, JsContext<JsExpression> ctx) { if (isComma(x) == null) { @@ -178,6 +186,41 @@ x.setArg1(inner.getArg1()); didChange = true; } + + /* + * Eliminate the pattern (localVar = expr, localVar). This tends to + * occur when a method interacted with pruned fields or had statements + * removed. + */ + JsName assignmentRef = null; + JsExpression expr = null; + JsName returnRef = null; + + if (x.getArg1() instanceof JsBinaryOperation) { + JsBinaryOperation op = (JsBinaryOperation) x.getArg1(); + if (op.getOperator() == JsBinaryOperator.ASG + && op.getArg1() instanceof JsNameRef) { + JsNameRef nameRef = (JsNameRef) op.getArg1(); + if (nameRef.getQualifier() == null) { + assignmentRef = nameRef.getName(); + expr = op.getArg2(); + } + } + } + + if (x.getArg2() instanceof JsNameRef) { + JsNameRef nameRef = (JsNameRef) x.getArg2(); + if (nameRef.getQualifier() == null) { + returnRef = nameRef.getName(); + } + } + + if (assignmentRef != null && assignmentRef.equals(returnRef) + && localVariableNames.contains(assignmentRef)) { + assert expr != null; + localVariableNames.remove(assignmentRef); + ctx.replaceMe(expr); + } return; } @@ -680,10 +723,14 @@ private final Set<JsFunction> blacklist = new HashSet<JsFunction>(); private final Stack<JsFunction> functionStack = new Stack<JsFunction>(); private final InvocationCountingVisitor invocationCountingVisitor = new InvocationCountingVisitor(); - private final Stack<List<JsName>> newLocalVariableStack = new Stack<List<JsName>>(); private final JsProgram program; + /** + * Not a stack because program fragments aren't nested. + */ + private JsFunction programFunction; + public InliningVisitor(JsProgram program) { this.program = program; invocationCountingVisitor.accept(program); @@ -785,33 +832,10 @@ throw new InternalCompilerException("Unexpected function popped"); } + JsBlock body = x.getBody(); List<JsName> newLocalVariables = newLocalVariableStack.pop(); - // Nothing to do - if (newLocalVariables.isEmpty()) { - return; - } - - List<JsStatement> statements = x.getBody().getStatements(); - - // The body can't be empty if we have local variables to create - assert !statements.isEmpty(); - - // Find or create the JsVars as the first statement - SourceInfo sourceInfo = x.getSourceInfo().makeChild( - InliningVisitor.class, "Synthetic locals"); - JsVars vars; - if (statements.get(0) instanceof JsVars) { - vars = (JsVars) statements.get(0); - } else { - vars = new JsVars(sourceInfo); - statements.add(0, vars); - } - - // Add all variables - for (JsName name : newLocalVariables) { - vars.add(new JsVar(sourceInfo, name)); - } + addVars(x, body, newLocalVariables); } @Override @@ -837,26 +861,22 @@ return; } - /* - * Will see this with certain classes whose clinits are folded into the - * main JsProgram body. - */ - if (f.getBody() == null) { - if (ctx.canRemove()) { - ctx.removeMe(); - } else { - ctx.replaceMe(program.getUndefinedLiteral()); - } - return; + List<JsStatement> statements; + if (f.getBody() != null) { + statements = new ArrayList<JsStatement>(f.getBody().getStatements()); + } else { + /* + * Will see this with certain classes whose clinits are folded into the + * main JsProgram body. + */ + statements = Collections.emptyList(); } - List<JsName> localVariableNames = new ArrayList<JsName>(); - List<JsStatement> statements = new ArrayList<JsStatement>( - f.getBody().getStatements()); List<JsExpression> hoisted = new ArrayList<JsExpression>( statements.size()); - + List<JsName> localVariableNames = new ArrayList<JsName>(); boolean sawReturnStatement = false; + for (JsStatement statement : statements) { if (sawReturnStatement) { /* @@ -888,10 +908,11 @@ return; } - hoisted.add(h); - if (isReturnStatement(statement)) { sawReturnStatement = true; + hoisted.add(h); + } else if (hasSideEffects(Collections.singletonList(h))) { + hoisted.add(h); } } @@ -953,7 +974,7 @@ op = v.accept(op); // Normalize any nested comma expressions that we may have generated. - op = (new CommaNormalizer()).accept(op); + op = (new CommaNormalizer(localVariableNames)).accept(op); /* * Compare the relative complexity of the original invocation versus the @@ -966,6 +987,12 @@ return; } + if (functionStack.peek() == programFunction + && localVariableNames.size() > 0) { + // Don't add additional variables to the top-level program. + return; + } + // We've committed to the inlining, ensure the vars are created newLocalVariableStack.peek().addAll(localVariableNames); @@ -984,10 +1011,76 @@ } @Override + public void endVisit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) { + if (!functionStack.pop().equals(programFunction)) { + throw new InternalCompilerException("Unexpected function popped"); + } + + assert programFunction.getBody().getStatements().size() == 0 : "Should not have moved statements into program"; + + List<JsName> newLocalVariables = newLocalVariableStack.pop(); + assert newLocalVariables.size() == 0 : "Should not have tried to create variables in program"; + } + + @Override + public boolean visit(JsExprStmt x, JsContext<JsStatement> ctx) { + if (functionStack.peek() == programFunction) { + /* Don't inline top-level invocations. */ + if (x.getExpression() instanceof JsInvocation) { + return false; + } + } + return true; + } + + @Override public boolean visit(JsFunction x, JsContext<JsExpression> ctx) { functionStack.push(x); newLocalVariableStack.push(new ArrayList<JsName>()); return true; + } + + /** + * Create a synthetic context to attempt to simplify statements in the + * top-level of the program. + */ + @Override + public boolean visit(JsProgramFragment x, JsContext<JsProgramFragment> ctx) { + programFunction = new JsFunction(program.getSourceInfo(), + program.getScope()); + programFunction.setBody(new JsBlock(x.getSourceInfo())); + functionStack.push(programFunction); + newLocalVariableStack.push(new ArrayList<JsName>()); + return true; + } + + private void addVars(HasSourceInfo x, JsBlock body, + List<JsName> newLocalVariables) { + // Nothing to do + if (newLocalVariables.isEmpty()) { + return; + } + + List<JsStatement> statements = body.getStatements(); + + // The body can't be empty if we have local variables to create + assert !statements.isEmpty(); + + // Find or create the JsVars as the first statement + SourceInfo sourceInfo = x.getSourceInfo().makeChild( + InliningVisitor.class, "Synthetic locals"); + JsVars vars; + if (statements.get(0) instanceof JsVars) { + vars = (JsVars) statements.get(0); + } else { + vars = new JsVars(sourceInfo); + statements.add(0, vars); + } + + // Add all variables + for (JsName name : newLocalVariables) { + vars.add(new JsVar(sourceInfo, name)); + } } private boolean isInvokedMoreThanOnce(JsFunction f) { --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---