Revision: 9675
Author: [email protected]
Date: Fri Feb 4 15:20:13 2011
Log: Don't allocate short names for unreferenced JsNames.
We create names during GenerateJavaScriptAST, but in many cases, references
to those names get optimized out. However, the JsName still exists in the
scope, and consumes a 'space'.
This change avoids wasting an allocation on unreferenced names, so that
more obfuscated idents can fit into fewer characters.
http://gwt-code-reviews.appspot.com/1337803/show
Review by: [email protected]
http://code.google.com/p/google-web-toolkit/source/detail?r=9675
Added:
/trunk/dev/core/src/com/google/gwt/dev/js/JsNamer.java
Modified:
/trunk/dev/core/src/com/google/gwt/dev/js/JsObfuscateNamer.java
/trunk/dev/core/src/com/google/gwt/dev/js/JsPrettyNamer.java
/trunk/dev/core/src/com/google/gwt/dev/js/JsVerboseNamer.java
=======================================
--- /dev/null
+++ /trunk/dev/core/src/com/google/gwt/dev/js/JsNamer.java Fri Feb 4
15:20:13 2011
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+package com.google.gwt.dev.js;
+
+import com.google.gwt.dev.js.ast.JsContext;
+import com.google.gwt.dev.js.ast.JsForIn;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsLabel;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameOf;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsParameter;
+import com.google.gwt.dev.js.ast.JsProgram;
+import com.google.gwt.dev.js.ast.JsScope;
+import com.google.gwt.dev.js.ast.JsVars;
+import com.google.gwt.dev.js.ast.JsVisitor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A class that allocates unique identifiers for JsNames.
+ */
+public abstract class JsNamer {
+
+ private static Set<JsName> collectReferencedNames(JsProgram program) {
+ final Set<JsName> referenced = new HashSet<JsName>();
+ new JsVisitor() {
+ public void endVisit(JsForIn x, JsContext ctx) {
+ reference(x.getIterVarName());
+ }
+
+ public void endVisit(JsFunction x, JsContext ctx) {
+ reference(x.getName());
+ };
+
+ public void endVisit(JsLabel x, JsContext ctx) {
+ reference(x.getName());
+ };
+
+ public void endVisit(JsNameOf x, JsContext ctx) {
+ reference(x.getName());
+ };
+
+ public void endVisit(JsNameRef x, JsContext ctx) {
+ reference(x.getName());
+ };
+
+ public void endVisit(JsParameter x, JsContext ctx) {
+ reference(x.getName());
+ };
+
+ public void endVisit(JsVars.JsVar x, JsContext ctx) {
+ reference(x.getName());
+ };
+
+ private void reference(JsName name) {
+ if (name != null) {
+ referenced.add(name);
+ }
+ }
+ }.accept(program);
+ return referenced;
+ }
+
+ protected final JsProgram program;
+
+ protected final Set<JsName> referenced;
+
+ public JsNamer(JsProgram program) {
+ this.program = program;
+ referenced = collectReferencedNames(program);
+ }
+
+ protected final void execImpl() {
+ reset();
+ visit(program.getScope());
+ reset();
+ visit(program.getObjectScope());
+ }
+
+ protected abstract void reset();
+
+ protected abstract void visit(JsScope scope);
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/js/JsObfuscateNamer.java Wed
Feb 2 13:13:58 2011
+++ /trunk/dev/core/src/com/google/gwt/dev/js/JsObfuscateNamer.java Fri
Feb 4 15:20:13 2011
@@ -17,17 +17,15 @@
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
-import com.google.gwt.dev.js.ast.JsRootScope;
import com.google.gwt.dev.js.ast.JsScope;
import java.util.Iterator;
-import java.util.List;
/**
* A namer that uses short, unrecognizable idents to minimize generated
code
* size.
*/
-public class JsObfuscateNamer {
+public class JsObfuscateNamer extends JsNamer {
/**
* A lookup table of base-64 chars we use to encode idents.
@@ -48,54 +46,22 @@
*/
private int maxChildId = 0;
- private final JsProgram program;
-
/**
* A temp buffer big enough to hold at least 32 bits worth of base-64
chars.
*/
private final char[] sIdentBuf = new char[6];
public JsObfuscateNamer(JsProgram program) {
- this.program = program;
+ super(program);
}
- private void execImpl() {
- visit(program.getRootScope());
+ @Override
+ protected void reset() {
+ maxChildId = 0;
}
- private boolean isLegal(JsScope scope, String newIdent) {
- if (JsKeywords.isKeyword(newIdent)) {
- return false;
- }
- /*
- * Never obfuscate a name into an identifier that conflicts with an
existing
- * unobfuscatable name! It's okay if it conflicts with an existing
- * obfuscatable name, since that name will get obfuscated to something
else
- * anyway.
- */
- return (scope.findExistingUnobfuscatableName(newIdent) == null);
- }
-
- private String makeObfuscatedIdent(int id) {
- // Use base-54 for the first character of the identifier,
- // so that we don't use any numbers (which are illegal at
- // the beginning of an identifier).
- //
- int i = 0;
- sIdentBuf[i++] = sBase64Chars[id % 54];
- id /= 54;
-
- // Use base-64 for the rest of the identifier.
- //
- while (id != 0) {
- sIdentBuf[i++] = sBase64Chars[id & 0x3f];
- id >>= 6;
- }
-
- return new String(sIdentBuf, 0, i);
- }
-
- private void visit(JsScope scope) {
+ @Override
+ protected void visit(JsScope scope) {
// Save off the maxChildId which is currently being computed for my
parent.
int mySiblingsMaxId = maxChildId;
@@ -104,21 +70,20 @@
* clean slate: I do not communicate to my children.
*/
maxChildId = 0;
- List<JsScope> children = scope.getChildren();
- for (Iterator<JsScope> it = children.iterator(); it.hasNext();) {
- visit(it.next());
+ for (JsScope child : scope.getChildren()) {
+ visit(child);
}
// maxChildId is now the max of all of my children's ids
-
- JsRootScope rootScope = program.getRootScope();
- if (scope == rootScope) {
- return;
- }
// Visit my idents.
int curId = maxChildId;
for (Iterator<JsName> it = scope.getAllNames(); it.hasNext();) {
JsName name = it.next();
+ if (!referenced.contains(name)) {
+ // Don't allocate idents for non-referenced names.
+ continue;
+ }
+
if (!name.isObfuscatable()) {
// Unobfuscatable names become themselves.
name.setShortIdent(name.getIdent());
@@ -138,4 +103,36 @@
maxChildId = Math.max(mySiblingsMaxId, curId);
}
-}
+
+ private boolean isLegal(JsScope scope, String newIdent) {
+ if (JsKeywords.isKeyword(newIdent)) {
+ return false;
+ }
+ /*
+ * Never obfuscate a name into an identifier that conflicts with an
existing
+ * unobfuscatable name! It's okay if it conflicts with an existing
+ * obfuscatable name, since that name will get obfuscated to something
else
+ * anyway.
+ */
+ return (scope.findExistingUnobfuscatableName(newIdent) == null);
+ }
+
+ private String makeObfuscatedIdent(int id) {
+ // Use base-54 for the first character of the identifier,
+ // so that we don't use any numbers (which are illegal at
+ // the beginning of an identifier).
+ //
+ int i = 0;
+ sIdentBuf[i++] = sBase64Chars[id % 54];
+ id /= 54;
+
+ // Use base-64 for the rest of the identifier.
+ //
+ while (id != 0) {
+ sIdentBuf[i++] = sBase64Chars[id & 0x3f];
+ id >>= 6;
+ }
+
+ return new String(sIdentBuf, 0, i);
+ }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/js/JsPrettyNamer.java Wed Feb 2
13:13:58 2011
+++ /trunk/dev/core/src/com/google/gwt/dev/js/JsPrettyNamer.java Fri Feb 4
12:13:45 2011
@@ -17,20 +17,17 @@
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
-import com.google.gwt.dev.js.ast.JsRootScope;
import com.google.gwt.dev.js.ast.JsScope;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.IdentityHashMap;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
/**
* A namer that uses short, readable idents to maximize reability.
*/
-public class JsPrettyNamer {
+public class JsPrettyNamer extends JsNamer {
public static void exec(JsProgram program) {
new JsPrettyNamer(program).execImpl();
@@ -41,46 +38,17 @@
*/
private Set<String> childIdents = null;
- private final JsProgram program;
-
- /**
- * A map containing the next integer to try as an identifier suffix for a
- * given JsScope.
- */
- private IdentityHashMap<JsScope, HashMap<String, Integer>>
startIdentForScope = new IdentityHashMap<JsScope, HashMap<String,
Integer>>();
-
public JsPrettyNamer(JsProgram program) {
- this.program = program;
+ super(program);
}
- private void execImpl() {
- visit(program.getRootScope());
+ @Override
+ protected void reset() {
+ childIdents = new HashSet<String>();
}
- private boolean isLegal(JsScope scope, Set<String> childIdents,
- String newIdent) {
- if (JsKeywords.isKeyword(newIdent)) {
- return false;
- }
- if (childIdents.contains(newIdent)) {
- // one of my children already claimed this ident
- return false;
- }
- /*
- * Never obfuscate a name into an identifier that conflicts with an
existing
- * unobfuscatable name! It's okay if it conflicts with an existing
- * obfuscatable name; that name will get obfuscated out of the way.
- */
- return (scope.findExistingUnobfuscatableName(newIdent) == null);
- }
-
- private void visit(JsScope scope) {
- HashMap<String, Integer> startIdent = startIdentForScope.get(scope);
- if (startIdent == null) {
- startIdent = new HashMap<String, Integer>();
- startIdentForScope.put(scope, startIdent);
- }
-
+ @Override
+ protected void visit(JsScope scope) {
// Save off the childIdents which is currently being computed for my
parent.
Set<String> myChildIdents = childIdents;
@@ -89,19 +57,22 @@
* clean slate: I do not communicate to my children.
*/
childIdents = new HashSet<String>();
- List<JsScope> children = scope.getChildren();
- for (Iterator<JsScope> it = children.iterator(); it.hasNext();) {
- visit(it.next());
- }
-
- JsRootScope rootScope = program.getRootScope();
- if (scope == rootScope) {
- return;
- }
+ for (JsScope child : scope.getChildren()) {
+ visit(child);
+ }
+ // Child idents now contains all idents my children are using.
+
+ // The next integer to try as an identifier suffix.
+ HashMap<String, Integer> startIdent = new HashMap<String, Integer>();
// Visit all my idents.
for (Iterator<JsName> it = scope.getAllNames(); it.hasNext();) {
JsName name = it.next();
+ if (!referenced.contains(name)) {
+ // Don't allocate idents for non-referenced names.
+ continue;
+ }
+
if (!name.isObfuscatable()) {
// Unobfuscatable names become themselves.
name.setShortIdent(name.getIdent());
@@ -130,4 +101,21 @@
myChildIdents.addAll(childIdents);
childIdents = myChildIdents;
}
-}
+
+ private boolean isLegal(JsScope scope, Set<String> childIdents,
+ String newIdent) {
+ if (JsKeywords.isKeyword(newIdent)) {
+ return false;
+ }
+ if (childIdents.contains(newIdent)) {
+ // one of my children already claimed this ident
+ return false;
+ }
+ /*
+ * Never obfuscate a name into an identifier that conflicts with an
existing
+ * unobfuscatable name! It's okay if it conflicts with an existing
+ * obfuscatable name; that name will get obfuscated out of the way.
+ */
+ return (scope.findExistingUnobfuscatableName(newIdent) == null);
+ }
+}
=======================================
--- /trunk/dev/core/src/com/google/gwt/dev/js/JsVerboseNamer.java Tue Aug
28 17:45:34 2007
+++ /trunk/dev/core/src/com/google/gwt/dev/js/JsVerboseNamer.java Fri Feb
4 12:13:45 2011
@@ -17,52 +17,44 @@
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
-import com.google.gwt.dev.js.ast.JsRootScope;
import com.google.gwt.dev.js.ast.JsScope;
import java.util.Iterator;
-import java.util.List;
/**
* A namer that uses long, fully qualified names for maximum unambiguous
* debuggability.
*/
-public class JsVerboseNamer {
+public class JsVerboseNamer extends JsNamer {
public static void exec(JsProgram program) {
new JsVerboseNamer(program).execImpl();
}
-
- private final JsProgram program;
public JsVerboseNamer(JsProgram program) {
- this.program = program;
+ super(program);
}
- private void execImpl() {
- visit(program.getRootScope());
+ @Override
+ protected void reset() {
+ // Nothing to do.
}
- private boolean isLegal(String newIdent) {
- // only keywords are forbidden
- return !JsKeywords.isKeyword(newIdent);
- }
-
- private void visit(JsScope scope) {
+ @Override
+ protected void visit(JsScope scope) {
// Visit children.
- List<JsScope> children = scope.getChildren();
- for (Iterator<JsScope> it = children.iterator(); it.hasNext();) {
- visit(it.next());
- }
-
- JsRootScope rootScope = program.getRootScope();
- if (scope == rootScope) {
- return;
+ for (JsScope child : scope.getChildren()) {
+ visit(child);
}
// Visit all my idents.
for (Iterator<JsName> it = scope.getAllNames(); it.hasNext();) {
JsName name = it.next();
+ if (!referenced.contains(name)) {
+ // Don't allocate idents for non-referenced names.
+ continue;
+ }
+
if (!name.isObfuscatable()) {
// Unobfuscatable names become themselves.
name.setShortIdent(name.getIdent());
@@ -85,4 +77,9 @@
}
}
}
-}
+
+ private boolean isLegal(String newIdent) {
+ // only keywords are forbidden
+ return !JsKeywords.isKeyword(newIdent);
+ }
+}
--
http://groups.google.com/group/Google-Web-Toolkit-Contributors