Removes UpRefVisitor from ControlFlowAnalyzer, which scans the whole
program to find extra methods that need rescuing due to virtual
method calls. Instead, such methods are now checked at the
time a new method or type becomes instantiable.
Also, adds PerfLogger calls to GWTCompiler, to make it easy to time
an entire compile.
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4015 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index 9e90603..fd4786a 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -20,6 +20,7 @@
import com.google.gwt.dev.CompilePerms.CompilePermsOptionsImpl;
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
import com.google.gwt.dev.Precompile.PrecompileOptionsImpl;
+import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
import java.io.File;
@@ -100,6 +101,7 @@
if (options.isValidateOnly()) {
return new Precompile(options).run(logger);
} else {
+ PerfLogger.start("compile");
logger = logger.branch(TreeLogger.INFO, "Compiling module "
+ options.getModuleName());
if (new Precompile(options).run(logger)) {
@@ -112,11 +114,13 @@
if (new CompilePerms(permsOptions).run(logger)) {
if (new Link(options).run(logger)) {
logger.log(TreeLogger.INFO, "Compilation succeeded");
+ PerfLogger.end();
return true;
}
}
}
logger.log(TreeLogger.ERROR, "Compilation failed");
+ PerfLogger.end();
return false;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
index d0e5e1f..42afd16 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -267,7 +267,6 @@
initiallyLive = new ControlFlowAnalyzer(jprogram);
traverseEntry(initiallyLive, 0);
- initiallyLive.finishTraversal();
methodsInJavaScript = fragmentExtractor.findAllMethodsInJavaScript();
}
@@ -310,7 +309,6 @@
// Traverse leftoversFragmentHasLoaded, because it should not
// go into any of the exclusive fragments.
cfa.traverseFromLeftoversFragmentHasLoaded();
- cfa.finishTraversal();
allButOnes.add(cfa);
}
@@ -326,7 +324,6 @@
traverseEntry(everything, entry);
}
everything.traverseFromLeftoversFragmentHasLoaded();
- everything.finishTraversal();
return everything;
}
@@ -368,7 +365,6 @@
for (int base = 1; base < numEntries; base++) {
ControlFlowAnalyzer baseCfa = new ControlFlowAnalyzer(initiallyLive);
traverseEntry(baseCfa, base);
- baseCfa.finishTraversal();
LivenessPredicate baseLive = new CfaLivenessPredicate(baseCfa);
// secondary base
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index 22ff39d..51d0721 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -57,15 +57,15 @@
import com.google.gwt.dev.js.ast.JsVisitor;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
* This class finds out what code in a program is live based on starting
- * execution at a specified location. Note that the client must call
- * {@link #finishTraversal()} after the other traversal methods have been
- * called, or the results will be incomplete.
+ * execution at a specified location.
*/
public class ControlFlowAnalyzer {
@@ -182,7 +182,7 @@
// Rescue my super type
rescue(type.extnds, true, isInstantiated);
- // Rescue my clinit (it won't ever be explicitly referenced
+ // Rescue my clinit (it won't ever be explicitly referenced)
rescue(type.methods.get(0));
// JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
@@ -192,6 +192,8 @@
rescue(intfType, false, isInstantiated);
}
+ rescueMethodsIfInstantiable(type);
+
return false;
}
@@ -256,6 +258,8 @@
accept(it);
}
+ rescueMethodsIfInstantiable(type);
+
return false;
}
@@ -305,7 +309,18 @@
@Override
public boolean visit(JMethodCall call, Context ctx) {
- rescue(call.getTarget());
+ JMethod method = call.getTarget();
+ if (method.isStatic()
+ || program.isJavaScriptObject(method.getEnclosingType())
+ || instantiatedTypes.contains(method.getEnclosingType())) {
+ rescue(method);
+ } else {
+ // It's a virtual method whose class is not instantiable
+ if (!liveFieldsAndMethods.contains(method)) {
+ methodsLiveExceptForInstantiability.add(method);
+ }
+ }
+
return true;
}
@@ -448,7 +463,10 @@
if (method != null) {
if (!liveFieldsAndMethods.contains(method)) {
liveFieldsAndMethods.add(method);
+ methodsLiveExceptForInstantiability.remove(method);
+
accept(method);
+
if (method.isNative()) {
/*
* SPECIAL: returning from this method passes a value from
@@ -456,6 +474,9 @@
*/
maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
}
+
+ rescueOverridingMethods(method);
+
return true;
}
}
@@ -528,57 +549,72 @@
rescue(stringValueOfChar);
}
}
- }
- /**
- * Traverse methods that are reachable via virtual method calls. Specifically,
- * traverse methods whose classes are instantiable and which override a method
- * that is live.
- */
- private class UpRefVisitor extends JVisitor {
-
- private boolean didRescue = false;
-
- public boolean didRescue() {
- return didRescue;
- }
-
- @Override
- public boolean visit(JClassType x, Context ctx) {
- return instantiatedTypes.contains(x);
- }
-
- @Override
- public boolean visit(JMethod x, Context ctx) {
- if (liveFieldsAndMethods.contains(x)) {
- return false;
- }
-
- for (JMethod override : program.typeOracle.getAllOverrides(x)) {
- if (liveFieldsAndMethods.contains(override)) {
- rescuer.rescue(x);
- didRescue = true;
- return false;
+ /**
+ * If the type is instantiable, rescue any of its virtual methods that a
+ * previously seen method call could call.
+ */
+ private void rescueMethodsIfInstantiable(JReferenceType type) {
+ if (instantiatedTypes.contains(type)) {
+ for (JMethod method : type.methods) {
+ if (!method.isStatic()) {
+ if (methodsLiveExceptForInstantiability.contains(method)) {
+ rescue(method);
+ continue;
+ }
+ }
}
}
- return false;
}
- @Override
- public boolean visit(JProgram x, Context ctx) {
- didRescue = false;
- return true;
+ /**
+ * Assume that <code>method</code> is live. Rescue any overriding methods
+ * that might be called if <code>method</code> is called through virtual
+ * dispatch.
+ */
+ private void rescueOverridingMethods(JMethod method) {
+ if (!method.isStatic()) {
+
+ List<JMethod> overriders = methodsThatOverrideMe.get(method);
+ if (overriders != null) {
+ for (JMethod overrider : overriders) {
+ if (liveFieldsAndMethods.contains(overrider)) {
+ // The override is already alive, do nothing.
+ } else if (instantiatedTypes.contains(overrider.getEnclosingType())) {
+ // The enclosing class is alive, make my override reachable.
+ rescue(overrider);
+ } else {
+ // The enclosing class is not yet alive, put override in limbo.
+ methodsLiveExceptForInstantiability.add(overrider);
+ }
+ }
+ }
+ }
}
}
private Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
private Set<JNode> liveFieldsAndMethods = new HashSet<JNode>();
private Set<String> liveStrings = new HashSet<String>();
+
+ /**
+ * Schrodinger's methods... aka "limbo". :) These are instance methods that
+ * seem to be reachable, only their enclosing type is uninstantiable. We place
+ * these methods into purgatory until/unless the enclosing type is found to be
+ * instantiable.
+ */
+ private Set<JMethod> methodsLiveExceptForInstantiability = new HashSet<JMethod>();
+
+ /**
+ * A precomputed map of all instance methods onto a set of methods that
+ * override each key method.
+ */
+ private Map<JMethod, List<JMethod>> methodsThatOverrideMe;
+
private final JProgram program;
private Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
private final RescueVisitor rescuer = new RescueVisitor();
private JMethod stringValueOfChar = null;
- private final UpRefVisitor upRefer = new UpRefVisitor();
public ControlFlowAnalyzer(ControlFlowAnalyzer cfa) {
program = cfa.program;
@@ -587,21 +623,14 @@
referencedTypes = new HashSet<JReferenceType>(cfa.referencedTypes);
stringValueOfChar = cfa.stringValueOfChar;
liveStrings = new HashSet<String>(cfa.liveStrings);
+ methodsLiveExceptForInstantiability = new HashSet<JMethod>(
+ cfa.methodsLiveExceptForInstantiability);
+ methodsThatOverrideMe = cfa.methodsThatOverrideMe;
}
public ControlFlowAnalyzer(JProgram program) {
this.program = program;
- }
-
- /**
- * Finish any remaining traversal that is needed. This must be called after
- * calling any of the other traversal methods in order to get accurate
- * results. It can also be called eagerly.
- */
- public void finishTraversal() {
- do {
- upRefer.accept(program);
- } while (upRefer.didRescue());
+ buildMethodsOverriding();
}
/**
@@ -653,9 +682,7 @@
class ReplaceStringLiterals extends JModVisitor {
@Override
public void endVisit(JStringLiteral stringLiteral, Context ctx) {
- ctx.replaceMe(program.getLiteralString(
- stringLiteral.getSourceInfo().makeChild(ControlFlowAnalyzer.class,
- "remove string literals"), ""));
+ ctx.replaceMe(program.getLiteralNull());
}
}
@@ -683,4 +710,20 @@
public void traverseFromReferenceTo(JReferenceType type) {
rescuer.rescue(type, true, false);
}
+
+ private void buildMethodsOverriding() {
+ methodsThatOverrideMe = new HashMap<JMethod, List<JMethod>>();
+ for (JReferenceType type : program.getDeclaredTypes()) {
+ for (JMethod method : type.methods) {
+ for (JMethod overridden : program.typeOracle.getAllOverrides(method)) {
+ List<JMethod> overs = methodsThatOverrideMe.get(overridden);
+ if (overs == null) {
+ overs = new ArrayList<JMethod>();
+ methodsThatOverrideMe.put(overridden, overs);
+ }
+ overs.add(method);
+ }
+ }
+ }
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index 231e624..a948c56 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -463,7 +463,6 @@
livenessAnalyzer.traverseFrom(method);
}
livenessAnalyzer.traverseFromLeftoversFragmentHasLoaded();
- livenessAnalyzer.finishTraversal();
program.typeOracle.setInstantiatedTypes(livenessAnalyzer.getInstantiatedTypes());