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

Reply via email to