Roll seed-function optimization forward again.
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10480 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java b/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
index 368acec..97cd1d7 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
@@ -108,6 +108,11 @@
int getQueryId();
/**
+ * Returns the seedId for types.
+ */
+ int getSeedId();
+
+ /**
* Returns the line number on which the symbol was originally declared or
* <code>-1</code> if the line number is unknown.
*/
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
index fc57185..78cfb0c 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
@@ -32,15 +32,15 @@
public class StandardSymbolData implements SymbolData {
public static StandardSymbolData forClass(String className, String uriString,
- int lineNumber, int queryId, CastableTypeMap castableTypeMap) {
+ int lineNumber, int queryId, CastableTypeMap castableTypeMap, int seedId) {
return new StandardSymbolData(className, null, null, uriString, lineNumber,
- queryId, castableTypeMap);
+ queryId, castableTypeMap, seedId);
}
public static StandardSymbolData forMember(String className,
String memberName, String methodSig, String uriString, int lineNumber) {
return new StandardSymbolData(className, memberName, methodSig, uriString,
- lineNumber, 0, null);
+ lineNumber, 0, null, -1);
}
public static String toUriString(String fileName) {
@@ -63,11 +63,12 @@
private String sourceUri;
private String symbolName;
private int queryId;
+ private int seedId;
private CastableTypeMap castableTypeMap;
private StandardSymbolData(String className, String memberName,
String methodSig, String sourceUri, int sourceLine, int queryId,
- CastableTypeMap castableTypeMap) {
+ CastableTypeMap castableTypeMap, int seedId) {
assert className != null && className.length() > 0 : "className";
assert memberName != null || methodSig == null : "methodSig without memberName";
assert sourceLine >= -1 : "sourceLine: " + sourceLine;
@@ -79,6 +80,7 @@
this.sourceLine = sourceLine;
this.queryId = queryId;
this.castableTypeMap = castableTypeMap;
+ this.seedId = seedId;
}
public CastableTypeMap getCastableTypeMap() {
@@ -107,6 +109,10 @@
return queryId;
}
+ public int getSeedId() {
+ return seedId;
+ }
+
public int getSourceLine() {
return sourceLine;
}
@@ -161,6 +167,7 @@
symbolName = in.readUTF();
queryId = in.readInt();
castableTypeMap = (CastableTypeMap) in.readObject();
+ seedId = in.readInt();
}
/**
@@ -189,5 +196,6 @@
out.writeUTF(symbolName);
out.writeInt(queryId);
out.writeObject(castableTypeMap);
+ out.writeInt(seedId);
}
}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
index 47906e4..a32d252 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
@@ -22,9 +22,6 @@
import com.google.gwt.core.ext.TreeLogger;
import java.io.Serializable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.TreeSet;
/**
@@ -155,7 +152,7 @@
possibleValues.add(v);
}
return new DefaultSelectionProperty(value, prop.getFallback(), name,
- possibleValues, (Map<String, List<Set<String>>>) prop.getFallbackValuesMap());
+ possibleValues, prop.getFallbackValuesMap());
}
}
diff --git a/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java b/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
index ee1c256..ea413fb 100644
--- a/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
+++ b/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
@@ -302,11 +302,12 @@
StringBuilder code = new StringBuilder();
code.append("package java.lang;\n");
code.append("public class Object {\n");
+ code.append(" private Class<?> ___clazz;");
code.append(" public boolean equals(Object that){return this == that;}");
code.append(" public int hashCode() { return 0; }\n");
code.append(" public String toString() { return \"Object\"; }\n");
code.append(" public Object clone() { return this; } ");
- code.append(" public Class<?> getClass() { return Object.class; } ");
+ code.append(" public Class<?> getClass() { return ___clazz; } ");
code.append("}\n");
return code;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index bac3201..67d86c7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -99,6 +99,7 @@
import com.google.gwt.dev.jjs.impl.Pruner;
import com.google.gwt.dev.jjs.impl.RecordRebinds;
import com.google.gwt.dev.jjs.impl.RemoveEmptySuperCalls;
+import com.google.gwt.dev.jjs.impl.ReplaceGetClassOverrides;
import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs;
import com.google.gwt.dev.jjs.impl.ResolveRebinds;
@@ -307,6 +308,8 @@
// (6) Perform further post-normalization optimizations
// Prune everything
Pruner.exec(jprogram, false);
+ // prune all Object.getClass() overrides and replace with inline field ref
+ ReplaceGetClassOverrides.exec(jprogram);
// (7) Generate a JavaScript code DOM from the Java type declarations
jprogram.typeOracle.recomputeAfterOptimizations();
@@ -335,7 +338,7 @@
/*
* Creates new variables, must run before code splitter and namer.
*/
- JsStackEmulator.exec(jsProgram, propertyOracles);
+ JsStackEmulator.exec(jprogram, jsProgram, propertyOracles, jjsmap);
/*
* Work around Safari 5 bug by rewriting a >> b as ~~a >> b.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index dfb9119..63351a5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -64,6 +64,21 @@
"com.google.gwt.lang.CollapsedPropertyHolder", "com.google.gwt.lang.Exceptions",
"com.google.gwt.lang.LongLib", "com.google.gwt.lang.Stats", "com.google.gwt.lang.Util"));
+ /*
+ * Types which are not referenced by any Java code, but are required to exist
+ * after Java optimizations have run in order to be used by backend
+ * code-generation. These classes and their members, are considered live
+ * by ControlFlowAnalysis, at all times. Immortal types always live in the
+ * initial fragment and their definitions are hoisted to appear before all
+ * other types. Only static methods and fields are allowed, and no clinits
+ * are run. Field initializers must be primitives, literals, or one of
+ * JSO.createObject() or JSO.createArray().
+ *
+ * Classes are inserted into the JsAST in the order they appear in the Set.
+ */
+ public static final Set<String> IMMORTAL_CODEGEN_TYPES_SET = new LinkedHashSet<String>(Arrays.asList(
+ "com.google.gwt.lang.SeedUtil"));
+
public static final Set<String> INDEX_TYPES_SET = new LinkedHashSet<String>(Arrays.asList(
"java.io.Serializable", "java.lang.Object", "java.lang.String", "java.lang.Class",
"java.lang.CharSequence", "java.lang.Cloneable", "java.lang.Comparable", "java.lang.Enum",
@@ -98,6 +113,7 @@
new HashMap<String, JPrimitiveType>();
static {
+ CODEGEN_TYPES_SET.addAll(IMMORTAL_CODEGEN_TYPES_SET);
INDEX_TYPES_SET.addAll(CODEGEN_TYPES_SET);
/*
@@ -302,6 +318,7 @@
}
public final List<JClassType> codeGenTypes = new ArrayList<JClassType>();
+ public final List<JClassType> immortalCodeGenTypes = new ArrayList<JClassType>();
public final JTypeOracle typeOracle = new JTypeOracle(this);
@@ -314,6 +331,8 @@
private IdentityHashMap<JReferenceType, JsCastMap> castMaps;
+ private Map<JType, JField> classLiteralFields;
+
/**
* A factory to create correlations.
*/
@@ -391,6 +410,11 @@
if (CODEGEN_TYPES_SET.contains(name)) {
codeGenTypes.add((JClassType) type);
}
+
+ if (IMMORTAL_CODEGEN_TYPES_SET.contains(name)) {
+ immortalCodeGenTypes.add((JClassType) type);
+ }
+
if (INDEX_TYPES_SET.contains(name)) {
indexedTypes.put(type.getShortName(), type);
for (JMethod method : type.getMethods()) {
@@ -730,6 +754,10 @@
return castMaps.get(referenceType);
}
+ public JField getClassLiteralField(JType type) {
+ return classLiteralFields.get(isJavaScriptObject(type) ? getJavaScriptObject() : type);
+ }
+
public String getClassLiteralName(JType type) {
return type.getJavahSignatureName() + "_classLit";
}
@@ -1011,6 +1039,10 @@
}
}
+ public void recordClassLiteralFields(Map<JType, JField> classLiteralFields) {
+ this.classLiteralFields = classLiteralFields;
+ }
+
public void recordQueryIds(Map<JReferenceType, Integer> queryIdsByType,
List<JReferenceType> typesByQueryId) {
this.queryIdsByType = queryIdsByType;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JSeedIdOf.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JSeedIdOf.java
new file mode 100644
index 0000000..ab1ca95
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JSeedIdOf.java
@@ -0,0 +1,36 @@
+/*
+ * 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.jjs.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * An AST node whose evaluation results in the seedId of its node.
+ */
+public class JSeedIdOf extends JNameOf {
+
+ public JSeedIdOf(SourceInfo info, JClassType stringType, HasName node) {
+ super(info, stringType, node);
+ }
+
+ public void traverse(JVisitor visitor, Context ctx) {
+ if (visitor.visit(this, ctx)) {
+ // Intentionally not visiting referenced node
+ }
+ visitor.endVisit(this, ctx);
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index b9095eb..8850cb6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -565,6 +565,10 @@
return results;
}
+ public Set<JReferenceType> getInstantiatedTypes() {
+ return instantiatedTypes;
+ }
+
public JMethod getPolyMethod(JClassType type, String signature) {
return getOrCreatePolyMap(type).get(signature);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
index 885d8e7..8e17e97 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -417,6 +417,10 @@
endVisit((JExpression) x, ctx);
}
+ public void endVisit(JSeedIdOf x, Context ctx) {
+ endVisit((JNameOf) x, ctx);
+ }
+
public void endVisit(JsCastMap x, Context ctx) {
endVisit((JsonArray) x, ctx);
}
@@ -741,6 +745,10 @@
return visit((JExpression) x, ctx);
}
+ public boolean visit(JSeedIdOf x, Context ctx) {
+ return visit((JNameOf) x, ctx);
+ }
+
public boolean visit(JsCastMap x, Context ctx) {
return visit((JsonArray) x, ctx);
}
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 7730084..4c85a0a 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
@@ -24,6 +24,7 @@
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
@@ -454,7 +455,7 @@
cfa.setDependencyRecorder(dependencyRecorder);
cfa.traverseEntryMethods();
traverseClassArray(jprogram, cfa);
-
+ traverseImmortalTypes(jprogram, cfa);
dependencyRecorder.endDependencyGraph();
return cfa;
}
@@ -580,6 +581,21 @@
}
}
+ /**
+ * Any immortal codegen types must be part of the initial download.
+ */
+ private static void traverseImmortalTypes(JProgram jprogram,
+ ControlFlowAnalyzer cfa) {
+ for (JClassType type : jprogram.immortalCodeGenTypes) {
+ cfa.traverseFromInstantiationOf(type);
+ for (JMethod method : type.getMethods()) {
+ if (!method.needsVtable()) {
+ cfa.traverseFrom(method);
+ }
+ }
+ }
+ }
+
private static <T> Set<T> union(Set<? extends T> set1, Set<? extends T> set2) {
Set<T> union = new HashSet<T>();
union.addAll(set1);
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 36c534f..f9b3aaa 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
@@ -518,6 +518,16 @@
return false;
}
+ private void maybeRescueClassLiteral(JReferenceType type) {
+ if (liveFieldsAndMethods.contains(getClassMethod) || liveFieldsAndMethods.contains(getClassField)) {
+ // getClass() already live so rescue class literal immediately
+ rescue(program.getClassLiteralField(type));
+ } else {
+ // getClass() not live yet, so mark for later rescue
+ classLiteralsToBeRescuedIfGetClassIsLive.add(type);
+ }
+ }
+
/**
* Subclasses of JavaScriptObject are never instantiated directly. They are
* implicitly created when a JSNI method passes a reference to an existing
@@ -574,6 +584,9 @@
maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
}
rescueOverridingMethods(method);
+ if (method == getClassMethod) {
+ rescueClassLiteralsIfGetClassIsLive();
+ }
return true;
}
}
@@ -594,6 +607,8 @@
boolean doVisit = false;
if (isInstantiated && !instantiatedTypes.contains(type)) {
instantiatedTypes.add(type);
+ maybeRescueClassLiteral(type);
+
doVisit = true;
}
@@ -623,6 +638,10 @@
if (var != null) {
if (liveFieldsAndMethods.add(var)) {
membersToRescueIfTypeIsInstantiated.remove(var);
+ if (var == getClassField) {
+ rescueClassLiteralsIfGetClassIsLive();
+ }
+
if (isStaticFieldInitializedToLiteral(var)) {
/*
* Rescue literal initializers when the field is rescued, not when
@@ -746,6 +765,18 @@
}
}
+ private void rescueClassLiteralsIfGetClassIsLive() {
+ if (classLiteralsToBeRescuedIfGetClassIsLive != null) {
+ // guard against re-entrant calls. This only needs to run once.
+ Set<JReferenceType> toRescue = classLiteralsToBeRescuedIfGetClassIsLive;
+ classLiteralsToBeRescuedIfGetClassIsLive = null;
+
+ for (JReferenceType classLit : toRescue) {
+ maybeRescueClassLiteral(classLit);
+ }
+ }
+ }
+
/**
* If the type is instantiable, rescue any of its virtual methods that a
* previously seen method call could call.
@@ -807,6 +838,13 @@
private final JMethod asyncFragmentOnLoad;
private final JDeclaredType baseArrayType;
+
+ /**
+ * Schrodinger set of classLiterals to be rescued if type is instantiated AND getClass()
+ * is live.
+ */
+ private Set<JReferenceType> classLiteralsToBeRescuedIfGetClassIsLive = new HashSet<JReferenceType>();
+
private DependencyRecorder dependencyRecorder;
private Set<JField> fieldsWritten = new HashSet<JField>();
private Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
@@ -827,6 +865,8 @@
*/
private Map<JMethod, List<JMethod>> methodsThatOverrideMe;
+ private final JField getClassField;
+ private final JMethod getClassMethod;
private final JProgram program;
private Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
private final RescueVisitor rescuer = new RescueVisitor();
@@ -851,6 +891,8 @@
new HashMap<JParameter, List<JExpression>>(cfa.argsToRescueIfParameterRead);
}
methodsThatOverrideMe = cfa.methodsThatOverrideMe;
+ getClassField = program.getIndexedField("Object.___clazz");
+ getClassMethod = program.getIndexedMethod("Object.getClass");
}
public ControlFlowAnalyzer(JProgram program) {
@@ -858,6 +900,8 @@
asyncFragmentOnLoad = program.getIndexedMethod("AsyncFragmentLoader.onLoad");
runAsyncOnsuccess = program.getIndexedMethod("RunAsyncCallback.onSuccess");
baseArrayType = program.getIndexedType("Array");
+ getClassField = program.getIndexedField("Object.___clazz");
+ getClassMethod = program.getIndexedMethod("Object.getClass");
buildMethodsOverriding();
}
@@ -964,6 +1008,15 @@
runAsync.traverseOnSuccess(rescuer);
}
+ /**
+ * Traverse the fragments for all runAsyncs.
+ */
+ public void traverseFromRunAsyncs() {
+ for (JRunAsync runAsync : program.getRunAsyncs()) {
+ traverseFromRunAsync(runAsync);
+ }
+ }
+
private void buildMethodsOverriding() {
methodsThatOverrideMe = new HashMap<JMethod, List<JMethod>>();
for (JDeclaredType type : program.getDeclaredTypes()) {
@@ -979,13 +1032,4 @@
}
}
}
-
- /**
- * Traverse the fragments for all runAsyncs.
- */
- private void traverseFromRunAsyncs() {
- for (JRunAsync runAsync : program.getRunAsyncs()) {
- traverseFromRunAsync(runAsync);
- }
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
index a522b6e..8d75fc9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
@@ -31,15 +31,15 @@
import com.google.gwt.dev.js.ast.JsExpression;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsInvocation;
+import com.google.gwt.dev.js.ast.JsModVisitor;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNameRef;
-import com.google.gwt.dev.js.ast.JsNew;
import com.google.gwt.dev.js.ast.JsNumberLiteral;
-import com.google.gwt.dev.js.ast.JsObjectLiteral;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsVars;
import com.google.gwt.dev.js.ast.JsVars.JsVar;
+import com.google.gwt.dev.js.ast.JsVisitable;
import java.util.ArrayList;
import java.util.Collections;
@@ -238,21 +238,31 @@
* The type whose vtables can currently be installed.
*/
JClassType currentVtableType = null;
+ JClassType pendingVtableType = null;
+ JsExprStmt pendingDefineSeed = null;
- // Since we haven't run yet.
+
+ // Since we haven't run yet.
assert jsprogram.getFragmentCount() == 1;
+
List<JsStatement> stats = jsprogram.getGlobalBlock().getStatements();
for (JsStatement stat : stats) {
+
boolean keepIt;
JClassType vtableTypeAssigned = vtableTypeAssigned(stat);
- if (vtableTypeAssigned != null && livenessPredicate.isLive(vtableTypeAssigned)) {
- JsExprStmt result =
- extractPrototypeSetup(livenessPredicate, alreadyLoadedPredicate, stat,
- vtableTypeAssigned);
- if (result != null) {
+ if (vtableTypeAssigned != null
+ && livenessPredicate.isLive(vtableTypeAssigned)) {
+ boolean[] anyCtorsSetup = new boolean[1];
+ JsExprStmt result = maybeRemoveCtorsFromDefineSeedStmt(livenessPredicate,
+ alreadyLoadedPredicate, stat, anyCtorsSetup);
+ boolean anyWorkDone = anyCtorsSetup[0]
+ || !alreadyLoadedPredicate.isLive(vtableTypeAssigned);
+ if (anyWorkDone) {
stat = result;
keepIt = true;
} else {
+ pendingDefineSeed = result;
+ pendingVtableType = vtableTypeAssigned;
keepIt = false;
}
} else if (containsRemovableVars(stat)) {
@@ -270,8 +280,11 @@
}
JClassType vtableType = vtableTypeNeeded(stat);
if (vtableType != null && vtableType != currentVtableType) {
- extractedStats.add(vtableStatFor(vtableType));
- currentVtableType = vtableType;
+ assert pendingVtableType == vtableType;
+ extractedStats.add(pendingDefineSeed);
+ currentVtableType = pendingVtableType;
+ pendingDefineSeed = null;
+ pendingVtableType = null;
}
extractedStats.add(stat);
}
@@ -323,57 +336,6 @@
return false;
}
- /**
- * Weird case: the seed function's liveness is associated with the type
- * itself. However, individual constructors can have a liveness that is a
- * subset of the type's liveness. We essentially have to break up the
- * prototype chain according to exactly what's newly live.
- */
- private JsExprStmt extractPrototypeSetup(final LivenessPredicate livenessPredicate,
- final LivenessPredicate alreadyLoadedPredicate, JsStatement stat,
- final JClassType vtableTypeAssigned) {
- final boolean[] anyLiveCode = new boolean[1];
- Cloner c = new Cloner() {
- @Override
- public void endVisit(JsBinaryOperation x, JsContext ctx) {
- JsExpression rhs = stack.pop();
- JsNameRef lhs = (JsNameRef) stack.pop();
- if (rhs instanceof JsNew || rhs instanceof JsObjectLiteral) {
- // The super op is being assigned to the seed prototype.
- if (alreadyLoadedPredicate.isLive(vtableTypeAssigned)) {
- stack.push(lhs);
- return;
- } else {
- anyLiveCode[0] = true;
- }
- } else if (lhs.getQualifier() == null) {
- // The underscore is being assigned to.
- assert "_".equals(lhs.getIdent());
- } else {
- // A constructor function is being assigned to.
- assert "prototype".equals(lhs.getIdent());
- JsNameRef ctorRef = (JsNameRef) lhs.getQualifier();
- JConstructor ctor = (JConstructor) map.nameToMethod(ctorRef.getName());
- assert ctor != null;
- if (livenessPredicate.isLive(ctor) && !alreadyLoadedPredicate.isLive(ctor)) {
- anyLiveCode[0] = true;
- } else {
- stack.push(rhs);
- return;
- }
- }
-
- JsBinaryOperation toReturn = new JsBinaryOperation(x.getSourceInfo(), x.getOperator());
- toReturn.setArg2(rhs);
- toReturn.setArg1(lhs);
- stack.push(toReturn);
- }
- };
- c.accept(((JsExprStmt) stat).getExpression());
- JsExprStmt result = anyLiveCode[0] ? c.getExpression().makeStmt() : null;
- return result;
- }
-
private boolean isLive(JsStatement stat, LivenessPredicate livenessPredicate) {
JClassType type = map.typeForStatement(stat);
if (type != null) {
@@ -420,6 +382,42 @@
}
/**
+ * Weird case: the seed function's liveness is associated with the type
+ * itself. However, individual constructors can have a liveness that is a
+ * subset of the type's liveness.
+ */
+ private JsExprStmt maybeRemoveCtorsFromDefineSeedStmt(
+ final LivenessPredicate livenessPredicate,
+ final LivenessPredicate alreadyLoadedPredicate, JsStatement stat,
+ final boolean[] anyCtorsSetup) {
+ Cloner c = new Cloner();
+ c.accept(((JsExprStmt) stat).getExpression());
+ JsExprStmt result = c.getExpression().makeStmt();
+ new JsModVisitor() {
+ public void endVisit(JsNameRef x, JsContext ctx) {
+ JMethod maybeCtor = map.nameToMethod(x.getName());
+ if (maybeCtor instanceof JConstructor) {
+ JConstructor ctor = (JConstructor) maybeCtor;
+ if (!livenessPredicate.isLive(ctor)
+ || alreadyLoadedPredicate.isLive(ctor)) {
+ ctx.removeMe();
+ } else {
+ anyCtorsSetup[0] = true;
+ }
+ }
+ };
+
+ /**
+ * Overridden to allow insert/remove on the varargs portion.
+ */
+ protected <T extends JsVisitable> void doAcceptList(List<T> collection) {
+ doAcceptWithInsertRemove(collection);
+ };
+ }.accept(result);
+ return result;
+ }
+
+ /**
* Return the Java method corresponding to <code>stat</code>, or
* <code>null</code> if there isn't one. It recognizes JavaScript of the form
* <code>function foo(...) { ...}</code>, where <code>foo</code> is the name
@@ -465,40 +463,30 @@
}
/**
- * Compute a statement that can be used to set up for installing instance
- * methods into a vtable. It will be of the form
- * <code>_ = foo.prototype</code>, where <code>foo</code> is the constructor
- * function for <code>vtableType</code>.
- */
- private JsStatement vtableStatFor(JClassType vtableType) {
- JsNameRef prototypeField =
- new JsNameRef(jsprogram.createSourceInfoSynthetic(FragmentExtractor.class), "prototype");
- JsExpression constructorRef;
- SourceInfo sourceInfoVtableSetup = jsprogram.createSourceInfoSynthetic(FragmentExtractor.class);
- if (vtableType == jprogram.getTypeJavaLangString()) {
- // The methods of java.lang.String are put onto JavaScript's String
- // prototype
- SourceInfo sourceInfoConstructorRef =
- jsprogram.createSourceInfoSynthetic(FragmentExtractor.class);
- constructorRef = new JsNameRef(sourceInfoConstructorRef, "String");
- } else {
- constructorRef = map.nameForType(vtableType).makeRef(sourceInfoVtableSetup);
- }
- prototypeField.setQualifier(constructorRef);
- SourceInfo underlineSourceInfo = jsprogram.createSourceInfoSynthetic(FragmentExtractor.class);
- return (new JsBinaryOperation(sourceInfoVtableSetup, JsBinaryOperator.ASG, jsprogram.getScope()
- .declareName("_").makeRef(underlineSourceInfo), prototypeField)).makeStmt();
- }
-
- /**
- * If <code>state</code> is of the form <code>_ = Foo.prototype = exp</code>,
- * then return <code>Foo</code>. Otherwise return <code>null</code>.
+ * If <code>state</code> is of the form <code>_ = String.prototype</code>,
+ * then return <code>String</code>. If the form is
+ * <code>defineSeed(id, superId, cTM, ctor1, ctor2, ...)</code> return the type
+ * corresponding to that id. Otherwise return <code>null</code>.
*/
private JClassType vtableTypeAssigned(JsStatement stat) {
if (!(stat instanceof JsExprStmt)) {
return null;
}
JsExprStmt expr = (JsExprStmt) stat;
+ if (expr.getExpression() instanceof JsInvocation) {
+ // Handle a defineSeed call.
+ JsInvocation call = (JsInvocation) expr.getExpression();
+ if (!(call.getQualifier() instanceof JsNameRef)) {
+ return null;
+ }
+ JsNameRef func = (JsNameRef) call.getQualifier();
+ if (func.getName() != jsprogram.getIndexedFunction("SeedUtil.defineSeed").getName()) {
+ return null;
+ }
+ return map.typeForStatement(stat);
+ }
+
+ // Handle String.
if (!(expr.getExpression() instanceof JsBinaryOperation)) {
return null;
}
@@ -510,30 +498,26 @@
return null;
}
JsNameRef lhs = (JsNameRef) binExpr.getArg1();
- if (lhs.getQualifier() != null) {
+ JsName underBar = jsprogram.getScope().findExistingName("_");
+ assert underBar != null;
+ if (lhs.getName() != underBar) {
return null;
}
- if (lhs.getName() == null) {
- return null;
- }
- if (!lhs.getName().getShortIdent().equals("_")) {
- return null;
- }
- if (!(binExpr.getArg2() instanceof JsBinaryOperation)) {
- return null;
- }
- JsBinaryOperation binExprRhs = (JsBinaryOperation) binExpr.getArg2();
- if (binExprRhs.getOperator() != JsBinaryOperator.ASG) {
- return null;
- }
- if (!(binExprRhs.getArg1() instanceof JsNameRef)) {
- return null;
- }
- JsNameRef middleNameRef = (JsNameRef) binExprRhs.getArg1();
- if (!middleNameRef.getName().getShortIdent().equals("prototype")) {
+ if (!(binExpr.getArg2() instanceof JsNameRef)) {
return null;
}
+ JsNameRef rhsRef = (JsNameRef) binExpr.getArg2();
+ if (!(rhsRef.getQualifier() instanceof JsNameRef)) {
+ return null;
+ }
+ if (!((JsNameRef) rhsRef.getQualifier()).getShortIdent().equals("String")) {
+ return null;
+ }
+
+ if (!rhsRef.getName().getShortIdent().equals("prototype")) {
+ return null;
+ }
return map.typeForStatement(stat);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index 87a6aa7..3c4883f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -514,14 +514,17 @@
// Just use JavaScriptObject's implementation for all subclasses.
currentClass.getMethods().remove(2);
} else {
- tryFindUpRefs(method);
- SourceInfo info = method.getSourceInfo();
- if (isScript(program) && currentClass == program.getIndexedType("Array")) {
- // Special implementation: return this.arrayClass
- implementMethod(method, new JFieldRef(info, new JThisRef(info,
- (JClassType) currentClass), program.getIndexedField("Array.arrayClass"),
- currentClass));
+ if (currentClass == program.getIndexedType("Array")) {
+ /*
+ * Don't implement, fall through to Object.getClass(). Array emulation code
+ * in com.google.gwt.lang.Array invokes Array.getClass() and expects to get the
+ * class literal for the actual runtime type of the array (e.g. Foo[].class) and
+ * not Array.class.
+ */
+ currentClass.getMethods().remove(2);
} else {
+ tryFindUpRefs(method);
+ SourceInfo info = method.getSourceInfo();
implementMethod(method, new JClassLiteral(info.makeChild(), currentClass));
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 8cfa63e..c599720 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -76,6 +76,7 @@
import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
import com.google.gwt.dev.jjs.ast.JStatement;
import com.google.gwt.dev.jjs.ast.JSwitchStatement;
import com.google.gwt.dev.jjs.ast.JThisRef;
@@ -138,6 +139,7 @@
import com.google.gwt.dev.js.ast.JsReturn;
import com.google.gwt.dev.js.ast.JsRootScope;
import com.google.gwt.dev.js.ast.JsScope;
+import com.google.gwt.dev.js.ast.JsSeedIdOf;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsSwitch;
import com.google.gwt.dev.js.ast.JsSwitchMember;
@@ -388,7 +390,7 @@
* as var foo = function blah() {} and introduce a separate scope for
* the function's name according to EcmaScript-262, but this would mess
* up stack traces by allowing two inner scope function names to
- * onfuscate to the same identifier, making function names no longer a
+ * obfuscate to the same identifier, making function names no longer a
* 1:1 mapping to obfuscated symbols. Leaving them in global scope
* causes no harm.
*/
@@ -405,6 +407,7 @@
Maps.put(indexedFunctions, x.getEnclosingType().getShortName() + "." + x.getName(),
jsFunction);
}
+
return true;
}
@@ -499,7 +502,8 @@
StandardSymbolData symbolData =
StandardSymbolData.forClass(x.getName(), x.getSourceInfo().getFileName(), x
- .getSourceInfo().getStartLine(), program.getQueryId(x), castableTypeMap);
+ .getSourceInfo().getStartLine(), program.getQueryId(x), castableTypeMap,
+ x instanceof JClassType || x instanceof JArrayType ? getSeedId(x) : -1);
assert !symbolTable.containsKey(symbolData);
symbolTable.put(symbolData, jsName);
}
@@ -568,6 +572,7 @@
JsName name = topScope.declareName("Q$" + longName, "Q$" + shortName);
namesByQueryId.add(name);
}
+ // TODO(cromwellian): see about moving this into an immortal type
StringBuilder sb = new StringBuilder();
sb.append("function makeCastMap(a) {");
sb.append(" var result = {};");
@@ -607,12 +612,16 @@
private final JsName prototype = objectScope.declareName("prototype");
+
{
globalTemp.setObfuscatable(false);
prototype.setObfuscatable(false);
arrayLength.setObfuscatable(false);
}
+ public GenerateJavaScriptVisitor() {
+ }
+
@Override
public void endVisit(JAbsentArrayDimension x, Context ctx) {
throw new InternalCompilerException("Should not get here.");
@@ -719,6 +728,11 @@
return;
}
+ if (program.immortalCodeGenTypes.contains(x)) {
+ // Handled in generateImmortalTypes
+ return;
+ }
+
alreadyRan.add(x);
List<JsFunction> jsFuncs = popList(x.getMethods().size()); // methods
@@ -738,6 +752,7 @@
// declare all methods into the global scope
for (int i = 0; i < jsFuncs.size(); ++i) {
JsFunction func = jsFuncs.get(i);
+
// don't add polymorphic JsFuncs, inline decl into vtable assignment
if (func != null && !polymorphicJsFunctions.contains(func)) {
globalStmts.add(func.makeStmt());
@@ -1260,7 +1275,7 @@
// Long lits must go at the top, they can be constant field initializers.
generateLongLiterals(vars);
-
+ generateImmortalTypes(vars);
generateQueryIdConstants(vars);
// Class objects, but only if there are any.
@@ -1298,6 +1313,12 @@
}
@Override
+ public void endVisit(JSeedIdOf x, Context ctx) {
+ JsName name = names.get(x.getNode());
+ push(new JsSeedIdOf(x.getSourceInfo(), name, getSeedId((JReferenceType) x.getNode())));
+ }
+
+ @Override
public void endVisit(JsCastMap x, Context ctx) {
super.endVisit(x, ctx);
JsArrayLiteral arrayLit = (JsArrayLiteral) pop();
@@ -1418,6 +1439,11 @@
return false;
}
+ if (program.immortalCodeGenTypes.contains(x)) {
+ // Handled in generateImmortalTypes
+ return false;
+ }
+
// force super type to generate code first, this is required for prototype
// chaining to work properly
if (x.getSuperClass() != null && !alreadyRan.contains(x)) {
@@ -1454,6 +1480,7 @@
for (int i = 0; i < entryMethods.size(); i++) {
entryMethodToIndex.put(entryMethods.get(i), i);
}
+
return true;
}
@@ -1647,27 +1674,20 @@
return new JsBinaryOperation(lhs.getSourceInfo(), JsBinaryOperator.COMMA, lhs, rhs);
}
- private void generateCastableTypeMap(JClassType x, List<JsStatement> globalStmts) {
+ private JsExpression generateCastableTypeMap(JClassType x) {
JsCastMap castMap = program.getCastMap(x);
if (castMap != null) {
JField castableTypeMapField = program.getIndexedField("Object.castableTypeMap");
JsName castableTypeMapName = names.get(castableTypeMapField);
if (castableTypeMapName == null) {
// Was pruned; this compilation must have no dynamic casts.
- return;
+ return new JsObjectLiteral(SourceOrigin.UNKNOWN);
}
- // Generate castableTypeMap for each type prototype
- // _.castableTypeMap$ = ...
- SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic(GenerateJavaScriptAST.class);
- JsNameRef fieldRef = castableTypeMapName.makeRef(sourceInfo);
- fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
accept(castMap);
- JsExpression asg = createAssignment(fieldRef, (JsExpression) pop());
- JsExprStmt asgStmt = asg.makeStmt();
- globalStmts.add(asgStmt);
- typeForStatMap.put(asgStmt, x);
+ return (JsExpression) pop();
}
+ return new JsObjectLiteral(SourceOrigin.UNKNOWN);
}
private void generateClassLiteral(JDeclarationStatement decl, JsVars vars) {
@@ -1704,8 +1724,6 @@
// special: setup the identifying typeMarker field
generateTypeMarker(globalStmts);
}
-
- generateCastableTypeMap(x, globalStmts);
}
private void generateGwtOnLoad(List<JsFunction> entryFuncs, List<JsStatement> globalStmts) {
@@ -1806,6 +1824,78 @@
errCall.getArguments().add(modName.makeRef(sourceInfo));
}
+ private void generateImmortalTypes(JsVars globals) {
+ List<JsStatement> globalStmts = jsProgram.getGlobalBlock().getStatements();
+ List<JClassType> immortalTypes = new ArrayList<JClassType>(
+ program.immortalCodeGenTypes);
+ // visit in reverse order since insertions start at head
+ Collections.reverse(immortalTypes);
+ JMethod createObjMethod = program.getIndexedMethod("JavaScriptObject.createObject");
+ JMethod createArrMethod = program.getIndexedMethod("JavaScriptObject.createArray");
+
+ for (JClassType x : immortalTypes) {
+ // should not be pruned
+ assert x.getMethods().size() > 0;
+ // insert all static methods
+ for (JMethod method : x.getMethods()) {
+ /*
+ * Skip virtual methods and constructors. Even in cases where there is no constructor
+ * defined, the compiler will synthesize a default constructor which invokes
+ * a synthensized $init() method. We must skip both of these inserted methods.
+ */
+ if (method.needsVtable() || method instanceof JConstructor) {
+ continue;
+ }
+ if (JProgram.isClinit(method)) {
+ /**
+ * Emit empty clinits that will be pruned. If a type B extends A, then even if
+ * B and A have no fields to initialize, there will be a call inserted in B's clinit
+ * to invoke A's clinit. Likewise, if you have a static field initialized to
+ * JavaScriptObject.createObject(), the clinit() will include this initializer code,
+ * which we don't want.
+ */
+ JsFunction func = new JsFunction(x.getSourceInfo(), topScope,
+ topScope.declareName(mangleNameForGlobal(method)), true);
+ func.setBody(new JsBlock(method.getBody().getSourceInfo()));
+ push(func);
+ } else {
+ accept(method);
+ }
+ // add after var declaration, but before everything else
+ JsFunction func = (JsFunction) pop();
+ assert func.getName() != null;
+ globalStmts.add(1, func.makeStmt());
+ }
+
+ // insert fields into global var declaration
+ for (JField field : x.getFields()) {
+ assert field.isStatic() : "All fields on immortal types must be static.";
+ accept(field);
+ JsNode node = pop();
+ assert node instanceof JsVar;
+ JsVar fieldVar = (JsVar) node;
+ JExpression init = field.getInitializer();
+ if (init != null
+ && field.getLiteralInitializer() == null) {
+ // no literal, but it could be a JavaScriptObject
+ if (init.getType() == program.getJavaScriptObject()) {
+ assert init instanceof JMethodCall;
+ JMethod meth = ((JMethodCall) init).getTarget();
+ // immortal types can only have non-primitive literal initializers of createArray,createObject
+ if (meth == createObjMethod) {
+ fieldVar.setInitExpr(new JsObjectLiteral(init.getSourceInfo()));
+ } else if (meth == createArrMethod) {
+ fieldVar.setInitExpr(new JsArrayLiteral(init.getSourceInfo()));
+ } else {
+ assert false : "Illegal initializer expression for immortal field " + field;
+ }
+ }
+ }
+ globals.add(fieldVar);
+ }
+ }
+ }
+
private void generateLongLiterals(JsVars vars) {
for (Entry<Long, JsName> entry : longLits.entrySet()) {
JsName jsName = entry.getValue();
@@ -1831,44 +1921,31 @@
private void generateSeedFuncAndPrototype(JClassType x, List<JsStatement> globalStmts) {
SourceInfo sourceInfo = x.getSourceInfo();
if (x != program.getTypeJavaLangString()) {
- JsName seedFuncName = names.get(x);
-
- // seed function
- // function com_example_foo_Foo() { }
- JsFunction seedFunc = new JsFunction(sourceInfo, topScope, seedFuncName, true);
- seedFuncName.setStaticRef(seedFunc);
- JsBlock body = new JsBlock(sourceInfo);
- seedFunc.setBody(body);
- JsExprStmt seedFuncStmt = seedFunc.makeStmt();
- globalStmts.add(seedFuncStmt);
- typeForStatMap.put(seedFuncStmt, x);
-
- // Setup prototype chain.
- // _ = Foo__V.prototype = FooSeed.prototype = new FooSuper();
- JsNameRef seedProtoRef = prototype.makeRef(sourceInfo);
- seedProtoRef.setQualifier(seedFuncName.makeRef(sourceInfo));
- JsExpression protoObj;
- if (x.getSuperClass() != null) {
- JsNameRef superPrototypeRef = names.get(x.getSuperClass()).makeRef(sourceInfo);
- JsNew newExpr = new JsNew(sourceInfo, superPrototypeRef);
- protoObj = newExpr;
- } else {
- protoObj = new JsObjectLiteral(sourceInfo);
- }
- JsExpression protoAsg = createAssignment(seedProtoRef, protoObj);
-
+ JsInvocation defineSeed = new JsInvocation(x.getSourceInfo());
+ JsName seedNameRef = indexedFunctions.get(
+ "SeedUtil.defineSeed").getName();
+ defineSeed.setQualifier(seedNameRef.makeRef(x.getSourceInfo()));
+ int newSeed = getSeedId(x);
+ assert newSeed > 0;
+ JClassType superClass = x.getSuperClass();
+ int superSeed = (superClass == null) ? -1 : getSeedId(x.getSuperClass());
+ // SeedUtil.defineSeed(queryId, superId, castableMap, constructors)
+ defineSeed.getArguments().add(new JsNumberLiteral(x.getSourceInfo(),
+ newSeed));
+ defineSeed.getArguments().add(new JsNumberLiteral(x.getSourceInfo(),
+ superSeed));
+ JsExpression castMap = generateCastableTypeMap(x);
+ defineSeed.getArguments().add(castMap);
+
// Chain assign the same prototype to every live constructor.
for (JMethod method : x.getMethods()) {
if (liveCtors.contains(method)) {
- JsNameRef protoRef = prototype.makeRef(sourceInfo);
- protoRef.setQualifier(names.get(method).makeRef(sourceInfo));
- protoAsg = createAssignment(protoRef, protoAsg);
+ defineSeed.getArguments().add(names.get(method).makeRef(
+ sourceInfo));
}
}
- // Finally, assign to the temp var for setup code.
- JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo), protoAsg);
- JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
+ JsStatement tmpAsgStmt = defineSeed.makeStmt();
globalStmts.add(tmpAsgStmt);
typeForStatMap.put(tmpAsgStmt, x);
} else {
@@ -1883,6 +1960,16 @@
JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
globalStmts.add(tmpAsgStmt);
typeForStatMap.put(tmpAsgStmt, x);
+ JField castableTypeMapField = program.getIndexedField("Object.castableTypeMap");
+ JsName castableTypeMapName = names.get(castableTypeMapField);
+ JsNameRef ctmRef = castableTypeMapName.makeRef(sourceInfo);
+ ctmRef.setQualifier(globalTemp.makeRef(sourceInfo));
+ JsExpression castMapLit = generateCastableTypeMap(x);
+ JsExpression ctmAsg = createAssignment(ctmRef,
+ castMapLit);
+ JsExprStmt ctmAsgStmt = ctmAsg.makeStmt();
+ globalStmts.add(ctmAsgStmt);
+ typeForStatMap.put(ctmAsgStmt, x);
}
}
@@ -2185,6 +2272,7 @@
private final Map<JAbstractMethodBody, JsFunction> methodBodyMap =
new IdentityHashMap<JAbstractMethodBody, JsFunction>();
private final Map<HasName, JsName> names = new IdentityHashMap<HasName, JsName>();
+ private int nextSeedId = 1;
private List<JsName> namesByQueryId;
private JsFunction nullFunc;
@@ -2199,6 +2287,11 @@
private final JProgram program;
/**
+ * Map of class type to allocated seed id.
+ */
+ private final Map<JReferenceType, Integer> seedIdMap = new HashMap<JReferenceType, Integer>();
+
+ /**
* All of the fields in String and Array need special handling for interop.
*/
private final Map<JField, String> specialObfuscatedFields = new HashMap<JField, String>();
@@ -2280,8 +2373,8 @@
namesToIdents.put("expando", "eX");
namesToIdents.put("typeMarker", "tM");
namesToIdents.put("castableTypeMap", "cM");
+ namesToIdents.put("___clazz", "cZ");
// Array magic field
- namesToIdents.put("arrayClass", "aC");
namesToIdents.put("queryId", "qI");
List<JField> fields = new ArrayList<JField>(program.getTypeJavaLangObject().getFields());
@@ -2293,6 +2386,11 @@
specialObfuscatedFields.put(field, ident);
}
}
+
+ // force java.lang.Object,java.lang.String
+ // to have seed ids 1,2
+ getSeedId(program.getTypeJavaLangObject());
+ getSeedId(program.getTypeJavaLangString());
}
String getNameString(HasName hasName) {
@@ -2300,6 +2398,20 @@
return s;
}
+ /**
+ * Looks up or assigns a seed id for a type..
+ */
+ int getSeedId(JReferenceType type) {
+ Integer val = seedIdMap.get(type);
+ int seedId = val == null ? 0 : val;
+
+ if (seedId == 0) {
+ seedId = nextSeedId++;
+ seedIdMap.put(type, seedId);
+ }
+ return seedId;
+ }
+
String mangleName(JField x) {
String s = getNameString(x.getEnclosingType()) + '_' + getNameString(x);
return s;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index ae2e645..831aa25 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -2158,15 +2158,13 @@
assert ("getClass".equals(method.getName()));
SourceInfo info = method.getSourceInfo();
if ("com.google.gwt.lang.Array".equals(type.getName())) {
- // Special implementation: return this.arrayClass
- JField arrayClassField = null;
- for (JField field : type.getFields()) {
- if ("arrayClass".equals(field.getName())) {
- arrayClassField = field;
- }
- }
- assert arrayClassField != null;
- implementMethod(method, new JFieldRef(info, makeThisRef(info), arrayClassField, type));
+ /*
+ * Don't implement, fall through to Object.getClass(). Array emulation code
+ * in com.google.gwt.lang.Array invokes Array.getClass() and expects to get the
+ * class literal for the actual runtime type of the array (e.g. Foo[].class) and
+ * not Array.class.
+ */
+ type.getMethods().remove(2);
} else {
implementMethod(method, new JClassLiteral(info, type));
}
@@ -2767,8 +2765,7 @@
*
* TODO(zundel): something much more awesome?
*/
- private static final long AST_VERSION = 2;
-
+ private static final long AST_VERSION = 3;
private static final char[] _STRING = "_String".toCharArray();
private static final String ARRAY_LENGTH_FIELD = "length";
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
index f525c3a..d13c0fc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
@@ -23,7 +23,6 @@
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
-import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JEnumType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JField.Disposition;
@@ -34,10 +33,10 @@
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
-import com.google.gwt.dev.jjs.ast.JNameOf;
import com.google.gwt.dev.jjs.ast.JNullLiteral;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
@@ -58,15 +57,20 @@
* Ordinarily, accessing one of these fields would trigger a clinit to run, but
* we've special-cased class literal fields to evaluate as top-level code before
* the application starts running to avoid the clinit.
+ *
+ * Class literal factory methods are responsible for installing references
+ * to themselves on the Object.clazz field of their JS runtime prototype
+ * since getClass() is no longer an overridden method. Prototypes can be
+ * looked up via 'seedId' from the global seedTable object, and so each
+ * class literal factory method is passed the seedId of its type.
* <p>
- * TODO(cromwellian): consider lazy-initialization to improve startup time.
*/
public class ImplementClassLiteralsAsFields {
private class NormalizeVisitor extends JModVisitor {
@Override
public void endVisit(JClassLiteral x, Context ctx) {
- JField field = resolveClassLiteralField(x);
+ JField field = resolveClassLiteralField(x.getRefType());
x.setField(field);
}
}
@@ -120,8 +124,8 @@
* Class:
*
* <pre>
- * Class.createForClass("java.lang.", "Object", /JNameOf/"java.lang.Object", null)
- * Class.createForClass("java.lang.", "Exception", /JNameOf/"java.lang.Exception", Throwable.class)
+ * Class.createForClass("java.lang.", "Object", /JSeedIdOf/"java.lang.Object", null)
+ * Class.createForClass("java.lang.", "Exception", /JSeedIdOf/"java.lang.Exception", Throwable.class)
* </pre>
*
* Interface:
@@ -139,21 +143,21 @@
* Array:
*
* <pre>
- * Class.createForArray("", "[I", /JNameOf/"com.google.gwt.lang.Array", int.class)
- * Class.createForArray("[Lcom.example.", "Foo;", /JNameOf/"com.google.gwt.lang.Array", Foo.class)
+ * Class.createForArray("", "[I", /JSeedIdOf/"com.google.gwt.lang.Array", int.class)
+ * Class.createForArray("[Lcom.example.", "Foo;", /JSeedIdOf/"com.google.gwt.lang.Array", Foo.class)
* </pre>
*
* Enum:
*
* <pre>
- * Class.createForEnum("com.example.", "MyEnum", /JNameOf/"com.example.MyEnum", Enum.class,
+ * Class.createForEnum("com.example.", "MyEnum", /JSeedIdOf/"com.example.MyEnum", Enum.class,
* public static MyEnum[] values(), public static MyEnum valueOf(String name))
* </pre>
*
* Enum subclass:
*
* <pre>
- * Class.createForEnum("com.example.", "MyEnum$1", /JNameOf/"com.example.MyEnum$1", MyEnum.class,
+ * Class.createForEnum("com.example.", "MyEnum$1", /JSeedIdOf/"com.example.MyEnum$1", MyEnum.class,
* null, null))
* </pre>
*/
@@ -182,15 +186,9 @@
JStringLiteral className = program.getLiteralString(info, getClassName(typeName));
call.addArgs(packageName, className);
- if (type instanceof JArrayType) {
- // There's only one seed function for all arrays
- JDeclaredType arrayType = program.getIndexedType("Array");
- call.addArg(new JNameOf(info, program.getTypeJavaLangString(), arrayType));
-
- } else if (type instanceof JClassType) {
+ if (type instanceof JArrayType || type instanceof JClassType) {
// Add the name of the seed function for concrete types
- call.addArg(new JNameOf(info, program.getTypeJavaLangString(), type));
-
+ call.addArg(new JSeedIdOf(info, program.getTypeJavaLangString(), type));
} else if (type instanceof JPrimitiveType) {
// And give primitive types an illegal, though meaningful, value
call.addArg(program.getLiteralString(info, " " + type.getJavahSignatureName()));
@@ -256,7 +254,7 @@
private JClassLiteral createDependentClassLiteral(SourceInfo info, JType type) {
JClassLiteral classLiteral = new JClassLiteral(info.makeChild(), type);
- JField field = resolveClassLiteralField(classLiteral);
+ JField field = resolveClassLiteralField(classLiteral.getRefType());
classLiteral.setField(field);
return classLiteral;
}
@@ -264,6 +262,7 @@
private void execImpl() {
NormalizeVisitor visitor = new NormalizeVisitor();
visitor.accept(program);
+ program.recordClassLiteralFields(classLiteralFields);
}
private String getTypeName(JType type) {
@@ -309,8 +308,7 @@
* }
* </pre>
*/
- private JField resolveClassLiteralField(JClassLiteral classLiteral) {
- JType type = classLiteral.getRefType();
+ private JField resolveClassLiteralField(JType type) {
type = normalizeJsoType(type);
JField field = classLiteralFields.get(type);
if (field == null) {
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 4375220..136f558 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
@@ -42,6 +42,7 @@
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JVariable;
import com.google.gwt.dev.jjs.ast.JVariableRef;
@@ -205,6 +206,13 @@
}
@Override
+ public void endVisit(JSeedIdOf x, Context ctx) {
+ if (x.getNode() instanceof JClassType) {
+ endVisit((JNameOf) x, ctx);
+ }
+ }
+
+ @Override
public void endVisit(JsniFieldRef x, Context ctx) {
if (isPruned(x.getField())) {
String ident = x.getIdent();
@@ -599,13 +607,17 @@
ControlFlowAnalyzer livenessAnalyzer = new ControlFlowAnalyzer(program);
livenessAnalyzer.setForPruning();
+
+ // SPECIAL: Immortal codegen types are never pruned
+ traverseTypes(livenessAnalyzer, program.immortalCodeGenTypes);
+
if (saveCodeGenTypes) {
/*
* SPECIAL: Some classes contain methods used by code generation later.
* Unless those transforms have already been performed, we must rescue all
* contained methods for later user.
*/
- traverseFromCodeGenTypes(livenessAnalyzer);
+ traverseTypes(livenessAnalyzer, program.codeGenTypes);
}
livenessAnalyzer.traverseEverything();
@@ -627,11 +639,11 @@
}
/**
- * Traverse from all methods in the program's code-gen types. See
- * {@link JProgram#CODEGEN_TYPES_SET}.
+ * Traverse from all methods starting from a set of types.
*/
- private void traverseFromCodeGenTypes(ControlFlowAnalyzer livenessAnalyzer) {
- for (JClassType type : program.codeGenTypes) {
+ private void traverseTypes(ControlFlowAnalyzer livenessAnalyzer,
+ List<JClassType> types) {
+ for (JClassType type : types) {
livenessAnalyzer.traverseFromReferenceTo(type);
for (JMethod method : type.getMethods()) {
if (method instanceof JConstructor) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceGetClassOverrides.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceGetClassOverrides.java
new file mode 100644
index 0000000..18152d0
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceGetClassOverrides.java
@@ -0,0 +1,71 @@
+/*
+ * 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.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JFieldRef;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JProgram;
+
+/**
+ * Prune all overrides of Object.getClass() except when the enclosing class is JavaScriptObject
+ * (to permit getClass() devirtualization in JsoDevirtualizer to continue to work).
+ * Also Inline all method calls to Object.getClass() as Object.clazz.
+ */
+public class ReplaceGetClassOverrides {
+ public static void exec(JProgram program) {
+ new GetClassInlinerRemover(program).accept(program);
+ }
+
+ private static class GetClassInlinerRemover extends JModVisitor {
+
+ private JProgram program;
+ private JMethod getClassMethod;
+ private JField clazzField;
+
+ public GetClassInlinerRemover(JProgram program) {
+ this.program = program;
+ getClassMethod = program.getIndexedMethod("Object.getClass");
+ clazzField = program.getIndexedField("Object.___clazz");
+ }
+
+ public void endVisit(JMethod x, Context ctx) {
+ // don't prune JSO.getClass()
+ if (x.getEnclosingType() == program.getJavaScriptObject()) {
+ return;
+ }
+ if (x.getOverrides().contains(getClassMethod)) {
+ ctx.removeMe();
+ }
+ }
+
+ public void endVisit(JMethodCall x, Context ctx) {
+ // don't inline JSO.getClass()
+ if (x.getTarget().getEnclosingType() == program.getJavaScriptObject()) {
+ return;
+ }
+ // replace overridden getClass() with reference to Object.clazz field
+ if (x.getTarget() == getClassMethod ||
+ x.getTarget().getOverrides().contains(getClassMethod)) {
+ ctx.replaceMe(new JFieldRef(x.getSourceInfo(), x.getInstance(),
+ clazzField, clazzField.getEnclosingType()));
+ }
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
index 829886f..691752a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
@@ -76,6 +76,7 @@
import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JReturnStatement;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
import com.google.gwt.dev.jjs.ast.JStatement;
import com.google.gwt.dev.jjs.ast.JStringLiteral;
import com.google.gwt.dev.jjs.ast.JSwitchStatement;
@@ -135,6 +136,7 @@
protected static final char[] CHARS_PROTECTED = "protected ".toCharArray();
protected static final char[] CHARS_PUBLIC = "public ".toCharArray();
protected static final char[] CHARS_RETURN = "return".toCharArray();
+ protected static final char[] CHARS_SEEDIDOF = " JSeedIdOf ".toCharArray();
protected static final char[] CHARS_SLASHSTAR = "/*".toCharArray();
protected static final char[] CHARS_STARSLASH = "*/".toCharArray();
protected static final char[] CHARS_STATIC = "static ".toCharArray();
@@ -677,7 +679,7 @@
@Override
public boolean visit(JNameOf x, Context ctx) {
print(CHARS_SLASHSTAR);
- print(CHARS_NAMEOF);
+ print(x instanceof JSeedIdOf ? CHARS_SEEDIDOF : CHARS_NAMEOF);
print(CHARS_STARSLASH);
printStringLiteral(x.getNode().getName());
return false;
diff --git a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
index 2a430a5..f123b0f 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
@@ -26,6 +26,7 @@
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsProgramFragment;
+import com.google.gwt.dev.js.ast.JsSeedIdOf;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.dev.js.ast.JsVisitable;
import com.google.gwt.dev.js.ast.JsVars.JsVar;
@@ -82,6 +83,12 @@
}
@Override
+ public boolean visit(JsSeedIdOf x, JsContext ctx) {
+ out.print(String.valueOf(x.getSeedId()));
+ return false;
+ }
+
+ @Override
protected <T extends JsVisitable> T doAccept(T node) {
JsName newName = nameToBillTo(node);
if (newName == null) {
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
index 82a6379..5df8898 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
@@ -22,6 +22,9 @@
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.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.js.ast.JsArrayAccess;
import com.google.gwt.dev.js.ast.JsArrayLiteral;
import com.google.gwt.dev.js.ast.JsBinaryOperation;
@@ -559,12 +562,22 @@
}
/**
- * Creates a visitor to instrument each JsFunction in the program.
+ * Creates a visitor to instrument each JsFunction in the jsProgram.
*/
private class InstrumentAllFunctions extends JsVisitor {
+
@Override
public void endVisit(JsFunction x, JsContext ctx) {
if (!x.getBody().getStatements().isEmpty()) {
+ JsName fnName = x.getName();
+ JMethod method = jjsmap.nameToMethod(fnName);
+ /**
+ * Do not instrumental immortal types because they are potentially
+ * evaluated before anything else has been defined.
+ */
+ if (method != null && jprogram.immortalCodeGenTypes.contains(method.getEnclosingType())) {
+ return;
+ }
if (recordLineNumbers) {
(new LocationVisitor(x)).accept(x.getBody());
} else {
@@ -802,9 +815,11 @@
STRIP, NATIVE, EMULATED;
}
- public static void exec(JsProgram program, PropertyOracle[] propertyOracles) {
+ public static void exec(JProgram jprogram, JsProgram jsProgram,
+ PropertyOracle[] propertyOracles,
+ JavaToJavaScriptMap jjsmap) {
if (getStackMode(propertyOracles) == StackMode.EMULATED) {
- (new JsStackEmulator(program, propertyOracles)).execImpl();
+ (new JsStackEmulator(jprogram, jsProgram, propertyOracles, jjsmap)).execImpl();
}
}
@@ -839,14 +854,20 @@
private JsFunction caughtFunction;
private JsName lineNumbers;
- private final JsProgram program;
+ private JProgram jprogram;
+ private final JsProgram jsProgram;
+ private JavaToJavaScriptMap jjsmap;
private boolean recordFileNames;
private boolean recordLineNumbers;
private JsName stack;
private JsName stackDepth;
- private JsStackEmulator(JsProgram program, PropertyOracle[] propertyOracles) {
- this.program = program;
+ private JsStackEmulator(JProgram jprogram, JsProgram jsProgram,
+ PropertyOracle[] propertyOracles,
+ JavaToJavaScriptMap jjsmap) {
+ this.jprogram = jprogram;
+ this.jsProgram = jsProgram;
+ this.jjsmap = jjsmap;
assert propertyOracles.length > 0;
PropertyOracle oracle = propertyOracles[0];
@@ -865,27 +886,27 @@
}
private void execImpl() {
- caughtFunction = program.getIndexedFunction("Exceptions.caught");
+ caughtFunction = jsProgram.getIndexedFunction("Exceptions.caught");
if (caughtFunction == null) {
// No exceptions caught? Weird, but possible.
return;
}
initNames();
makeVars();
- (new ReplaceUnobfuscatableNames()).accept(program);
- (new InstrumentAllFunctions()).accept(program);
+ (new ReplaceUnobfuscatableNames()).accept(jsProgram);
+ (new InstrumentAllFunctions()).accept(jsProgram);
}
private void initNames() {
- stack = program.getScope().declareName("$JsStackEmulator_stack", "$stack");
- stackDepth = program.getScope().declareName("$JsStackEmulator_stackDepth",
+ stack = jsProgram.getScope().declareName("$JsStackEmulator_stack", "$stack");
+ stackDepth = jsProgram.getScope().declareName("$JsStackEmulator_stackDepth",
"$stackDepth");
- lineNumbers = program.getScope().declareName("$JsStackEmulator_location",
+ lineNumbers = jsProgram.getScope().declareName("$JsStackEmulator_location",
"$location");
}
private void makeVars() {
- SourceInfo info = program.createSourceInfoSynthetic(getClass());
+ SourceInfo info = jsProgram.createSourceInfoSynthetic(getClass());
JsVar stackVar = new JsVar(info, stack);
stackVar.setInitExpr(new JsArrayLiteral(info));
JsVar stackDepthVar = new JsVar(info, stackDepth);
@@ -894,12 +915,12 @@
lineNumbersVar.setInitExpr(new JsArrayLiteral(info));
JsVars vars;
- JsStatement first = program.getGlobalBlock().getStatements().get(0);
+ JsStatement first = jsProgram.getGlobalBlock().getStatements().get(0);
if (first instanceof JsVars) {
vars = (JsVars) first;
} else {
vars = new JsVars(info);
- program.getGlobalBlock().getStatements().add(0, vars);
+ jsProgram.getGlobalBlock().getStatements().add(0, vars);
}
vars.add(stackVar);
vars.add(stackDepthVar);
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsSeedIdOf.java b/dev/core/src/com/google/gwt/dev/js/ast/JsSeedIdOf.java
new file mode 100644
index 0000000..e7c1778
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsSeedIdOf.java
@@ -0,0 +1,41 @@
+/*
+ * 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.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * An AST node whose evaluation results in the numeric seed id of its type.
+ */
+public class JsSeedIdOf extends JsNameOf {
+
+ private final int seedId;
+
+ public JsSeedIdOf(SourceInfo info, JsName name, int seedId) {
+ super(info, name);
+ this.seedId = seedId;
+ }
+
+ public int getSeedId() {
+ return seedId;
+ }
+
+ public void traverse(JsVisitor visitor, JsContext ctx) {
+ if (visitor.visit(this, ctx)) {
+ }
+ visitor.endVisit(this, ctx);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
index e64799f..142e462 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
@@ -219,6 +219,10 @@
public void endVisit(JsReturn x, JsContext ctx) {
}
+ public void endVisit(JsSeedIdOf x, JsContext ctx) {
+ endVisit((JsNameOf) x, ctx);
+ }
+
public void endVisit(JsStringLiteral x, JsContext ctx) {
}
@@ -383,6 +387,10 @@
return true;
}
+ public boolean visit(JsSeedIdOf x, JsContext ctx) {
+ return visit((JsNameOf) x, ctx);
+ }
+
public boolean visit(JsStringLiteral x, JsContext ctx) {
return true;
}
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
index 60c809f..3c36c55 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
@@ -173,7 +173,7 @@
public static Array initValues(Class<?> arrayClass,
JavaScriptObject castableTypeMap, int queryId, Array array) {
ExpandoWrapper.wrapArray(array);
- array.arrayClass = arrayClass;
+ setClass(array, arrayClass);
Util.setCastableTypeMap(array, castableTypeMap);
array.queryId = queryId;
return array;
@@ -302,17 +302,15 @@
return array[index] = value;
}-*/;
+ // violator pattern so that the field remains private
+ private static native void setClass(Object o, Class<?> clazz) /*-{
+ o.@java.lang.Object::___clazz = clazz;
+ }-*/;
+
/*
* Explicitly initialize all fields to JS false values; see comment in
* ExpandoWrapper.initExpandos().
*/
-
- /**
- * Holds the real type-specific Class object for a given array instance. The
- * compiler produces a magic implementation of getClass() which returns this
- * field directly.
- */
- protected Class<?> arrayClass = null;
/**
* A representation of the necessary cast target for objects stored into this
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/SeedUtil.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/SeedUtil.java
new file mode 100644
index 0000000..bd80104
--- /dev/null
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/SeedUtil.java
@@ -0,0 +1,70 @@
+/*
+ * 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.lang;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Utility class for fetching prototype-seed functions for injection into JsAST.
+ */
+public class SeedUtil {
+
+ /*
+ * Holds a map of seedId to anonymous Javascript functions (prototypes for class vtables).
+ */
+ private static JavaScriptObject seedTable = JavaScriptObject.createObject();
+
+ /**
+ * If not already created, generates an anonymous function and assigns it a slot in the global
+ * seedTable. If superSeed is > -1, it creates an instance of the superSeed by invoking
+ * newSeed() and then assigns it as the prototype of the seed being defined. It also sets up the
+ * castableTypeMap, as well as any ctors which are passed in via Javascript varargs. Finally, if
+ * the class literal for this seed id was setup first, which can happen if they are in separate
+ * code-split fragments, the Class.createFor* methods will have created a placeholder seedTable
+ * entry containing the Class literal, and this will be copied from the placeholder location
+ * onto the current prototype.
+ */
+ public static native JavaScriptObject defineSeed(int id, int superSeed,
+ JavaScriptObject castableTypeMap) /*-{
+ var seed = @com.google.gwt.lang.SeedUtil::seedTable[id];
+ if (seed && !seed.@java.lang.Object::___clazz) {
+ // not a placeholder entry setup by Class.setClassLiteral
+ _ = seed.prototype;
+ } else {
+ if (!seed) {
+ seed = @com.google.gwt.lang.SeedUtil::seedTable[id] = function() {
+ };
+ }
+ _ = seed.prototype = (superSeed < 0) ? {}
+ : @com.google.gwt.lang.SeedUtil::newSeed(I)(superSeed);
+ _.@java.lang.Object::castableTypeMap = castableTypeMap;
+ }
+ for (var i = 3; i < arguments.length; ++i) {
+ arguments[i].prototype = _;
+ }
+ if (seed.@java.lang.Object::___clazz) {
+ _.@java.lang.Object::___clazz = seed.@java.lang.Object::___clazz;
+ seed.@java.lang.Object::___clazz = null;
+ }
+ }-*/;
+
+ /**
+ * Lookup seed function by id and instantiate an object with it.
+ */
+ public static native JavaScriptObject newSeed(int id) /*-{
+ return new (@com.google.gwt.lang.SeedUtil::seedTable[id]);
+ }-*/;
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java
index 187b660..d9cdc10 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java
@@ -31,7 +31,5 @@
ControlFlowAnalyzer cfa = CodeSplitter.computeInitiallyLive(program);
assertTrue(cfa.getInstantiatedTypes().contains(findType(program, "com.google.gwt.lang.Array")));
- assertTrue(cfa.getLiveFieldsAndMethods().contains(
- findMethod(findType(program, "com.google.gwt.lang.Array"), "getClass")));
}
}
diff --git a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
index eb3c555..9bd5e36 100644
--- a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
+++ b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
@@ -77,7 +77,8 @@
builder.add(symbolData.getSymbolName(), symbolData.getJsniIdent(),
symbolData.getClassName(), symbolData.getMemberName(),
symbolData.getQueryId(),
- new CastableTypeDataImpl(castableTypeMapString));
+ new CastableTypeDataImpl(castableTypeMapString),
+ symbolData.getSeedId());
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
diff --git a/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java b/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
index d91c0e3..4c8976b 100644
--- a/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
+++ b/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
@@ -52,7 +52,8 @@
private WebModeClientOracle oracle = new WebModeClientOracle();
public void add(String jsIdent, String jsniIdent, String className,
- String memberName, int queryId, CastableTypeData castableTypeData) {
+ String memberName, int queryId, CastableTypeData castableTypeData,
+ int seedId) {
oracle.idents.add(jsIdent);
ClassData data = oracle.getClassData(className);
@@ -73,6 +74,9 @@
data.typeName = className;
data.seedName = jsIdent;
oracle.seedNamesToClassData.put(jsIdent, data);
+ // Class.getName() with metadata disabled is "Class$S<seedId>"
+ oracle.seedIdsToClassData.put("S" + seedId, data);
+ data.seedId = seedId;
} else {
if (jsniIdent.contains("(")) {
jsniIdent = jsniIdent.substring(jsniIdent.indexOf("::") + 2,
@@ -131,6 +135,7 @@
public String seedName;
public List<String> serializableFields = Collections.emptyList();
public String typeName;
+ public int seedId;
}
/**
@@ -139,7 +144,7 @@
* TODO: Use something other than Java serialization to store this type's
* data.
*/
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
/**
* Recreate a WebModeClientOracle based on the contents previously emitted by
@@ -222,6 +227,7 @@
private final Set<String> idents = new HashSet<String>();
private final Map<String, ClassData> seedNamesToClassData = new HashMap<String, ClassData>();
+ private final Map<String, ClassData> seedIdsToClassData = new HashMap<String, ClassData>();
private transient Map<Class<?>, Field[]> operableFieldMap = new IdentityHashMap<Class<?>, Field[]>();
@@ -381,6 +387,9 @@
seedName = seedName.substring(6);
}
ClassData data = seedNamesToClassData.get(seedName);
+ if (data == null) {
+ data = seedIdsToClassData.get(seedName);
+ }
return data == null ? null : data.typeName;
}
diff --git a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
index b5716fd..7e9f431 100644
--- a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
+++ b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
@@ -257,12 +257,14 @@
byte[] currentBackRef = begin(x);
byte[] constructorFunction = constructorFunction(x);
- String seedName = clientOracle.getSeedName(x.getTargetClass());
- if (seedName == null) {
- throw new IncompatibleRemoteServiceException(
- "The client cannot create type " + x.getTargetClass());
- }
+ String getSeedFunc = clientOracle.getMethodId("java.lang.Class",
+ "getSeedFunction", "Ljava/lang/Class;");
+ String classLitId = clientOracle.getFieldId(
+ "com.google.gwt.lang.ClassLiteralHolder",
+ getJavahSignatureName(x.getTargetClass()) + "_classLit");
+ assert classLitId != null : "No class literal for "
+ + x.getTargetClass().getName();
/*
* If we need to maintain a backreference to the object, it's established
@@ -270,7 +272,7 @@
* constructorFunction. This is done in case one of the fields should
* require a reference to the object that is currently being constructed.
*/
- // constructorFunctionFoo(x = new Foo, field1, field2)
+ // constructorFunctionFoo(x = new (classLit.getSeedFunction()), field1, field2)
push(constructorFunction);
lparen();
if (hasBackRef) {
@@ -278,7 +280,12 @@
eq();
}
_new();
- push(seedName);
+ lparen();
+ push(getSeedFunc);
+ lparen();
+ push(classLitId);
+ rparen();
+ rparen();
for (SetCommand setter : x.getSetters()) {
comma();
accept(setter.getValue());
@@ -562,7 +569,7 @@
byte[] ident = getBytes("_0");
- // function foo(_0) {return initValues(classLid, castableTypeData, queryId, _0)}
+ // function foo(_0) {return initValues(classLit, castableTypeData, queryId, _0)}
function();
push(functionName);
lparen();
diff --git a/user/super/com/google/gwt/emul/java/lang/Class.java b/user/super/com/google/gwt/emul/java/lang/Class.java
index d000d2d..8db8ead 100644
--- a/user/super/com/google/gwt/emul/java/lang/Class.java
+++ b/user/super/com/google/gwt/emul/java/lang/Class.java
@@ -30,16 +30,21 @@
private static final int ARRAY = 0x00000004;
private static final int ENUM = 0x00000008;
+ static native String asString(int number) /*-{
+ // for primitives, the seedId isn't a number, but a string like ' Z'
+ return typeof(number) == 'number' ? "S" + (number < 0 ? -number : number) : number;
+ }-*/;
+
/**
* Create a Class object for an array.
*
* @skip
*/
static <T> Class<T> createForArray(String packageName, String className,
- String seedName, Class<?> componentType) {
+ int seedId, Class<?> componentType) {
// Initialize here to avoid method inliner
Class<T> clazz = new Class<T>();
- setName(clazz, packageName, className, seedName);
+ setName(clazz, packageName, className, seedId != 0 ? -seedId : 0);
clazz.modifiers = ARRAY;
clazz.superclass = Object.class;
clazz.componentType = componentType;
@@ -52,10 +57,10 @@
* @skip
*/
static <T> Class<T> createForClass(String packageName, String className,
- String seedName, Class<? super T> superclass) {
+ int seedId, Class<? super T> superclass) {
// Initialize here to avoid method inliner
Class<T> clazz = new Class<T>();
- setName(clazz, packageName, className, seedName);
+ setName(clazz, packageName, className, seedId);
clazz.superclass = superclass;
return clazz;
}
@@ -66,11 +71,11 @@
* @skip
*/
static <T> Class<T> createForEnum(String packageName, String className,
- String seedName, Class<? super T> superclass,
+ int seedId, Class<? super T> superclass,
JavaScriptObject enumConstantsFunc, JavaScriptObject enumValueOfFunc) {
// Initialize here to avoid method inliner
Class<T> clazz = new Class<T>();
- setName(clazz, packageName, className, seedName);
+ setName(clazz, packageName, className, seedId);
clazz.modifiers = (enumConstantsFunc != null) ? ENUM : 0;
clazz.superclass = clazz.enumSuperclass = superclass;
clazz.enumConstantsFunc = enumConstantsFunc;
@@ -86,7 +91,7 @@
static <T> Class<T> createForInterface(String packageName, String className) {
// Initialize here to avoid method inliner
Class<T> clazz = new Class<T>();
- setName(clazz, packageName, className, null);
+ setName(clazz, packageName, className, 0);
clazz.modifiers = INTERFACE;
return clazz;
}
@@ -97,21 +102,87 @@
* @skip
*/
static Class<?> createForPrimitive(String packageName, String className,
- String seedName) {
+ int seedId) {
// Initialize here to avoid method inliner
Class<?> clazz = new Class<Object>();
- setName(clazz, packageName, className, seedName);
+ setName(clazz, packageName, className, seedId);
clazz.modifiers = PRIMITIVE;
return clazz;
}
+ /**
+ * Used by {@link WebModePayloadSink} to create uninitialized instances.
+ */
+ static native JavaScriptObject getSeedFunction(Class<?> clazz) /*-{
+ var func = @com.google.gwt.lang.SeedUtil::seedTable[clazz.@java.lang.Class::seedId];
+ clazz = null; // HACK: prevent pruning via inlining by using param as lvalue
+ return func;
+ }-*/;
+
static boolean isClassMetadataEnabled() {
// This body may be replaced by the compiler
return true;
}
+ /**
+ * null or 0 implies lack of seed function / non-instantiable type
+ */
+ static native boolean isInstantiable(int seedId) /*-{
+ return typeof (seedId) == 'number' && seedId > 0;
+ }-*/;
+
+ /**
+ * null implies pruned.
+ */
+ static native boolean isInstantiableOrPrimitive(int seedId) /*-{
+ return seedId != null && seedId != 0;
+ }-*/;
+
+ /**
+ * Install class literal into seed.prototype.clazz field such that
+ * Object.getClass() returning this.clazz returns the literal. Also stores
+ * seedId on class literal for looking up prototypes given a literal. This
+ * is used for deRPC at the moment, but may be used to implement
+ * Class.newInstance() in the future.
+ */
+ static native void setClassLiteral(int seedId, Class<?> clazz) /*-{
+ var proto;
+ clazz.@java.lang.Class::seedId = seedId;
+ // String is the exception to the usual vtable setup logic
+ if (seedId == 2) {
+ proto = String.prototype
+ } else {
+ if (seedId > 0) {
+ // Guarantees virtual method won't be pruned by using a JSNI ref
+ // This is required because deRPC needs to call it.
+ var seed = @java.lang.Class::getSeedFunction(Ljava/lang/Class;)(clazz);
+ // A class literal may be referenced prior to an async-loaded vtable setup
+ // For example, class literal lives in inital fragment,
+ // but type is instantiated in another fragment
+ if (seed) {
+ proto = seed.prototype;
+ } else {
+ // Leave a place holder for now to be filled in by __defineSeed__ later
+ seed = @com.google.gwt.lang.SeedUtil::seedTable[seedId] = function(){};
+ seed.@java.lang.Object::___clazz = clazz;
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+ proto.@java.lang.Object::___clazz = clazz;
+ }-*/;
+
+ /**
+ * The seedId parameter can take on the following values:
+ * > 0 => type is instantiable class
+ * < 0 => type is instantiable array
+ * null => type is not instantiable
+ * string => type is primitive
+ */
static void setName(Class<?> clazz, String packageName, String className,
- String seedName) {
+ int seedId) {
if (clazz.isClassMetadataEnabled()) {
clazz.typeName = packageName + className;
} else {
@@ -121,7 +192,11 @@
* during application start up, before class Integer has been initialized.
*/
clazz.typeName = "Class$"
- + (seedName != null ? seedName : "" + clazz.hashCode());
+ + (isInstantiableOrPrimitive(seedId) ? asString(seedId) : "" + clazz.hashCode());
+ }
+
+ if (isInstantiable(seedId)) {
+ setClassLiteral(seedId, clazz);
}
}
@@ -140,6 +215,8 @@
private String typeName;
+ private int seedId;
+
/**
* Not publicly instantiable.
*
diff --git a/user/super/com/google/gwt/emul/java/lang/Object.java b/user/super/com/google/gwt/emul/java/lang/Object.java
index 3b8efff..63717d2 100644
--- a/user/super/com/google/gwt/emul/java/lang/Object.java
+++ b/user/super/com/google/gwt/emul/java/lang/Object.java
@@ -26,6 +26,14 @@
public class Object {
/**
+ * Holds class literal for subtypes of Object.
+ */
+ // BUG: If this field name conflicts with a method param name, JDT will complain
+ // CHECKSTYLE_OFF
+ private transient Class<?> ___clazz;
+ // CHECKSTYLE_ON
+
+ /**
* Used by {@link com.google.gwt.core.client.impl.WeakMapping} in web mode
* to store an expando containing a String -> Object mapping.
*
@@ -60,14 +68,11 @@
}
/*
- * Note: Unlike the real JRE, we don't spec this method as final because the
- * compiler generates a polymorphic override on every other class which will
- * return the correct class object.
- *
- * TODO(scottb, compiler magician): declare this final, but have the compiler fix it up.
+ * magic; Actual assignment to this field is done by Class.createFor() methods by injecting it
+ * into the prototype.
*/
public Class<? extends Object> getClass() {
- return Object.class;
+ return ___clazz;
}
public int hashCode() {