Reviewers: scottb, jbrosenberg,
Description:
Enhancement to JsDuplicateFunctionRemover to remove duplicate virtual
methods as well. That is, given
_.method1 = function(a) { body }
_.method2 = function(b) { body }
Hoist the duplicate virtual methods and replace with a named global
function.
function c(a) { body }
_.method1 = c;
_.method2 = c;
Produces a 1%-3% improvement in code size when stack stripping is
enabled.
Please review this at http://gwt-code-reviews.appspot.com/1454806/
Affected files:
M dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
M dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
Index: dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
===================================================================
--- dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
(revision 10277)
+++ dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
(working copy)
@@ -391,6 +391,8 @@
}
if (changed) {
JsUnusedFunctionRemover.exec(jsProgram);
+ // run again
+ JsObfuscateNamer.exec(jsProgram);
}
}
}
Index: dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
===================================================================
--- dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
(revision 10277)
+++ dev/core/src/com/google/gwt/dev/js/JsDuplicateFunctionRemover.java
(working copy)
@@ -46,9 +46,16 @@
private final Map<JsName, JsName> duplicateOriginalMap = new
IdentityHashMap<JsName, JsName>();
+ private final Map<JsFunction, JsFunction> duplicateMethodOriginalMap =
new IdentityHashMap<JsFunction, JsFunction>();
+
+
private final Stack<JsNameRef> invocationQualifiers = new
Stack<JsNameRef>();
+ // static / global methods
private final Map<String, JsName> uniqueBodies = new HashMap<String,
JsName>();
+
+ // vtable methods
+ private final Map<String, JsFunction> uniqueMethodBodies = new
HashMap<String, JsFunction>();
public DuplicateFunctionBodyRecorder() {
// Add sentinel to stop Stack.peek() from throwing exception.
@@ -84,20 +91,31 @@
return duplicateOriginalMap;
}
+ public Map<JsFunction, JsFunction> getDuplicateMethodMap() {
+ return duplicateMethodOriginalMap;
+ }
+
@Override
public boolean visit(JsFunction x, JsContext ctx) {
+ String fnSource = x.toSource();
+ String body = fnSource.substring(fnSource.indexOf("("));
/*
- * Don't process anonymous functions.
+ * Static function processed separate from virtual functions
*/
if (x.getName() != null) {
- String fnSource = x.toSource();
- String body = fnSource.substring(fnSource.indexOf("("));
JsName original = uniqueBodies.get(body);
if (original != null) {
duplicateOriginalMap.put(x.getName(), original);
} else {
uniqueBodies.put(body, x.getName());
}
+ } else if (x.isFromJava()) {
+ JsFunction original = uniqueMethodBodies.get(body);
+ if (original != null) {
+ duplicateMethodOriginalMap.put(x, original);
+ } else {
+ uniqueMethodBodies.put(body, x);
+ }
}
return true;
}
@@ -114,13 +132,27 @@
private class ReplaceDuplicateInvocationNameRefs extends JsModVisitor {
private final Set<JsName> blacklist;
+ private Map<JsFunction, JsFunction> dupMethodMap;
+ private Map<JsFunction, JsName> hoistMap;
private final Map<JsName, JsName> duplicateMap;
public ReplaceDuplicateInvocationNameRefs(Map<JsName, JsName>
duplicateMap,
- Set<JsName> blacklist) {
+ Set<JsName> blacklist, Map<JsFunction, JsFunction> dupMethodMap,
+ Map<JsFunction, JsName> hoistMap) {
this.duplicateMap = duplicateMap;
this.blacklist = blacklist;
+ this.dupMethodMap = dupMethodMap;
+ this.hoistMap = hoistMap;
+ }
+
+ @Override
+ public void endVisit(JsFunction x, JsContext ctx) {
+ if (dupMethodMap.containsKey(x)) {
+
ctx.replaceMe(hoistMap.get(dupMethodMap.get(x)).makeRef(x.getSourceInfo()));
+ } else if (hoistMap.containsKey(x)) {
+ ctx.replaceMe(hoistMap.get(x).makeRef(x.getSourceInfo()));
+ }
}
@Override
@@ -152,8 +184,25 @@
private boolean execImpl(JsBlock fragment) {
DuplicateFunctionBodyRecorder dfbr = new
DuplicateFunctionBodyRecorder();
dfbr.accept(fragment);
+ int count = 0;
+ Map<JsFunction, JsName> hoistMap = new HashMap<JsFunction, JsName>();
+ // Hoist all anonymous versions
+ Map<JsFunction, JsFunction> dupMethodMap =
dfbr.getDuplicateMethodMap();
+ for (JsFunction x : dupMethodMap.values()) {
+ if (!hoistMap.containsKey(x)) {
+ JsName newName = program.getScope().declareName("_DUP" + count++);
+ JsFunction newFunc = new JsFunction(x.getSourceInfo(),
+ program.getScope(), newName, x.isFromJava());
+ newFunc.setBody(x.getBody());
+ newFunc.getParameters().addAll(x.getParameters());
+
+ fragment.getStatements().add(newFunc.makeStmt());
+ hoistMap.put(x, newName);
+ }
+ }
+
ReplaceDuplicateInvocationNameRefs rdup = new
ReplaceDuplicateInvocationNameRefs(
- dfbr.getDuplicateMap(), dfbr.getBlacklist());
+ dfbr.getDuplicateMap(), dfbr.getBlacklist(), dupMethodMap,
hoistMap);
rdup.accept(fragment);
return rdup.didChange();
}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors