Tracks nullness within the compiler by adding a JNonNull type.
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7279 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SizeMapRecorder.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SizeMapRecorder.java
index 19ea629..d1543ef 100644
--- a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SizeMapRecorder.java
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SizeMapRecorder.java
@@ -16,8 +16,8 @@
package com.google.gwt.core.ext.soyc.impl;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.js.SizeBreakdown;
@@ -182,7 +182,7 @@
return new TypedProgramReference("method", desc);
}
- JReferenceType type = jjsmap.nameToType(name);
+ JClassType type = jjsmap.nameToType(name);
if (type != null) {
return new TypedProgramReference("type", type.getName());
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java b/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java
index 4dd00aa..be0fbfb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/CorrelationFactory.java
@@ -17,9 +17,9 @@
import com.google.gwt.dev.jjs.Correlation.Axis;
import com.google.gwt.dev.jjs.Correlation.Literal;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsName;
@@ -51,7 +51,7 @@
}
@Override
- public Correlation by(JReferenceType type) {
+ public Correlation by(JDeclaredType type) {
return null;
}
@@ -155,7 +155,7 @@
}
@Override
- public Correlation by(JReferenceType type) {
+ public Correlation by(JDeclaredType type) {
Correlation toReturn = canonicalMap.get(type);
if (toReturn == null) {
toReturn = new Correlation(Axis.CLASS, type.getName(), type);
@@ -225,7 +225,7 @@
public abstract Correlation by(JMethod method);
- public abstract Correlation by(JReferenceType type);
+ public abstract Correlation by(JDeclaredType type);
public abstract Correlation by(JsFunction function);
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 aef1490..d4fbd85 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -724,7 +724,7 @@
throw new UnableToCompleteException();
}
- JMethod entryMethod = findMainMethodRecurse(reboundEntryType);
+ JMethod entryMethod = findMainMethodRecurse(entryClass);
if (entryMethod == null) {
logger.log(TreeLogger.ERROR,
"Could not find entry method 'onModuleLoad()' method in entry point class '"
@@ -738,7 +738,7 @@
+ originalMainClassName + "' must not be abstract", null);
throw new UnableToCompleteException();
}
- SourceInfo sourceInfo = reboundEntryType.getSourceInfo().makeChild(
+ SourceInfo sourceInfo = entryClass.getSourceInfo().makeChild(
JavaToJavaScriptCompiler.class, "Rebound entry point");
JExpression qualifier = null;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/HasSettableType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/HasSettableType.java
deleted file mode 100644
index c56737f..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/HasSettableType.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2007 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;
-
-/**
- * Characteristic interface to be overlaid on AST constructs that have a type
- * that can be explicitly set.
- */
-public interface HasSettableType extends HasType {
- void setType(JType newType);
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
index 8d8e469..e63722f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JArrayRef.java
@@ -22,8 +22,8 @@
*/
public class JArrayRef extends JExpression {
- private JExpression instance;
private JExpression indexExpr;
+ private JExpression instance;
public JArrayRef(SourceInfo info, JExpression instance, JExpression indexExpr) {
super(info);
@@ -31,6 +31,14 @@
this.indexExpr = indexExpr;
}
+ public JArrayType getArrayType() {
+ JType type = instance.getType();
+ if (type instanceof JNullType) {
+ return null;
+ }
+ return (JArrayType) ((JReferenceType) type).getUnderlyingType();
+ }
+
public JExpression getIndexExpr() {
return indexExpr;
}
@@ -40,12 +48,9 @@
}
public JType getType() {
- JType type = instance.getType();
- if (type instanceof JNullType) {
- return JNullType.INSTANCE;
- }
- JArrayType arrayType = (JArrayType) type;
- return arrayType.getElementType();
+ JArrayType arrayType = getArrayType();
+ return (arrayType == null) ? JNullType.INSTANCE
+ : arrayType.getElementType();
}
public boolean hasSideEffects() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
index 54db51d..61af84f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JBinaryOperation.java
@@ -20,7 +20,7 @@
/**
* Binary operator expression.
*/
-public class JBinaryOperation extends JExpression implements HasSettableType {
+public class JBinaryOperation extends JExpression {
private JExpression lhs;
private final JBinaryOperator op;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java
index f474f25..b8f4dff 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JConditional.java
@@ -20,7 +20,7 @@
/**
* Conditional expression.
*/
-public class JConditional extends JExpression implements HasSettableType {
+public class JConditional extends JExpression {
private JExpression elseExpr;
private JExpression ifTest;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
index cb46c35..69ebac2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
@@ -25,7 +25,7 @@
* finalized. Replaced with the entry call for the appropriate rebind result in
* that permutation.
*/
-public class JGwtCreate extends JExpression implements HasSettableType {
+public class JGwtCreate extends JExpression {
public static JExpression createInstantiationExpression(SourceInfo info,
JClassType classType) {
@@ -46,7 +46,8 @@
return null;
}
// Call it, using a new expression as a qualifier
- JNewInstance newInstance = new JNewInstance(info, classType);
+ JNewInstance newInstance = new JNewInstance(info,
+ (JNonNullType) noArgCtor.getType());
return new JMethodCall(info, newInstance, noArgCtor);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index 0a4a833..e2cb5de 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -31,7 +31,7 @@
* A Java method implementation.
*/
public final class JMethod extends JNode implements HasEnclosingType, HasName,
- HasSettableType, CanBeAbstract, CanBeSetFinal, CanBeNative, CanBeStatic {
+ HasType, CanBeAbstract, CanBeSetFinal, CanBeNative, CanBeStatic {
private static final String TRACE_METHOD_WILDCARD = "*";
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
index 24de34a..5df5e87 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
@@ -23,7 +23,7 @@
/**
* New array expression.
*/
-public class JNewArray extends JExpression implements HasSettableType {
+public class JNewArray extends JExpression {
public static JNewArray createDims(JProgram program, SourceInfo info,
JArrayType arrayType, List<JExpression> dims) {
@@ -45,39 +45,40 @@
classLiterals.add(classLit);
cur = ((JArrayType) cur).getElementType();
}
- return new JNewArray(info, arrayType, dims, null, classLiterals);
+ return new JNewArray(info, program.getNonNullType(arrayType), dims, null,
+ classLiterals);
}
public static JNewArray createInitializers(JProgram program, SourceInfo info,
JArrayType arrayType, List<JExpression> initializers) {
List<JClassLiteral> classLiterals = new ArrayList<JClassLiteral>();
classLiterals.add(program.getLiteralClass(arrayType));
- return new JNewArray(info, arrayType, null, initializers, classLiterals);
+ return new JNewArray(info, program.getNonNullType(arrayType), null,
+ initializers, classLiterals);
}
public final List<JExpression> dims;
public final List<JExpression> initializers;
- private JArrayType arrayType;
+ private JNonNullType type;
/**
* The list of class literals that will be needed to support this expression.
*/
private final List<JClassLiteral> classLiterals;
- public JNewArray(SourceInfo info, JArrayType arrayType,
- List<JExpression> dims, List<JExpression> initializers,
- List<JClassLiteral> classLits) {
+ public JNewArray(SourceInfo info, JNonNullType type, List<JExpression> dims,
+ List<JExpression> initializers, List<JClassLiteral> classLits) {
super(info);
- this.arrayType = arrayType;
+ setType(type);
this.dims = dims;
this.initializers = initializers;
this.classLiterals = classLits;
}
public JArrayType getArrayType() {
- return arrayType;
+ return (JArrayType) type.getUnderlyingType();
}
/**
@@ -98,8 +99,8 @@
return classLiterals;
}
- public JType getType() {
- return arrayType;
+ public JNonNullType getType() {
+ return type;
}
@Override
@@ -122,8 +123,9 @@
return false;
}
- public void setType(JType arrayType) {
- this.arrayType = (JArrayType) arrayType;
+ public void setType(JNonNullType type) {
+ assert type.getUnderlyingType() instanceof JArrayType;
+ this.type = type;
}
public void traverse(JVisitor visitor, Context ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
index f8a11a1..eeea366 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
@@ -24,21 +24,23 @@
*/
public class JNewInstance extends JExpression {
- private final JClassType classType;
+ private final JNonNullType type;
- public JNewInstance(SourceInfo info, JClassType classType) {
+ public JNewInstance(SourceInfo info, JNonNullType type) {
super(info);
- this.classType = classType;
+ assert type.getUnderlyingType() instanceof JClassType;
+ this.type = type;
}
public JClassType getClassType() {
- return classType;
+ return (JClassType) type.getUnderlyingType();
}
- public JType getType() {
- return classType;
+ public JNonNullType getType() {
+ return type;
}
+ @Override
public boolean hasSideEffects() {
// The actual new operation itself has no side effects (see class comment).
return false;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNonNullType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNonNullType.java
new file mode 100644
index 0000000..1d19196
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNonNullType.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2008 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.InternalCompilerException;
+
+/**
+ * A type including all the values in some other type except for
+ * <code>null</code>.
+ */
+public class JNonNullType extends JReferenceType {
+
+ private final JReferenceType ref;
+
+ JNonNullType(JReferenceType ref) {
+ super(ref.getSourceInfo(), ref.getName());
+ assert ref.canBeNull();
+ this.ref = ref;
+ }
+
+ @Override
+ public boolean canBeNull() {
+ return false;
+ }
+
+ @Override
+ public String getClassLiteralFactoryMethod() {
+ return ref.getClassLiteralFactoryMethod();
+ }
+
+ @Override
+ public JClassType getSuperClass() {
+ return ref.getSuperClass();
+ }
+
+ @Override
+ public JReferenceType getUnderlyingType() {
+ return ref;
+ }
+
+ public boolean isAbstract() {
+ return ref.isAbstract();
+ }
+
+ public boolean isFinal() {
+ return ref.isFinal();
+ }
+
+ @Override
+ public void setSuperClass(JClassType superClass) {
+ throw new InternalCompilerException("should not be called");
+ }
+
+ public void traverse(JVisitor visitor, Context ctx) {
+ visitor.accept(ref);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
index 2a2c513..cac95fb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNullType.java
@@ -36,10 +36,12 @@
"Cannot get class literal for null type");
}
+ @Override
public String getJavahSignatureName() {
return "N";
}
+ @Override
public String getJsniSignatureName() {
return "N";
}
@@ -52,6 +54,11 @@
return true;
}
+ @Override
+ public void setSuperClass(JClassType superClass) {
+ throw new InternalCompilerException("should not be called");
+ }
+
public void traverse(JVisitor visitor, Context ctx) {
if (visitor.visit(this, ctx)) {
}
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 c6bd9f6..df289af 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
@@ -283,6 +283,8 @@
private List<JsonObject> jsonTypeTable;
+ private Map<JReferenceType, JNonNullType> nonNullTypes = new IdentityHashMap<JReferenceType, JNonNullType>();
+
private JField nullField;
private JMethod nullMethod;
@@ -317,6 +319,8 @@
private final Map<String, JDeclaredType> typeNameMap = new HashMap<String, JDeclaredType>();
+ private JNonNullType typeNonNullString;
+
private JClassType typeSpecialClassLiteralHolder;
private JClassType typeSpecialJavaScriptObject;
@@ -397,6 +401,7 @@
typeJavaLangObject = x;
} else if (sname.equals("java.lang.String")) {
typeString = x;
+ typeNonNullString = getNonNullType(x);
} else if (sname.equals("java.lang.Enum")) {
typeJavaLangEnum = x;
} else if (sname.equals("java.lang.Class")) {
@@ -544,6 +549,10 @@
return createSourceInfo(0, caller.getName()).makeChild(caller, description);
}
+ /**
+ * Return the least upper bound of a set of types. That is, the smallest type
+ * that is a supertype of all the input types.
+ */
public JReferenceType generalizeTypes(
Collection<? extends JReferenceType> types) {
assert (types != null);
@@ -556,12 +565,31 @@
return curType;
}
+ /**
+ * Return the least upper bound of two types. That is, the smallest type that
+ * is a supertype of both types.
+ */
public JReferenceType generalizeTypes(JReferenceType type1,
JReferenceType type2) {
if (type1 == type2) {
return type1;
}
+ if (type1 instanceof JNonNullType && type2 instanceof JNonNullType) {
+ // Neither can be null.
+ type1 = type1.getUnderlyingType();
+ type2 = type2.getUnderlyingType();
+ return getNonNullType(generalizeTypes(type1, type2));
+ } else if (type1 instanceof JNonNullType) {
+ // type2 can be null, so the result can be null
+ type1 = type1.getUnderlyingType();
+ } else if (type2 instanceof JNonNullType) {
+ // type1 can be null, so the result can be null
+ type2 = type2.getUnderlyingType();
+ }
+ assert !(type1 instanceof JNonNullType);
+ assert !(type2 instanceof JNonNullType);
+
int classify1 = classifyType(type1);
int classify2 = classifyType(type2);
@@ -679,8 +707,8 @@
int lesser = Math.min(classify1, classify2);
int greater = Math.max(classify1, classify2);
- JReferenceType tLesser = classify1 > classify2 ? type1 : type2;
- JReferenceType tGreater = classify1 < classify2 ? type1 : type2;
+ JReferenceType tLesser = classify1 < classify2 ? type1 : type2;
+ JReferenceType tGreater = classify1 > classify2 ? type1 : type2;
if (lesser == IS_INTERFACE && greater == IS_CLASS) {
@@ -730,7 +758,7 @@
}
public JThisRef getExprThisRef(SourceInfo info, JClassType enclosingType) {
- return new JThisRef(info, enclosingType);
+ return new JThisRef(info, getNonNullType(enclosingType));
}
public int getFragmentCount() {
@@ -884,13 +912,25 @@
JStringLiteral toReturn = stringLiteralMap.get(s);
if (toReturn == null) {
toReturn = new JStringLiteral(stringPoolSourceInfo.makeChild(
- JProgram.class, "String literal: " + s), s, getTypeJavaLangString());
+ JProgram.class, "String literal: " + s), s, typeNonNullString);
stringLiteralMap.put(s, toReturn);
}
toReturn.getSourceInfo().merge(sourceInfo);
return toReturn;
}
+ public JNonNullType getNonNullType(JReferenceType type) {
+ if (type instanceof JNonNullType) {
+ return (JNonNullType) type;
+ }
+ JNonNullType nonNullType = nonNullTypes.get(type);
+ if (nonNullType == null) {
+ nonNullType = new JNonNullType(type);
+ nonNullTypes.put(type, nonNullType);
+ }
+ return nonNullType;
+ }
+
public JField getNullField() {
if (nullField == null) {
nullField = new JField(createSourceInfoSynthetic(JProgram.class,
@@ -910,6 +950,7 @@
}
public int getQueryId(JReferenceType elementType) {
+ assert (elementType == getRunTimeType(elementType));
Integer integer = queryIds.get(elementType);
if (integer == null) {
return 0;
@@ -922,6 +963,25 @@
return runAsyncReplacements;
}
+ /**
+ * A run-time type is a type at the granularity that GWT tests at run time.
+ * These include declared types, arrays of declared types, arrays of
+ * primitives, and null. This is also the granularity for the notion of
+ * instantiability recorded in {@link JTypeOracle}. This method returns the
+ * narrowest supertype of <code>type</code> that is a run-time type.
+ */
+ public JReferenceType getRunTimeType(JReferenceType type) {
+ type = type.getUnderlyingType();
+ if (type instanceof JArrayType) {
+ JArrayType typeArray = (JArrayType) type;
+ if (typeArray.getLeafType() instanceof JNonNullType) {
+ JNonNullType leafType = (JNonNullType) typeArray.getLeafType();
+ type = getTypeArray(leafType.getUnderlyingType(), typeArray.getDims());
+ }
+ }
+ return type;
+ }
+
public List<Integer> getSplitPointInitialSequence() {
return splitPointInitialSequence;
}
@@ -931,6 +991,7 @@
}
public JArrayType getTypeArray(JType leafType, int dimensions) {
+ assert (!(leafType instanceof JArrayType));
HashMap<JType, JArrayType> typeToArrayType;
// Create typeToArrayType maps for index slots that don't exist yet.
@@ -1014,6 +1075,7 @@
}
public int getTypeId(JReferenceType referenceType) {
+ assert (referenceType == getRunTimeType(referenceType));
Integer integer = typeIdMap.get(referenceType);
if (integer == null) {
return 0;
@@ -1086,9 +1148,13 @@
this.jsonTypeTable = jsonObjects;
}
+ public boolean isJavaLangString(JType type) {
+ return type == typeString || type == typeNonNullString;
+ }
+
public boolean isJavaScriptObject(JType type) {
- if (type instanceof JClassType && typeSpecialJavaScriptObject != null) {
- return typeOracle.canTriviallyCast((JClassType) type,
+ if (type instanceof JReferenceType && typeSpecialJavaScriptObject != null) {
+ return typeOracle.canTriviallyCast((JReferenceType) type,
typeSpecialJavaScriptObject);
}
return false;
@@ -1145,11 +1211,20 @@
return staticToInstanceMap.get(method);
}
+ /**
+ * Return the greatest lower bound of two types. That is, return the largest
+ * type that is a subtype of both inputs.
+ */
public JReferenceType strongerType(JReferenceType type1, JReferenceType type2) {
if (type1 == type2) {
return type1;
}
+ if (type1 instanceof JNonNullType != type2 instanceof JNonNullType) {
+ // If either is non-nullable, the result should be non-nullable.
+ return strongerType(getNonNullType(type1), getNonNullType(type2));
+ }
+
if (typeOracle.canTriviallyCast(type1, type2)) {
return type1;
}
@@ -1171,6 +1246,7 @@
}
private int classifyType(JReferenceType type) {
+ assert !(type instanceof JNonNullType);
if (type instanceof JNullType) {
return IS_NULL;
} else if (type instanceof JInterfaceType) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JReboundEntryPoint.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JReboundEntryPoint.java
index 41d6045..c93e3ec 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JReboundEntryPoint.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JReboundEntryPoint.java
@@ -28,9 +28,9 @@
private final List<JExpression> entryCalls;
private final List<JClassType> resultTypes;
- private final JReferenceType sourceType;
+ private final JDeclaredType sourceType;
- public JReboundEntryPoint(SourceInfo info, JReferenceType sourceType,
+ public JReboundEntryPoint(SourceInfo info, JDeclaredType sourceType,
List<JClassType> resultTypes, List<JExpression> entryCalls) {
super(info);
this.sourceType = sourceType;
@@ -46,7 +46,7 @@
return resultTypes;
}
- public JReferenceType getSourceType() {
+ public JDeclaredType getSourceType() {
return sourceType;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
index e70f1d7..db07d97 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JReferenceType.java
@@ -31,6 +31,16 @@
super(info, name, JNullLiteral.INSTANCE);
}
+ /**
+ * Returns <code>true</code> if it's possible for this type to be
+ * <code>null</code>.
+ *
+ * @see JNonNullType
+ */
+ public boolean canBeNull() {
+ return true;
+ }
+
@Override
public String getJavahSignatureName() {
return "L" + name.replaceAll("_", "_1").replace('.', '_') + "_2";
@@ -55,6 +65,13 @@
}
/**
+ * If this type is a non-null type, returns the underlying (original) type.
+ */
+ public JReferenceType getUnderlyingType() {
+ return this;
+ }
+
+ /**
* Sets this type's super class.
*/
public void setSuperClass(JClassType superClass) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java
index 9197a91..e38cd41 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JStringLiteral.java
@@ -22,13 +22,13 @@
*/
public class JStringLiteral extends JValueLiteral {
- private final JClassType stringType;
+ private final JNonNullType stringType;
private final String value;
/**
* These are only supposed to be constructed by JProgram.
*/
- JStringLiteral(SourceInfo sourceInfo, String value, JClassType stringType) {
+ JStringLiteral(SourceInfo sourceInfo, String value, JNonNullType stringType) {
super(sourceInfo);
this.value = value;
this.stringType = stringType;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
index d24101c..6f58f3b 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
@@ -22,19 +22,20 @@
*/
public class JThisRef extends JExpression {
- private final JClassType classType;
+ private final JNonNullType type;
- public JThisRef(SourceInfo info, JClassType classType) {
+ public JThisRef(SourceInfo info, JNonNullType type) {
super(info);
- this.classType = classType;
+ assert type.getUnderlyingType() instanceof JClassType;
+ this.type = type;
}
public JClassType getClassType() {
- return classType;
+ return (JClassType) type.getUnderlyingType();
}
public JType getType() {
- return classType;
+ return type;
}
public boolean hasSideEffects() {
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 1310630..ebd7a0b 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
@@ -177,35 +177,6 @@
}
/**
- * Determine whether a type is instantiated, given an assumed list of
- * instantiated types.
- *
- * @param type any type
- * @param instantiatedTypes a set of types assumed to be instantiated. If
- * <code>null</code>, then there are no assumptions about which types
- * are instantiated.
- * @return whether the type is instantiated
- */
- private static boolean isInstantiatedType(JReferenceType type,
- Set<JReferenceType> instantiatedTypes) {
- if (instantiatedTypes == null) {
- return true;
- }
-
- if (type instanceof JNullType) {
- return true;
- }
-
- if (type instanceof JArrayType) {
- JArrayType arrayType = (JArrayType) type;
- if (arrayType.getLeafType() instanceof JNullType) {
- return true;
- }
- }
- return instantiatedTypes.contains(type);
- }
-
- /**
* A map of all interfaces to the set of classes that could theoretically
* implement them.
*/
@@ -229,6 +200,11 @@
*/
private final Map<JClassType, Set<JInterfaceType>> implementsMap = new IdentityHashMap<JClassType, Set<JInterfaceType>>();
+ /**
+ * The types in the program that are instantiable. All types in this set
+ * should be run-time types as defined at
+ * {@link JProgram#getRunTimeType(JReferenceType)}.
+ */
private Set<JReferenceType> instantiatedTypes = null;
/**
@@ -291,6 +267,15 @@
}
public boolean canTheoreticallyCast(JReferenceType type, JReferenceType qType) {
+ if (!type.canBeNull() && qType == program.getTypeNull()) {
+ // Cannot cast non-nullable to null
+ return false;
+ }
+
+ // Compare the underlying types.
+ type = type.getUnderlyingType();
+ qType = qType.getUnderlyingType();
+
JClassType jlo = program.getTypeJavaLangObject();
if (type == qType || type == jlo) {
return true;
@@ -346,6 +331,15 @@
}
public boolean canTriviallyCast(JReferenceType type, JReferenceType qType) {
+ if (type.canBeNull() && !qType.canBeNull()) {
+ // Cannot reliably cast nullable to non-nullable
+ return false;
+ }
+
+ // Compare the underlying types.
+ type = type.getUnderlyingType();
+ qType = qType.getUnderlyingType();
+
JClassType jlo = program.getTypeJavaLangObject();
if (type == qType || qType == jlo) {
return true;
@@ -419,8 +413,7 @@
jsoSingleImpls.clear();
dualImpls.clear();
- for (int i = 0; i < program.getDeclaredTypes().size(); ++i) {
- JReferenceType type = program.getDeclaredTypes().get(i);
+ for (JDeclaredType type : program.getDeclaredTypes()) {
if (type instanceof JClassType) {
recordSuperSubInfo((JClassType) type);
} else {
@@ -451,20 +444,17 @@
}
}
- for (int i = 0; i < program.getDeclaredTypes().size(); ++i) {
- JReferenceType type = program.getDeclaredTypes().get(i);
+ for (JDeclaredType type : program.getDeclaredTypes()) {
if (type instanceof JClassType) {
computeImplements((JClassType) type);
}
}
- for (int i = 0; i < program.getDeclaredTypes().size(); ++i) {
- JReferenceType type = program.getDeclaredTypes().get(i);
+ for (JDeclaredType type : program.getDeclaredTypes()) {
if (type instanceof JClassType) {
computeCouldImplement((JClassType) type);
}
}
- for (int i = 0; i < program.getDeclaredTypes().size(); ++i) {
- JReferenceType type = program.getDeclaredTypes().get(i);
+ for (JDeclaredType type : program.getDeclaredTypes()) {
if (type instanceof JClassType) {
computeVirtualUpRefs((JClassType) type);
}
@@ -538,12 +528,12 @@
return results;
}
- public Set<JInterfaceType> getInterfacesWithJavaAndJsoImpls() {
- return Collections.unmodifiableSet(dualImpls);
+ public JClassType getSingleJsoImpl(JReferenceType maybeSingleJsoIntf) {
+ return jsoSingleImpls.get(maybeSingleJsoIntf.getUnderlyingType());
}
- public Map<JInterfaceType, JClassType> getSingleJsoImpls() {
- return Collections.unmodifiableMap(jsoSingleImpls);
+ public boolean isDualJsoInterface(JReferenceType maybeDualImpl) {
+ return dualImpls.contains(maybeDualImpl.getUnderlyingType());
}
public boolean isInstantiatedType(JReferenceType type) {
@@ -815,6 +805,37 @@
return get(implementsMap, type).contains(qType);
}
+ /**
+ * Determine whether a type is instantiated, given an assumed list of
+ * instantiated types.
+ *
+ * @param type any type
+ * @param instantiatedTypes a set of types assumed to be instantiated. If
+ * <code>null</code>, then there are no assumptions about which types
+ * are instantiated.
+ * @return whether the type is instantiated
+ */
+ private boolean isInstantiatedType(JReferenceType type,
+ Set<JReferenceType> instantiatedTypes) {
+ type = program.getRunTimeType(type);
+
+ if (instantiatedTypes == null) {
+ return true;
+ }
+
+ if (type instanceof JNullType) {
+ return true;
+ }
+
+ if (type instanceof JArrayType) {
+ JArrayType arrayType = (JArrayType) type;
+ if (arrayType.getLeafType() instanceof JNullType) {
+ return true;
+ }
+ }
+ return instantiatedTypes.contains(type);
+ }
+
private boolean isSameOrSuper(JClassType type, JClassType qType) {
return (type == qType || isSuperClass(type, qType));
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
index f9d46f2..8184366 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVariable.java
@@ -21,7 +21,7 @@
* Base class for any storage location.
*/
public abstract class JVariable extends JNode implements CanBeSetFinal,
- CanHaveInitializer, HasName, HasSettableType {
+ CanHaveInitializer, HasName, HasType {
protected JDeclarationStatement declStmt = null;
private boolean isFinal;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
index a6b46b8..0accf75 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsniFieldRef.java
@@ -29,7 +29,7 @@
public class JsniFieldRef extends JFieldRef {
private final String ident;
- private boolean isLvalue;
+ private final boolean isLvalue;
public JsniFieldRef(SourceInfo info, String ident, JField field,
JDeclaredType enclosingType, boolean isLvalue) {
@@ -47,6 +47,7 @@
return isLvalue;
}
+ @Override
public void traverse(JVisitor visitor, Context ctx) {
if (visitor.visit(this, ctx)) {
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
index a1e2292..14f1f2a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
@@ -49,12 +49,11 @@
public void endVisit(JBinaryOperation x, Context ctx) {
if (x.getOp() == JBinaryOperator.ASG && x.getLhs() instanceof JArrayRef) {
JArrayRef arrayRef = (JArrayRef) x.getLhs();
- if (arrayRef.getType() instanceof JNullType) {
+ JType elementType = arrayRef.getType();
+ if (elementType instanceof JNullType) {
// will generate a null pointer exception instead
return;
}
- JArrayType arrayType = (JArrayType) arrayRef.getInstance().getType();
- JType elementType = arrayType.getElementType();
/*
* See if we need to do a checked store. Primitives and (effectively)
@@ -62,7 +61,9 @@
*/
if (elementType instanceof JReferenceType) {
if (!((JReferenceType) elementType).isFinal()
- || elementType != x.getRhs().getType()) {
+ || !program.typeOracle.canTriviallyCast(
+ (JReferenceType) x.getRhs().getType(),
+ (JReferenceType) elementType)) {
// replace this assignment with a call to setCheck()
JMethodCall call = new JMethodCall(x.getSourceInfo(), null,
setCheckMethod);
@@ -137,10 +138,14 @@
SourceInfo sourceInfo = x.getSourceInfo().makeChild(ArrayVisitor.class,
"Creating dimensions");
JMethodCall call = new JMethodCall(sourceInfo, null, initDims, arrayType);
- JsonArray classLitList = new JsonArray(sourceInfo, program.getJavaScriptObject());
- JsonArray typeIdList = new JsonArray(sourceInfo, program.getJavaScriptObject());
- JsonArray queryIdList = new JsonArray(sourceInfo, program.getJavaScriptObject());
- JsonArray dimList = new JsonArray(sourceInfo, program.getJavaScriptObject());
+ JsonArray classLitList = new JsonArray(sourceInfo,
+ program.getJavaScriptObject());
+ JsonArray typeIdList = new JsonArray(sourceInfo,
+ program.getJavaScriptObject());
+ JsonArray queryIdList = new JsonArray(sourceInfo,
+ program.getJavaScriptObject());
+ JsonArray dimList = new JsonArray(sourceInfo,
+ program.getJavaScriptObject());
JType cur = arrayType;
for (int i = 0; i < dims; ++i) {
// Walk down each type from most dims to least.
@@ -168,11 +173,13 @@
// override the type of the called method with the array's type
SourceInfo sourceInfo = x.getSourceInfo().makeChild(ArrayVisitor.class,
"Array initializer");
- JMethodCall call = new JMethodCall(sourceInfo, null, initValues, arrayType);
+ JMethodCall call = new JMethodCall(sourceInfo, null, initValues,
+ arrayType);
JLiteral classLit = x.getClassLiteral();
JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(arrayType));
JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
- JsonArray initList = new JsonArray(sourceInfo, program.getJavaScriptObject());
+ JsonArray initList = new JsonArray(sourceInfo,
+ program.getJavaScriptObject());
for (int i = 0; i < x.initializers.size(); ++i) {
initList.exprs.add(x.initializers.get(i));
}
@@ -184,7 +191,7 @@
JType elementType = type.getElementType();
int leafTypeId = -1;
if (elementType instanceof JReferenceType) {
- leafTypeId = program.getQueryId((JReferenceType) elementType);
+ leafTypeId = program.getQueryId(program.getRunTimeType((JReferenceType) elementType));
}
return leafTypeId;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
index 81b3b9c..55f54ea 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -29,6 +29,7 @@
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JNewInstance;
+import com.google.gwt.dev.jjs.ast.JNonNullType;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
@@ -168,8 +169,8 @@
String name = enclosingType.getShortName();
SourceInfo info = makeSourceInfo(ctorDecl, enclosingType);
JMethod newMethod = program.createMethod(info, name.toCharArray(),
- enclosingType, enclosingType, false, false, true, b.isPrivate(),
- false);
+ enclosingType, program.getNonNullType(enclosingType), false, false,
+ true, b.isPrivate(), false);
// Enums have hidden arguments for name and value
if (enclosingType.isEnumOrSubclass() != null) {
@@ -400,12 +401,13 @@
// Define the method
JMethod synthetic = program.createMethod(type.getSourceInfo().makeChild(
BuildDeclMapVisitor.class, "Synthetic constructor"),
- "new".toCharArray(), type, type, false, true, true, false, false);
+ "new".toCharArray(), type, program.getNonNullType(type), false, true,
+ true, false, false);
// new Foo() : Create the instance
JNewInstance newInstance = new JNewInstance(
type.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
- "new instance"), type);
+ "new instance"), (JNonNullType) synthetic.getType());
// (new Foo()).Foo() : Invoke the constructor method on the instance
JMethodCall call = new JMethodCall(type.getSourceInfo().makeChild(
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
index 4296607..dc0a041 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
@@ -71,7 +71,6 @@
* </p>
*/
public class CastNormalizer {
-
private class AssignTypeIdsVisitor extends JVisitor {
Set<JReferenceType> alreadyRan = new HashSet<JReferenceType>();
@@ -147,17 +146,17 @@
*/
@Override
public void endVisit(JBinaryOperation x, Context ctx) {
- if (x.getOp() == JBinaryOperator.ASG && x.getLhs() instanceof JArrayRef) {
+ if (x.getOp().isAssignment() && x.getLhs() instanceof JArrayRef) {
// first, calculate the transitive closure of all possible runtime types
// the lhs could be
- JExpression instance = ((JArrayRef) x.getLhs()).getInstance();
- if (instance.getType() instanceof JNullType) {
+ JArrayRef lhsArrayRef = (JArrayRef) x.getLhs();
+ JType elementType = lhsArrayRef.getType();
+ if (elementType instanceof JNullType) {
// will generate a null pointer exception instead
return;
}
- JArrayType lhsArrayType = (JArrayType) instance.getType();
- JType elementType = lhsArrayType.getElementType();
+ JArrayType lhsArrayType = lhsArrayRef.getArrayType();
// primitives are statically correct
if (!(elementType instanceof JReferenceType)) {
@@ -183,7 +182,7 @@
if (typeOracle.canTheoreticallyCast(arrayType, lhsArrayType)) {
JType itElementType = arrayType.getElementType();
if (itElementType instanceof JReferenceType) {
- recordCastInternal((JReferenceType) itElementType, refRhsType);
+ recordCast(itElementType, x.getRhs());
}
}
}
@@ -214,6 +213,7 @@
if (type == null || alreadyRan.contains(type)) {
return;
}
+ assert (type == program.getRunTimeType(type));
alreadyRan.add(type);
@@ -288,14 +288,13 @@
private void recordCast(JType targetType, JExpression rhs) {
if (targetType instanceof JReferenceType) {
+ targetType = program.getRunTimeType((JReferenceType) targetType);
// unconditional cast b/c it would've been a semantic error earlier
- JReferenceType rhsType = (JReferenceType) rhs.getType();
+ JReferenceType rhsType = program.getRunTimeType((JReferenceType) rhs.getType());
// don't record a type for trivial casts that won't generate code
- if (rhsType instanceof JClassType) {
- if (program.typeOracle.canTriviallyCast(rhsType,
- (JReferenceType) targetType)) {
- return;
- }
+ if (program.typeOracle.canTriviallyCast(rhsType,
+ (JReferenceType) targetType)) {
+ return;
}
// If the target type is a JavaScriptObject, don't record an id.
@@ -307,9 +306,10 @@
}
}
- private void recordCastInternal(JReferenceType targetType,
+ private void recordCastInternal(JReferenceType toType,
JReferenceType rhsType) {
- JReferenceType toType = targetType;
+ toType = program.getRunTimeType(toType);
+ rhsType = program.getRunTimeType(rhsType);
Set<JReferenceType> querySet = queriedTypes.get(toType);
if (querySet == null) {
queryIds.put(toType, nextQueryId++);
@@ -434,7 +434,7 @@
replaceExpr = call;
} else if (toType instanceof JReferenceType) {
JExpression curExpr = expr;
- JReferenceType refType = (JReferenceType) toType;
+ JReferenceType refType = program.getRunTimeType((JReferenceType) toType);
JReferenceType argType = (JReferenceType) expr.getType();
if (program.typeOracle.canTriviallyCast(argType, refType)) {
// just remove the cast
@@ -442,11 +442,11 @@
} else {
JMethod method;
- boolean isJsoCast = program.isJavaScriptObject(toType);
+ boolean isJsoCast = program.isJavaScriptObject(refType);
if (isJsoCast) {
// A cast to a concrete JSO subtype
method = program.getIndexedMethod("Cast.dynamicCastJso");
- } else if (program.typeOracle.getSingleJsoImpls().containsKey(toType)) {
+ } else if (program.typeOracle.isDualJsoInterface(refType)) {
// An interface that should succeed when the object is a JSO
method = program.getIndexedMethod("Cast.dynamicCastAllowJso");
} else {
@@ -549,6 +549,8 @@
public void endVisit(JInstanceOf x, Context ctx) {
JReferenceType argType = (JReferenceType) x.getExpr().getType();
JReferenceType toType = x.getTestType();
+ // Only tests on run-time types are supported
+ assert (toType == program.getRunTimeType(toType));
if (program.typeOracle.canTriviallyCast(argType, toType)) {
// trivially true if non-null; replace with a null test
JNullLiteral nullLit = program.getLiteralNull();
@@ -559,7 +561,7 @@
} else {
JMethod method;
boolean isJsoCast = false;
- if (program.typeOracle.getSingleJsoImpls().containsKey(toType)) {
+ if (program.typeOracle.getSingleJsoImpl(toType) != null) {
method = program.getIndexedMethod("Cast.instanceOfOrJso");
} else if (program.isJavaScriptObject(toType)) {
isJsoCast = true;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
index 5c7de38..6bcc205 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
@@ -242,7 +242,7 @@
@Override
public boolean visit(JNewArray x, Context ctx) {
- expression = new JNewArray(x.getSourceInfo(), x.getArrayType(),
+ expression = new JNewArray(x.getSourceInfo(), x.getType(),
cloneExpressions(x.dims), cloneExpressions(x.initializers),
x.getClassLiterals());
return false;
@@ -250,7 +250,7 @@
@Override
public boolean visit(JNewInstance x, Context ctx) {
- expression = new JNewInstance(x.getSourceInfo(), x.getClassType());
+ expression = new JNewInstance(x.getSourceInfo(), x.getType());
return false;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
index 0c0d090..0095722 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
@@ -167,7 +167,7 @@
public Map<JField, Integer> fields = new HashMap<JField, Integer>();
public Map<JMethod, Integer> methods = new HashMap<JMethod, Integer>();
public Map<String, Integer> strings = new HashMap<String, Integer>();
- public Map<JReferenceType, Integer> types = new HashMap<JReferenceType, Integer>();
+ public Map<JDeclaredType, Integer> types = new HashMap<JDeclaredType, Integer>();
}
/**
@@ -184,6 +184,10 @@
this.fragment = fragment;
}
+ public boolean isLive(JDeclaredType type) {
+ return checkMap(fragmentMap.types, type);
+ }
+
public boolean isLive(JField field) {
return checkMap(fragmentMap.fields, field);
}
@@ -192,10 +196,6 @@
return checkMap(fragmentMap.methods, method);
}
- public boolean isLive(JReferenceType type) {
- return checkMap(fragmentMap.types, type);
- }
-
public boolean isLive(String literal) {
return checkMap(fragmentMap.strings, literal);
}
@@ -458,6 +458,19 @@
return cfa;
}
+ /**
+ * Extract the types from a set that happen to be declared types.
+ */
+ private static Set<JDeclaredType> declaredTypesIn(Set<JReferenceType> types) {
+ Set<JDeclaredType> result = new HashSet<JDeclaredType>();
+ for (JReferenceType type : types) {
+ if (type instanceof JDeclaredType) {
+ result.add((JDeclaredType) type);
+ }
+ }
+ return result;
+ }
+
private static String fullNameString(JField field) {
return field.getEnclosingType().getName() + "." + field.getName();
}
@@ -487,8 +500,8 @@
private static void installInitialLoadSequenceField(JProgram program,
LinkedHashSet<Integer> initialLoadSequence) {
JMethodCall constructorCall = ReplaceRunAsyncs.getBrowserLoaderConstructor(program);
- assert constructorCall.getArgs().get(1).getType() instanceof JArrayType;
- assert ((JArrayType) constructorCall.getArgs().get(1).getType()).getElementType() == JPrimitiveType.INT;
+ assert ((JReferenceType) constructorCall.getArgs().get(1).getType()).getUnderlyingType() instanceof JArrayType;
+ assert ((JArrayType) ((JReferenceType) constructorCall.getArgs().get(1).getType()).getUnderlyingType()).getElementType() == JPrimitiveType.INT;
SourceInfo info = program.createSourceInfoSynthetic(ReplaceRunAsyncs.class,
"array with initial load sequence");
@@ -600,7 +613,6 @@
}
private final MultipleDependencyGraphRecorder dependencyRecorder;
-
private final Map<JField, JClassLiteral> fieldToLiteralOfClass;
private final FragmentExtractor fragmentExtractor;
private final LinkedHashSet<Integer> initialLoadSequence;
@@ -933,12 +945,12 @@
private void fixUpLoadOrderDependenciesForTypes(ExclusivityMap fragmentMap) {
int numFixups = 0;
- Queue<JReferenceType> typesToCheck = new ArrayBlockingQueue<JReferenceType>(
+ Queue<JDeclaredType> typesToCheck = new ArrayBlockingQueue<JDeclaredType>(
jprogram.getDeclaredTypes().size());
typesToCheck.addAll(jprogram.getDeclaredTypes());
while (!typesToCheck.isEmpty()) {
- JReferenceType type = typesToCheck.remove();
+ JDeclaredType type = typesToCheck.remove();
if (type.getSuperClass() != null) {
int typeFrag = getOrZero(fragmentMap.types, type);
int supertypeFrag = getOrZero(fragmentMap.types, type.getSuperClass());
@@ -995,8 +1007,9 @@
allButOne.getLiveFieldsAndMethods(), allMethods);
updateMap(entry, fragmentMap.strings, allButOne.getLiveStrings(),
everything.getLiveStrings());
- updateMap(entry, fragmentMap.types, allButOne.getInstantiatedTypes(),
- everything.getInstantiatedTypes());
+ updateMap(entry, fragmentMap.types,
+ declaredTypesIn(allButOne.getInstantiatedTypes()),
+ declaredTypesIn(everything.getInstantiatedTypes()));
}
}
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 f62bba9..6abc9de 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
@@ -464,8 +464,7 @@
*/
private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
boolean doIt = false;
- if (program.isJavaScriptObject(type)
- || type == program.getTypeJavaLangString()) {
+ if (program.isJavaScriptObject(type) || program.isJavaLangString(type)) {
doIt = true;
} else if (type instanceof JArrayType) {
/*
@@ -475,7 +474,7 @@
JArrayType arrayType = (JArrayType) type;
JType elementType = arrayType.getElementType();
if (elementType instanceof JPrimitiveType
- || elementType == program.getTypeJavaLangString()
+ || program.isJavaLangString(elementType)
|| program.isJavaScriptObject(elementType)) {
doIt = true;
}
@@ -527,32 +526,39 @@
private void rescue(JReferenceType type, boolean isReferenced,
boolean isInstantiated) {
- if (type != null) {
+ if (type == null) {
+ return;
+ }
- boolean doVisit = false;
- if (isInstantiated && !instantiatedTypes.contains(type)) {
- instantiatedTypes.add(type);
- doVisit = true;
- }
+ /*
+ * Track references and instantiability at the granularity of run-time
+ * types. For example, ignore nullness.
+ */
+ type = program.getRunTimeType(type);
- if (isReferenced && !referencedTypes.contains(type)) {
- referencedTypes.add(type);
- doVisit = true;
- }
+ boolean doVisit = false;
+ if (isInstantiated && !instantiatedTypes.contains(type)) {
+ instantiatedTypes.add(type);
+ doVisit = true;
+ }
- if (doVisit) {
- accept(type);
+ if (isReferenced && !referencedTypes.contains(type)) {
+ referencedTypes.add(type);
+ doVisit = true;
+ }
- if (type instanceof JDeclaredType) {
- for (JNode artificial : ((JDeclaredType) type).getArtificialRescues()) {
- if (artificial instanceof JReferenceType) {
- rescue((JReferenceType) artificial, true, true);
- rescue(program.getLiteralClass((JReferenceType) artificial).getField());
- } else if (artificial instanceof JVariable) {
- rescue((JVariable) artificial);
- } else if (artificial instanceof JMethod) {
- rescue((JMethod) artificial);
- }
+ if (doVisit) {
+ accept(type);
+
+ if (type instanceof JDeclaredType) {
+ for (JNode artificial : ((JDeclaredType) type).getArtificialRescues()) {
+ if (artificial instanceof JReferenceType) {
+ rescue((JReferenceType) artificial, true, true);
+ rescue(program.getLiteralClass((JReferenceType) artificial).getField());
+ } else if (artificial instanceof JVariable) {
+ rescue((JVariable) artificial);
+ } else if (artificial instanceof JMethod) {
+ rescue((JMethod) artificial);
}
}
}
@@ -602,10 +608,11 @@
* Handle special rescues needed implicitly to support concat.
*/
private void rescueByConcat(JType type) {
- JClassType stringType = program.getTypeJavaLangString();
JPrimitiveType charType = program.getTypePrimitiveChar();
- if (type instanceof JReferenceType && type != stringType
- && type != program.getTypeNull()) {
+ JClassType stringType = program.getTypeJavaLangString();
+ if (type instanceof JReferenceType
+ && !program.typeOracle.canTriviallyCast((JReferenceType) type,
+ stringType) && type != program.getTypeNull()) {
/*
* Any reference types (except String, which works by default) that take
* part in a concat must rescue java.lang.Object.toString().
@@ -789,7 +796,7 @@
}
}
- public void traverseFromReferenceTo(JReferenceType type) {
+ public void traverseFromReferenceTo(JDeclaredType type) {
rescuer.rescue(type, true, false);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index 90505e7..b39364c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -1161,11 +1161,7 @@
}
private boolean isTypeString(JExpression exp) {
- return isTypeString(exp.getType());
- }
-
- private boolean isTypeString(JType type) {
- return type == program.getTypeJavaLangString();
+ return program.isJavaLangString(exp.getType());
}
private boolean isUnconditionalBreak(JStatement statement) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java
index 83578f5..fdff227 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java
@@ -28,37 +28,36 @@
import com.google.gwt.dev.jjs.ast.JType;
/**
- * Handle all of the cases where Java reference identity causes problems in
- * JavaScript due to the <code>null === undefined</code> problem. Reference
- * identity checks will generally fall into one of two cases right now.
- *
* <p>
- * If something that may be a String is compared to something that may not be a
- * <code>String</code>, we must use the <code>===</code> to prevent
- * JavaScript compare-as-strings behavior. However, this invites the
- * <code>null === undefined</code> problem, so we must emit calls to mask off
- * <code>undefined</code> as <code>null</code>. These cases include:
+ * Rewrite Java <code>==</code> so that it will execute correctly in JavaScript.
+ * After this pass, Java's <code>==</code> is considered equivalent to
+ * JavaScript's <code>===</code>.
+ *</p>
+ *<p>
+ * Whenever possible, a Java <code>==</code> is replaced by a JavaScript
+ * <code>==</code>. This is shorter than <code>===</code>, and it avoids any
+ * complication due to GWT treating both <code>null</code> and
+ * <code>undefined</code> as a valid translation of a Java <code>null</code>.
+ * </p>
+ * <p>
+ * However, whenever something that may be a String is compared to something
+ * that may not be a <code>String</code>, use <code>===</code>. A Java object
+ * compared to a string should always yield false, but that's not true when
+ * comparing in JavaScript using <code>==</code>. The cases where
+ * <code>===</code> must be used are:
* </p>
* <ul>
* <li>One or both sides have unknown <code>String</code> status.</li>
- * <li>One side is definitely <code>String</code> and one side is definitely !<code>String</code>.
- * <br/>TODO: This case could be optimized as
- * <code>(a == null) & (b == null)</code>. </li>
+ * <li>One side is definitely <code>String</code> and one side is definitely !
+ * <code>String</code>. <br/>
+ * TODO: This case could be optimized as
+ * <code>(a == null) & (b == null)</code>.</li>
* </ul>
- *
* <p>
- * Otherwise, we can use the <code>==</code> operator to avoid having to mask
- * of <code>undefined</code>. These cases include:
+ * Since <code>null !== undefined</code>, it is also necessary to normalize
+ * <code>null</code> vs. <code>undefined</code> if it's possible for one side to
+ * be <code>null</code> and the other to be <code>undefined</code>.
* </p>
- * <ul>
- * <li>Comparing two things statically typed as <code>String</code>.</li>
- * <li>Comparing two things statically typed as !<code>String</code>.</li>
- * <li>Comparing anything to something that is definitely <code>null</code>.</li>
- * </ul>
- *
- * TODO: There will be a third case when we can identity things that are
- * definitely not <code>null</code>. The presence of a definitely not null
- * operand allows us to use <code>===</code> with impunity.
*/
public class EqualityNormalizer {
@@ -84,45 +83,60 @@
StringStatus lhsStatus = getStringStatus((JReferenceType) lhsType);
StringStatus rhsStatus = getStringStatus((JReferenceType) rhsType);
+ int strat = COMPARISON_STRAT[lhsStatus.getIndex()][rhsStatus.getIndex()];
- if ((USE_TRIPLE_EQUALS[lhsStatus.getIndex()][rhsStatus.getIndex()] == 1)) {
- // Mask each side to prevent null === undefined.
- lhs = maskUndefined(lhs);
- rhs = maskUndefined(rhs);
- JBinaryOperation binOp = new JBinaryOperation(x.getSourceInfo(),
- x.getType(), x.getOp(), lhs, rhs);
- ctx.replaceMe(binOp);
- } else {
- boolean lhsNullLit = lhs == program.getLiteralNull();
- boolean rhsNullLit = rhs == program.getLiteralNull();
- if ((lhsNullLit && rhsStatus == StringStatus.NOTSTRING)
- || (rhsNullLit && lhsStatus == StringStatus.NOTSTRING)) {
- /*
- * If either side is a null literal and the other is non-String,
- * replace with a null-check.
- */
- String methodName;
- if (op == JBinaryOperator.EQ) {
- methodName = "Cast.isNull";
- } else {
- methodName = "Cast.isNotNull";
+ switch (strat) {
+ case STRAT_TRIPLE: {
+ if (canBeNull(lhs) && canBeNull(rhs)) {
+ /*
+ * If it's possible for one side to be null and the other side
+ * undefined, then mask both sides.
+ */
+ lhs = maskUndefined(lhs);
+ rhs = maskUndefined(rhs);
}
- JMethod isNullMethod = program.getIndexedMethod(methodName);
- JMethodCall call = new JMethodCall(x.getSourceInfo(), null, isNullMethod);
- call.addArg(lhsNullLit ? rhs : lhs);
- ctx.replaceMe(call);
- } else {
- // Replace with a call to Cast.jsEquals, which does a == internally.
- String methodName;
- if (op == JBinaryOperator.EQ) {
- methodName = "Cast.jsEquals";
+
+ JBinaryOperation binOp = new JBinaryOperation(x.getSourceInfo(),
+ x.getType(), x.getOp(), lhs, rhs);
+ ctx.replaceMe(binOp);
+ break;
+ }
+
+ case STRAT_DOUBLE: {
+ boolean lhsNullLit = lhs == program.getLiteralNull();
+ boolean rhsNullLit = rhs == program.getLiteralNull();
+ if ((lhsNullLit && rhsStatus == StringStatus.NOTSTRING)
+ || (rhsNullLit && lhsStatus == StringStatus.NOTSTRING)) {
+ /*
+ * If either side is a null literal and the other is non-String,
+ * replace with a null-check.
+ */
+ String methodName;
+ if (op == JBinaryOperator.EQ) {
+ methodName = "Cast.isNull";
+ } else {
+ methodName = "Cast.isNotNull";
+ }
+ JMethod isNullMethod = program.getIndexedMethod(methodName);
+ JMethodCall call = new JMethodCall(x.getSourceInfo(), null,
+ isNullMethod);
+ call.addArg(lhsNullLit ? rhs : lhs);
+ ctx.replaceMe(call);
} else {
- methodName = "Cast.jsNotEquals";
+ // Replace with a call to Cast.jsEquals, which does a == internally.
+ String methodName;
+ if (op == JBinaryOperator.EQ) {
+ methodName = "Cast.jsEquals";
+ } else {
+ methodName = "Cast.jsNotEquals";
+ }
+ JMethod eqMethod = program.getIndexedMethod(methodName);
+ JMethodCall call = new JMethodCall(x.getSourceInfo(), null,
+ eqMethod);
+ call.addArgs(lhs, rhs);
+ ctx.replaceMe(call);
}
- JMethod eqMethod = program.getIndexedMethod(methodName);
- JMethodCall call = new JMethodCall(x.getSourceInfo(), null, eqMethod);
- call.addArgs(lhs, rhs);
- ctx.replaceMe(call);
+ break;
}
}
}
@@ -141,9 +155,11 @@
}
private JExpression maskUndefined(JExpression lhs) {
+ assert ((JReferenceType) lhs.getType()).canBeNull();
+
JMethod maskMethod = program.getIndexedMethod("Cast.maskUndefined");
- JMethodCall lhsCall = new JMethodCall(lhs.getSourceInfo(), null, maskMethod,
- lhs.getType());
+ JMethodCall lhsCall = new JMethodCall(lhs.getSourceInfo(), null,
+ maskMethod, lhs.getType());
lhsCall.addArg(lhs);
return lhsCall;
}
@@ -152,8 +168,6 @@
/**
* Represents what we know about an operand type in terms of its type and
* <code>null</code> status.
- *
- * TODO: represent definitely non-null things.
*/
private enum StringStatus {
NOTSTRING(2), NULL(3), STRING(1), UNKNOWN(0);
@@ -170,20 +184,34 @@
}
/**
- * A map of the combinations where <code>===</code> should be used.
+ * A map of the combinations where each comparison strategy should be used.
*/
- private static int[][] USE_TRIPLE_EQUALS = {
+ private static int[][] COMPARISON_STRAT = {
// ..U..S.!S..N
- {1, 1, 1, 0}, // UNKNOWN
- {1, 0, 1, 0}, // STRING
- {1, 1, 0, 0}, // NOTSTRING
- {0, 0, 0, 0}, // NULL
+ {1, 1, 1, 0,}, // UNKNOWN
+ {1, 0, 1, 0,}, // STRING
+ {1, 1, 0, 0,}, // NOTSTRING
+ {0, 0, 0, 0,}, // NULL
};
+ /**
+ * The comparison strategy of using ==.
+ */
+ private static final int STRAT_DOUBLE = 0;
+
+ /**
+ * The comparison strategy of using ===.
+ */
+ private static final int STRAT_TRIPLE = 1;
+
public static void exec(JProgram program) {
new EqualityNormalizer(program).execImpl();
}
+ private static boolean canBeNull(JExpression x) {
+ return ((JReferenceType) x.getType()).canBeNull();
+ }
+
private final JProgram program;
private EqualityNormalizer(JProgram program) {
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 5ac8a58..fbe8748 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
@@ -16,10 +16,11 @@
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.js.ast.JsBinaryOperation;
import com.google.gwt.dev.js.ast.JsBinaryOperator;
import com.google.gwt.dev.js.ast.JsEmpty;
@@ -65,7 +66,7 @@
return cfa.getLiveFieldsAndMethods().contains(method);
}
- public boolean isLive(JReferenceType type) {
+ public boolean isLive(JDeclaredType type) {
return cfa.getInstantiatedTypes().contains(type);
}
@@ -107,7 +108,7 @@
boolean isLive(JMethod method);
- boolean isLive(JReferenceType type);
+ boolean isLive(JDeclaredType type);
boolean isLive(String literal);
@@ -133,7 +134,7 @@
return false;
}
- public boolean isLive(JReferenceType type) {
+ public boolean isLive(JDeclaredType type) {
return false;
}
@@ -258,7 +259,7 @@
/**
* The type whose vtables can currently be installed.
*/
- JReferenceType currentVtableType = null;
+ JClassType currentVtableType = null;
for (int frag = 0; frag < jsprogram.getFragmentCount(); frag++) {
List<JsStatement> stats = jsprogram.getFragmentBlock(frag).getStatements();
@@ -284,7 +285,7 @@
if (vtableTypeAssigned(stat) != null) {
currentVtableType = vtableTypeAssigned(stat);
}
- JReferenceType vtableType = vtableTypeNeeded(stat);
+ JClassType vtableType = vtableTypeNeeded(stat);
if (vtableType != null && vtableType != currentVtableType) {
extractedStats.add(vtableStatFor(vtableType));
currentVtableType = vtableType;
@@ -379,7 +380,7 @@
}
private boolean isLive(JsStatement stat, LivenessPredicate livenessPredicate) {
- JReferenceType type = map.typeForStatement(stat);
+ JClassType type = map.typeForStatement(stat);
if (type != null) {
// This is part of the code only needed once a type is instantiable
return livenessPredicate.isLive(type);
@@ -478,7 +479,7 @@
* <code>_ = foo.prototype</code>, where <code>foo</code> is the constructor
* function for <code>vtableType</code>.
*/
- private JsStatement vtableStatFor(JReferenceType vtableType) {
+ private JsStatement vtableStatFor(JClassType vtableType) {
JsNameRef prototypeField = new JsNameRef(
jsprogram.createSourceInfoSynthetic(FragmentExtractor.class,
"prototype field"), "prototype");
@@ -506,10 +507,8 @@
/**
* If <code>state</code> is of the form <code>_ = Foo.prototype = exp</code>,
* then return <code>Foo</code>. Otherwise return <code>null</code>.
- *
- * TODO(spoon): get this information via source info on the statement
*/
- private JReferenceType vtableTypeAssigned(JsStatement stat) {
+ private JClassType vtableTypeAssigned(JsStatement stat) {
if (!(stat instanceof JsExprStmt)) {
return null;
}
@@ -552,11 +551,11 @@
return map.typeForStatement(stat);
}
- private JReferenceType vtableTypeNeeded(JsStatement stat) {
+ private JClassType vtableTypeNeeded(JsStatement stat) {
JMethod meth = map.vtableInitToMethod(stat);
if (meth != null) {
if (!meth.isStatic()) {
- return meth.getEnclosingType();
+ return (JClassType) meth.getEnclosingType();
}
}
return null;
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 ace3d27..5e2add6 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
@@ -66,6 +66,7 @@
import com.google.gwt.dev.jjs.ast.JNewArray;
import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JNode;
+import com.google.gwt.dev.jjs.ast.JNonNullType;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JParameterRef;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
@@ -790,7 +791,8 @@
}
call = new JMethodCall(makeSourceInfo(x), null, targetMethod);
} else {
- JNewInstance newInstance = new JNewInstance(info, newType);
+ JNewInstance newInstance = new JNewInstance(info,
+ (JNonNullType) ctor.getType());
call = new JMethodCall(info, newInstance, ctor);
}
@@ -904,7 +906,7 @@
op = JBinaryOperator.SHRU;
break;
case BinaryExpression.PLUS:
- if (typeMap.get(x.resolvedType) == program.getTypeJavaLangString()) {
+ if (program.isJavaLangString((JType) typeMap.get(x.resolvedType))) {
op = JBinaryOperator.CONCAT;
} else {
op = JBinaryOperator.ADD;
@@ -975,7 +977,7 @@
switch (x.operator) {
case CompoundAssignment.PLUS:
- if (typeMap.get(x.resolvedType) == program.getTypeJavaLangString()) {
+ if (program.isJavaLangString((JType) typeMap.get(x.resolvedType))) {
op = JBinaryOperator.ASG_CONCAT;
} else {
op = JBinaryOperator.ASG_ADD;
@@ -1214,8 +1216,8 @@
SourceInfo info = makeSourceInfo(x);
MethodBinding b = x.binding;
JMethod ctor = (JMethod) typeMap.get(b);
- JClassType enclosingType = (JClassType) ctor.getEnclosingType();
- JNewInstance newInstance = new JNewInstance(info, enclosingType);
+ JNewInstance newInstance = new JNewInstance(info,
+ (JNonNullType) ctor.getType());
JMethodCall call = new JMethodCall(info, newInstance, ctor);
JExpression qualifier = dispProcessExpression(x.enclosingInstance);
List<JExpression> qualList = new ArrayList<JExpression>();
@@ -2155,7 +2157,7 @@
* world would we need to do this? It turns out that when making an
* unqualified explicit super constructor call to something that needs a
* synthetic outer this arg, the correct value to pass in can be one of
- * several of the calling constructor's own synthetic ags. The catch is,
+ * several of the calling constructor's own synthetic args. The catch is,
* it's possible none of the args are exactly the right type-- we have to
* make one of them the right type by following each of their synthetic this
* refs up an arbitrarily big tree of enclosing classes and
@@ -2167,6 +2169,8 @@
* prefer the current expression as one of its supertypes over a synthetic
* this ref rooted off the current expression that happens to be the correct
* type. We have observed this to be consistent with how Java handles it.
+ *
+ * TODO(scottb): could we get this info directly from JDT?
*/
private JExpression createThisRef(JReferenceType qualType,
List<JExpression> list) {
@@ -2174,7 +2178,7 @@
workList.addAll(list);
while (!workList.isEmpty()) {
JExpression expr = workList.removeFirst();
- JClassType classType = (JClassType) expr.getType();
+ JClassType classType = (JClassType) ((JReferenceType) expr.getType()).getUnderlyingType();
for (; classType != null; classType = classType.getSuperClass()) {
// prefer myself or myself-as-supertype over any of my this$ fields
// that may have already been added to the work list
@@ -2226,7 +2230,7 @@
JClassType fieldEnclosingType = (JClassType) field.getEnclosingType();
instance = createThisRef(info, fieldEnclosingType);
if (!program.typeOracle.canTriviallyCast(
- (JClassType) instance.getType(), fieldEnclosingType)) {
+ (JReferenceType) instance.getType(), fieldEnclosingType)) {
throw new InternalCompilerException(
"FieldRef referencing field in a different type.");
}
@@ -2344,8 +2348,8 @@
* expression. Beware that when autoboxing, the type of the expression is
* not necessarily the same as the type of the box to be created. The JDT
* figures out what the necessary conversion is, depending on the context
- * the expression appears in, and stores it in <code>x.implicitConversion</code>,
- * so extract it from there.
+ * the expression appears in, and stores it in
+ * <code>x.implicitConversion</code>, so extract it from there.
*/
private JPrimitiveType implicitConversionTargetType(Expression x)
throws InternalCompilerException {
@@ -2862,8 +2866,7 @@
JLiteral initializer = field.getConstInitializer();
JType type = initializer.getType();
- if (type instanceof JPrimitiveType
- || type == program.getTypeJavaLangString()) {
+ if (type instanceof JPrimitiveType || program.isJavaLangString(type)) {
GenerateJavaScriptLiterals generator = new GenerateJavaScriptLiterals(
jsProgram);
generator.accept(initializer);
@@ -2882,10 +2885,9 @@
private void processMethod(JsNameRef nameRef, SourceInfo info,
JMethod method, JsContext<JsExpression> ctx) {
- JReferenceType enclosingType = method.getEnclosingType();
+ JDeclaredType enclosingType = method.getEnclosingType();
if (enclosingType != null) {
- JClassType jsoImplType = program.typeOracle.getSingleJsoImpls().get(
- enclosingType);
+ JClassType jsoImplType = program.typeOracle.getSingleJsoImpl(enclosingType);
if (jsoImplType != null) {
JsniCollector.reportJsniError(info, methodDecl,
"Illegal reference to method '" + method.getName()
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 a107a0d..7bc960f 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
@@ -1086,7 +1086,7 @@
@Override
public void endVisit(JNewInstance x, Context ctx) {
JsNew newOp = new JsNew(x.getSourceInfo());
- JsNameRef nameRef = names.get(x.getType()).makeRef(x.getSourceInfo());
+ JsNameRef nameRef = names.get(x.getClassType()).makeRef(x.getSourceInfo());
newOp.setConstructorExpression(nameRef);
push(newOp);
}
@@ -1949,7 +1949,7 @@
* Because we modify String's prototype, all fields and polymorphic methods on
* String's super types need special handling.
*/
- private final Set<JReferenceType> specialObfuscatedTypes = new HashSet<JReferenceType>();
+ private final Set<JDeclaredType> specialObfuscatedTypes = new HashSet<JDeclaredType>();
/**
* Maps JsNames to machine-usable identifiers.
@@ -1961,7 +1961,7 @@
*/
private final JsScope topScope;
- private final Map<JsStatement, JReferenceType> typeForStatMap = new HashMap<JsStatement, JReferenceType>();
+ private final Map<JsStatement, JClassType> typeForStatMap = new HashMap<JsStatement, JClassType>();
private final JTypeOracle typeOracle;
@@ -2121,11 +2121,11 @@
generator.accept(program);
final Map<JsName, JMethod> nameToMethodMap = new HashMap<JsName, JMethod>();
final HashMap<JsName, JField> nameToFieldMap = new HashMap<JsName, JField>();
- final HashMap<JsName, JReferenceType> constructorNameToTypeMap = new HashMap<JsName, JReferenceType>();
+ final HashMap<JsName, JClassType> constructorNameToTypeMap = new HashMap<JsName, JClassType>();
for (JDeclaredType type : program.getDeclaredTypes()) {
JsName typeName = names.get(type);
- if (typeName != null) {
- constructorNameToTypeMap.put(typeName, type);
+ if (type instanceof JClassType && typeName != null) {
+ constructorNameToTypeMap.put(typeName, (JClassType) type);
}
for (JField field : type.getFields()) {
if (field.isStatic()) {
@@ -2152,7 +2152,7 @@
return names.get(method);
}
- public JsName nameForType(JReferenceType type) {
+ public JsName nameForType(JClassType type) {
return names.get(type);
}
@@ -2164,11 +2164,11 @@
return nameToMethodMap.get(name);
}
- public JReferenceType nameToType(JsName name) {
+ public JClassType nameToType(JsName name) {
return constructorNameToTypeMap.get(name);
}
- public JReferenceType typeForStatement(JsStatement stat) {
+ public JClassType typeForStatement(JsStatement stat) {
return typeForStatMap.get(stat);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
index 0760216..1eb02f0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
@@ -22,10 +22,10 @@
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConditional;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JInstanceOf;
-import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JLocalRef;
import com.google.gwt.dev.jjs.ast.JMethod;
@@ -33,14 +33,13 @@
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNewArray;
+import com.google.gwt.dev.jjs.ast.JNonNullType;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
-import java.util.Map;
-import java.util.Set;
import java.util.Stack;
/**
@@ -110,8 +109,8 @@
*/
@Override
public void endVisit(JMethodCall x, Context ctx) {
- JType targetClass = x.getTarget().getEnclosingType();
- if (jsoSingleImpls.containsKey(targetClass)) {
+ JDeclaredType targetClass = x.getTarget().getEnclosingType();
+ if (program.typeOracle.getSingleJsoImpl(targetClass) != null) {
SourceInfo info = x.getSourceInfo().makeChild(
JavaScriptObjectNormalizer.class,
@@ -121,7 +120,7 @@
JMethod jsoMethod = findJsoMethod(x.getTarget());
assert jsoMethod != null;
- if (dualImpls.contains(targetClass)) {
+ if (program.typeOracle.isDualJsoInterface(targetClass)) {
/*
* This is the special-case code to handle interfaces.
*/
@@ -163,7 +162,7 @@
@Override
public void endVisit(JNewArray x, Context ctx) {
- x.setType(translate(x.getType()));
+ x.setType((JNonNullType) translate(x.getType()));
}
@Override
@@ -198,7 +197,7 @@
}
private JMethod findJsoMethod(JMethod interfaceMethod) {
- JClassType jsoClass = jsoSingleImpls.get(interfaceMethod.getEnclosingType());
+ JClassType jsoClass = program.typeOracle.getSingleJsoImpl(interfaceMethod.getEnclosingType());
assert program.isJavaScriptObject(jsoClass);
assert jsoClass != null;
@@ -245,22 +244,28 @@
}
private JType translate(JType type) {
- if (program.isJavaScriptObject(type)) {
- return program.getJavaScriptObject();
+ if (!(type instanceof JReferenceType)) {
+ return type;
+ }
+ JReferenceType refType = (JReferenceType) type;
+ boolean canBeNull = refType.canBeNull();
+ refType = refType.getUnderlyingType();
- } else if (jsoSingleImpls.containsKey(type) && !dualImpls.contains(type)) {
+ if (program.isJavaScriptObject(refType)) {
+ refType = program.getJavaScriptObject();
+ } else if (program.typeOracle.getSingleJsoImpl(refType) != null
+ && !program.typeOracle.isDualJsoInterface(refType)) {
// Optimization: narrow to JSO if it's not a dual impl.
- return program.getJavaScriptObject();
-
- } else if (type instanceof JArrayType) {
- JArrayType arrayType = (JArrayType) type;
+ refType = program.getJavaScriptObject();
+ } else if (refType instanceof JArrayType) {
+ JArrayType arrayType = (JArrayType) refType;
JType leafType = arrayType.getLeafType();
JType replacement = translate(leafType);
if (leafType != replacement) {
- return program.getTypeArray(replacement, arrayType.getDims());
+ refType = program.getTypeArray(replacement, arrayType.getDims());
}
}
- return type;
+ return canBeNull ? refType : program.getNonNullType(refType);
}
}
@@ -268,22 +273,10 @@
new JavaScriptObjectNormalizer(program).execImpl();
}
- /**
- * Interfaces implemented both by a JSO type and a regular Java type.
- */
- private final Set<JInterfaceType> dualImpls;
-
- /**
- * Maps SingleJsoImpl interfaces onto the single JSO implementation.
- */
- private final Map<JInterfaceType, JClassType> jsoSingleImpls;
-
private final JProgram program;
private JavaScriptObjectNormalizer(JProgram program) {
this.program = program;
- dualImpls = program.typeOracle.getInterfacesWithJavaAndJsoImpls();
- jsoSingleImpls = program.typeOracle.getSingleJsoImpls();
}
private void execImpl() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
index 3a232b1..89aace3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
@@ -15,9 +15,9 @@
*/
package com.google.gwt.dev.jjs.impl;
+import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsStatement;
@@ -33,7 +33,7 @@
/**
* Return the JavaScript name corresponding to a Java type.
*/
- JsName nameForType(JReferenceType type);
+ JsName nameForType(JClassType type);
/**
* If <code>name</code> is the name of a
@@ -52,13 +52,13 @@
* If <code>name</code> is the name of a constructor function corresponding to
* a Java type, then return that type. Otherwise, return <code>null</code>.
*/
- JReferenceType nameToType(JsName name);
+ JClassType nameToType(JsName name);
/**
* If <code>stat</code> is used to set up the definition of some class, return
* that class. Otherwise, return null.
*/
- JReferenceType typeForStatement(JsStatement stat);
+ JClassType typeForStatement(JsStatement stat);
/**
* If <code>stat</code> is used to set up a vtable entry for a method, then
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java
index d5929ac..9e71d88 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/LongCastNormalizer.java
@@ -59,7 +59,7 @@
JType resultType = x.getType();
JBinaryOperator op = x.getOp();
- if (resultType == program.getTypeJavaLangString()) {
+ if (program.isJavaLangString(resultType)) {
// Don't mess with concat.
return;
}
@@ -123,8 +123,8 @@
if (init != null) {
init = checkAndReplace(init, x.getVariableRef().getType());
if (init != x.getInitializer()) {
- JDeclarationStatement newStmt = new JDeclarationStatement(x.getSourceInfo(),
- x.getVariableRef(), init);
+ JDeclarationStatement newStmt = new JDeclarationStatement(
+ x.getSourceInfo(), x.getVariableRef(), init);
ctx.replaceMe(newStmt);
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
index d35dbc9..4a00988 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/LongEmulationNormalizer.java
@@ -50,7 +50,7 @@
@Override
public void endVisit(JBinaryOperation x, Context ctx) {
// Concats are handled by CastNormalizer.ConcatVisitor.
- if (x.getType() == program.getTypeJavaLangString()) {
+ if (program.isJavaLangString(x.getType())) {
return;
}
JType lhsType = x.getLhs().getType();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
index 1fe417a..d0bf13f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
@@ -159,7 +159,7 @@
// Setup parameters; map from the old params to the new params
JParameter thisParam = JParameter.create(sourceInfo.makeChild(
CreateStaticImplsVisitor.class, "Instance parameter"), "this$static",
- enclosingType, true, true, newMethod);
+ program.getNonNullType(enclosingType), true, true, newMethod);
Map<JParameter, JParameter> varMap = new IdentityHashMap<JParameter, JParameter>();
for (int i = 0; i < x.getParams().size(); ++i) {
JParameter oldVar = x.getParams().get(i);
@@ -171,7 +171,7 @@
// Set the new original param types based on the old original param types
List<JType> originalParamTypes = new ArrayList<JType>();
- originalParamTypes.add(enclosingType);
+ originalParamTypes.add(program.getNonNullType(enclosingType));
originalParamTypes.addAll(x.getOriginalParamTypes());
newMethod.setOriginalTypes(x.getOriginalReturnType(), originalParamTypes);
@@ -186,7 +186,8 @@
x.setBody(newBody);
JMethodCall newCall = new JMethodCall(delegateCallSourceInfo, null,
newMethod);
- newCall.addArg(new JThisRef(delegateCallSourceInfo, enclosingType));
+ newCall.addArg(new JThisRef(delegateCallSourceInfo,
+ program.getNonNullType(enclosingType)));
for (int i = 0; i < x.getParams().size(); ++i) {
JParameter param = x.getParams().get(i);
newCall.addArg(new JParameterRef(delegateCallSourceInfo, param));
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
index 4e5cb42..d5ec679 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
@@ -26,7 +26,6 @@
import com.google.gwt.dev.jjs.ast.JNullType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
-import com.google.gwt.dev.jjs.ast.JType;
/**
* Update polymorphic method calls to tighter bindings based on the type of the
@@ -54,7 +53,7 @@
return;
}
- JType instanceType = instance.getType();
+ JReferenceType instanceType = ((JReferenceType) instance.getType()).getUnderlyingType();
JReferenceType enclosingType = method.getEnclosingType();
if (instanceType == enclosingType
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java
index 841735a..048a7b0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java
@@ -18,11 +18,11 @@
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JGwtCreate;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import java.util.List;
@@ -91,7 +91,7 @@
throw new InternalCompilerException("Unexpected failure to rebind '"
+ reqType + "'");
}
- JReferenceType result = program.getFromTypeMap(reboundClassName);
+ JDeclaredType result = program.getFromTypeMap(reboundClassName);
assert (result != null);
return (JClassType) result;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
index abbef1d..6c35360 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/SourceGenerationVisitor.java
@@ -17,12 +17,12 @@
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.util.TextOutput;
/**
@@ -99,7 +99,7 @@
@Override
public boolean visit(JProgram x, Context ctx) {
for (int i = 0; i < x.getDeclaredTypes().size(); ++i) {
- JReferenceType type = x.getDeclaredTypes().get(i);
+ JDeclaredType type = x.getDeclaredTypes().get(i);
accept(type);
newline();
newline();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index e7bb5a4..04ed496 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -306,7 +306,8 @@
*/
JMethod staticImplFor = program.staticImplFor(x);
if (staticImplFor == null
- || !staticImplFor.getEnclosingType().getMethods().contains(staticImplFor)) {
+ || !staticImplFor.getEnclosingType().getMethods().contains(
+ staticImplFor)) {
// The instance method has already been pruned.
return true;
}
@@ -404,17 +405,18 @@
if (triviallyTrue) {
// remove the cast operation
ctx.replaceMe(x.getExpr());
- } else if (triviallyFalse) {
- // replace with a magic NULL cast
+ } else if (triviallyFalse && toType != program.getTypeNull()) {
+ // replace with a magic NULL cast, unless it's already a cast to NULL
JCastOperation newOp = new JCastOperation(x.getSourceInfo(),
program.getTypeNull(), x.getExpr());
ctx.replaceMe(newOp);
} else {
// If possible, try to use a narrower cast
- JClassType concreteType = getSingleConcreteType(toType);
- if (concreteType != null) {
+ JReferenceType tighterType = getSingleConcreteType(toType);
+
+ if (tighterType != null && tighterType != toType) {
JCastOperation newOp = new JCastOperation(x.getSourceInfo(),
- concreteType, x.getExpr());
+ tighterType, x.getExpr());
ctx.replaceMe(newOp);
}
}
@@ -442,10 +444,10 @@
@Override
public void endVisit(JGwtCreate x, Context ctx) {
- List<JClassType> typeList = new ArrayList<JClassType>();
+ List<JReferenceType> typeList = new ArrayList<JReferenceType>();
for (JExpression expr : x.getInstantiationExpressions()) {
- JType type = expr.getType();
- typeList.add((JClassType) type);
+ JReferenceType type = (JReferenceType) expr.getType();
+ typeList.add(type);
}
JReferenceType resultType = program.generalizeTypes(typeList);
@@ -493,7 +495,7 @@
ctx.replaceMe(program.getLiteralBoolean(false));
} else {
// If possible, try to use a narrower cast
- JClassType concreteType = getSingleConcreteType(toType);
+ JReferenceType concreteType = getSingleConcreteType(toType);
if (concreteType != null) {
JInstanceOf newOp = new JInstanceOf(x.getSourceInfo(), concreteType,
x.getExpr());
@@ -528,7 +530,7 @@
return;
}
- JClassType concreteType = getSingleConcreteType(x.getType());
+ JReferenceType concreteType = getSingleConcreteType(x.getType());
if (concreteType != null) {
x.setType(concreteType);
myDidChange = true;
@@ -545,13 +547,6 @@
// tighten based on both returned types and possible overrides
List<JReferenceType> typeList = new ArrayList<JReferenceType>();
- /*
- * Always assume at least one null assignment; if there really aren't any
- * other assignments, then this variable will get the null type. If there
- * are, it won't hurt anything because null type will always lose.
- */
- typeList.add(typeNull);
-
Set<JExpression> myReturns = returns.get(x);
if (myReturns != null) {
for (JExpression expr : myReturns) {
@@ -565,7 +560,13 @@
}
}
- JReferenceType resultType = program.generalizeTypes(typeList);
+ JReferenceType resultType;
+ if (typeList.isEmpty()) {
+ // The method returns nothing
+ resultType = typeNull;
+ } else {
+ resultType = program.generalizeTypes(typeList);
+ }
resultType = program.strongerType(refType, resultType);
if (refType != resultType) {
x.setType(resultType);
@@ -657,14 +658,18 @@
* Given an abstract type, return the single concrete implementation of that
* type.
*/
- private JClassType getSingleConcreteType(JType type) {
+ private JReferenceType getSingleConcreteType(JType type) {
if (type instanceof JReferenceType) {
JReferenceType refType = (JReferenceType) type;
if (refType.isAbstract()) {
- JClassType singleConcrete = getSingleConcrete((JReferenceType) type,
- implementors);
+ JClassType singleConcrete = getSingleConcrete(
+ refType.getUnderlyingType(), implementors);
assert (singleConcrete == null || program.typeOracle.isInstantiatedType(singleConcrete));
- return singleConcrete;
+ if (singleConcrete == null) {
+ return null;
+ }
+ return refType.canBeNull() ? singleConcrete
+ : program.getNonNullType(singleConcrete);
}
}
return null;
@@ -715,7 +720,7 @@
}
// tighten based on leaf types
- JClassType leafType = getSingleConcreteType(refType);
+ JReferenceType leafType = getSingleConcreteType(refType);
if (leafType != null) {
x.setType(leafType);
myDidChange = true;
@@ -726,15 +731,13 @@
List<JReferenceType> typeList = new ArrayList<JReferenceType>();
/*
- * For non-parameters, always assume at least one null assignment; if
- * there really aren't any other assignments, then this variable will get
- * the null type. If there are, it won't hurt anything because null type
- * will always lose.
- *
- * For parameters, don't perform any tightening if we can't find any
- * actual assignments. The method should eventually get pruned.
+ * For fields without an initializer, add a null assignment, because the
+ * field might be accessed before initialized. Technically even a field
+ * with an initializer might be accessed before initialization, but
+ * presumably that is not the programmer's intent, so the compiler cheats
+ * and assumes the initial null will not be seen.
*/
- if (!(x instanceof JParameter)) {
+ if ((x instanceof JField) && !x.hasInitializer()) {
typeList.add(typeNull);
}
@@ -758,12 +761,22 @@
}
}
- if (typeList.isEmpty()) {
- return;
+ JReferenceType resultType;
+ if (!typeList.isEmpty()) {
+ resultType = program.generalizeTypes(typeList);
+ resultType = program.strongerType(refType, resultType);
+ } else {
+ if (x instanceof JParameter) {
+ /*
+ * There is no need to tighten unused parameters, because they will be
+ * pruned.
+ */
+ resultType = refType;
+ } else {
+ resultType = typeNull;
+ }
}
- JReferenceType resultType = program.generalizeTypes(typeList);
- resultType = program.strongerType(refType, resultType);
if (refType != resultType) {
x.setType(resultType);
myDidChange = true;
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 1a154a6..1f4bed4 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
@@ -15,8 +15,8 @@
*/
package com.google.gwt.dev.js;
+import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.impl.FragmentExtractor;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.js.ast.JsBlock;
@@ -121,7 +121,7 @@
private JsName nameToBillTo(JsVisitable node) {
if (node instanceof JsStatement) {
JsStatement stat = (JsStatement) node;
- JReferenceType type = map.typeForStatement(stat);
+ JClassType type = map.typeForStatement(stat);
if (type != null) {
return map.nameForType(type);
}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
new file mode 100644
index 0000000..38821ac
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2009 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;
+
+import com.google.gwt.dev.jjs.ast.JArrayType;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
+import com.google.gwt.dev.jjs.ast.JNonNullType;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JTypeOracle;
+
+import junit.framework.TestCase;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Test basic operations on the types used by the JJS compiler. See
+ * {@link com.google.gwt.dev.jjs.ast.JType}.
+ */
+public class JjsTypeTest extends TestCase {
+ private JArrayType arrayOfA;
+ private JArrayType arrayOfArrayOfB;
+ private JReferenceType arrayOfArrayOfInt;
+ private JArrayType arrayOfB;
+ private JArrayType arrayOfBSub;
+ private JArrayType arrayOfC;
+ private JReferenceType arrayOfInt;
+ private JArrayType arrayOfObject;
+ private JClassType classA;
+ private JClassType classB;
+ private JClassType classBase;
+ private JNonNullType classBaseNn;
+ private JNonNullType classBnn;
+ private JClassType classBSub;
+ private JClassType classC;
+ private JClassType classJso;
+ private JClassType classJso1;
+ private JClassType classJso2;
+ private JClassType classObject;
+ private JClassType classString;
+ private JInterfaceType intfI;
+ private JInterfaceType intfIBase;
+ private JInterfaceType intfJ;
+ private JProgram program;
+ private SourceInfo synthSource;
+ private JReferenceType typeNull;
+ private JTypeOracle typeOracle;
+
+ public void testCanTheoreticallyCast() {
+ assertFalse(typeOracle.canTheoreticallyCast(classBnn, typeNull));
+
+ assertTrue(typeOracle.canTheoreticallyCast(classBSub, classB));
+ assertTrue(typeOracle.canTheoreticallyCast(classB, classBSub));
+
+ assertTrue(typeOracle.canTheoreticallyCast(classB, classBnn));
+ assertTrue(typeOracle.canTheoreticallyCast(classBnn, classB));
+
+ assertTrue(typeOracle.canTheoreticallyCast(classB, classB));
+
+ assertTrue(typeOracle.canTheoreticallyCast(classObject, arrayOfB));
+ assertFalse(typeOracle.canTheoreticallyCast(arrayOfA, arrayOfArrayOfB));
+
+ assertTrue(typeOracle.canTheoreticallyCast(arrayOfObject, arrayOfArrayOfB));
+
+ assertTrue(typeOracle.canTheoreticallyCast(arrayOfB, arrayOfBSub));
+
+ assertTrue(typeOracle.canTheoreticallyCast(classBase, intfI));
+ assertFalse(typeOracle.canTheoreticallyCast(classA, intfJ));
+
+ assertTrue(typeOracle.canTheoreticallyCast(intfIBase, intfI));
+
+ assertTrue(typeOracle.canTheoreticallyCast(intfIBase, classBase));
+ assertFalse(typeOracle.canTheoreticallyCast(intfJ, classA));
+ }
+
+ public void testCanTriviallyCast() {
+ assertTrue(typeOracle.canTriviallyCast(classB, classB));
+
+ assertTrue(typeOracle.canTriviallyCast(classBSub, classB));
+ assertFalse(typeOracle.canTriviallyCast(classB, classBSub));
+
+ assertFalse(typeOracle.canTriviallyCast(classC, classA));
+ assertFalse(typeOracle.canTriviallyCast(classA, classC));
+
+ assertTrue(typeOracle.canTriviallyCast(classB, intfI));
+ assertFalse(typeOracle.canTriviallyCast(intfI, classB));
+
+ assertTrue(typeOracle.canTriviallyCast(classB, classObject));
+ assertFalse(typeOracle.canTriviallyCast(classObject, classB));
+
+ assertTrue(typeOracle.canTriviallyCast(classB, intfI));
+ assertFalse(typeOracle.canTriviallyCast(intfI, classB));
+
+ assertTrue(typeOracle.canTriviallyCast(classBnn, classB));
+ assertFalse(typeOracle.canTriviallyCast(classB, classBnn));
+
+ assertTrue(typeOracle.canTriviallyCast(typeNull, classB));
+ assertFalse(typeOracle.canTriviallyCast(classB, typeNull));
+
+ assertTrue(typeOracle.canTriviallyCast(arrayOfBSub, arrayOfB));
+ assertFalse(typeOracle.canTriviallyCast(arrayOfB, arrayOfBSub));
+
+ assertFalse(typeOracle.canTriviallyCast(arrayOfA, arrayOfB));
+ assertFalse(typeOracle.canTriviallyCast(arrayOfB, arrayOfA));
+
+ assertFalse(typeOracle.canTriviallyCast(arrayOfArrayOfB, arrayOfB));
+ assertFalse(typeOracle.canTriviallyCast(arrayOfB, arrayOfArrayOfB));
+
+ assertTrue(typeOracle.canTriviallyCast(arrayOfArrayOfB, arrayOfObject));
+ assertFalse(typeOracle.canTriviallyCast(arrayOfObject, arrayOfArrayOfB));
+
+ assertTrue(typeOracle.canTriviallyCast(classJso1, classJso2));
+ assertTrue(typeOracle.canTriviallyCast(classJso2, classJso1));
+
+ assertTrue(typeOracle.canTriviallyCast(classJso, classJso1));
+ assertTrue(typeOracle.canTriviallyCast(classJso, classJso1));
+
+ /*
+ * Test that two types cannot both be trivially castable to each other,
+ * unless they are the same type. Or, unless they are both JSOs.
+ */
+ for (JReferenceType type1 : severalTypes()) {
+ for (JReferenceType type2 : severalTypes()) {
+ if (type1 != type2) {
+ if (!isJso(type1) || !isJso(type2)) {
+ assertFalse(typeOracle.canTriviallyCast(type1, type2)
+ && typeOracle.canTriviallyCast(type2, type1));
+ }
+ }
+ }
+ }
+ }
+
+ public void testGeneralizeTypes() {
+ assertSame(classA, generalizeTypes(classA, classA));
+ assertSame(classB, generalizeTypes(classB, classBnn));
+ assertSame(classB, generalizeTypes(classBnn, classB));
+ assertSame(classBaseNn, generalizeTypes(classBnn, classBaseNn));
+ assertSame(classB, generalizeTypes(classB, typeNull));
+ assertSame(classB, generalizeTypes(typeNull, classB));
+
+ assertSame(intfIBase, generalizeTypes(intfI, intfIBase));
+ assertSame(intfIBase, generalizeTypes(intfIBase, intfI));
+ assertSame(classObject, generalizeTypes(intfJ, intfI));
+
+ assertSame(classObject, generalizeTypes(arrayOfB, arrayOfInt));
+ assertSame(classObject, generalizeTypes(arrayOfC, arrayOfArrayOfB));
+ assertSame(arrayOfObject, generalizeTypes(arrayOfC, arrayOfB));
+ assertSame(arrayOfObject, generalizeTypes(arrayOfObject, arrayOfArrayOfInt));
+
+ assertSame(intfI, generalizeTypes(classB, intfI));
+ assertSame(classObject, generalizeTypes(classB, intfJ));
+
+ assertSame(classObject, generalizeTypes(intfI, arrayOfInt));
+
+ for (JReferenceType type1 : severalTypes()) {
+ for (JReferenceType type2 : severalTypes()) {
+ JReferenceType generalized = generalizeTypes(type1, type2);
+ assertTrue(typeOracle.canTriviallyCast(type1, generalized));
+ assertTrue(typeOracle.canTriviallyCast(type2, generalized));
+ }
+ }
+ }
+
+ public void testStrongerType() {
+ assertSame(classA, program.strongerType(classA, classA));
+ assertSame(classBnn, program.strongerType(classB, classBnn));
+ assertSame(classB, program.strongerType(classB, classBase));
+ assertSame(classB, program.strongerType(classBase, classB));
+ assertSame(intfI, program.strongerType(intfI, intfJ));
+ }
+
+ @Override
+ protected void setUp() {
+ createSampleProgram();
+ }
+
+ private JClassType createClass(String className, JClassType superClass,
+ boolean isAbstract, boolean isFinal) {
+ JClassType clazz = program.createClass(synthSource, CharOperation.splitOn(
+ '.', className.toCharArray()), isAbstract, isFinal);
+ clazz.setSuperClass(superClass);
+ return clazz;
+ }
+
+ private JInterfaceType createInterface(String className) {
+ JInterfaceType intf = program.createInterface(synthSource,
+ CharOperation.splitOn('.', className.toCharArray()));
+ return intf;
+ }
+
+ private void createSampleProgram() {
+ // Make the program itself
+ program = new JProgram();
+ typeOracle = program.typeOracle;
+ synthSource = program.createSourceInfoSynthetic(JjsTypeTest.class,
+ "synthetic node used for testing");
+
+ classObject = createClass("java.lang.Object", null, false, false);
+ classString = createClass("java.lang.String", classObject, false, true);
+ classJso = createClass("com.google.gwt.core.client.JavaScriptObject",
+ classObject, false, false);
+
+ intfIBase = createInterface("IBase");
+
+ intfI = createInterface("I");
+ intfI.addImplements(intfIBase);
+
+ intfJ = createInterface("J");
+
+ classBase = createClass("Base", classObject, false, false);
+
+ classA = createClass("A", classBase, false, false);
+
+ classB = createClass("B", classBase, false, false);
+ classB.addImplements(intfI);
+
+ classC = createClass("C", classObject, false, false);
+ classC.addImplements(intfI);
+
+ classBSub = createClass("BSub", classB, false, false);
+
+ classJso1 = createClass("Jso1", classJso, false, false);
+ classJso2 = createClass("Jso2", classJso, false, false);
+
+ program.typeOracle.computeBeforeAST();
+
+ // Save off some miscellaneous types to test against
+ typeNull = program.getTypeNull();
+
+ classBnn = program.getNonNullType(classB);
+ classBaseNn = program.getNonNullType(classBase);
+
+ arrayOfA = program.getTypeArray(classA, 1);
+ arrayOfB = program.getTypeArray(classB, 1);
+ arrayOfBSub = program.getTypeArray(classBSub, 1);
+ arrayOfC = program.getTypeArray(classC, 1);
+ arrayOfObject = program.getTypeArray(classObject, 1);
+ arrayOfInt = program.getTypeArray(program.getTypePrimitiveInt(), 1);
+ arrayOfArrayOfInt = program.getTypeArray(program.getTypePrimitiveInt(), 2);
+
+ arrayOfArrayOfB = program.getTypeArray(classB, 2);
+ }
+
+ private JReferenceType generalizeTypes(JReferenceType type1,
+ JReferenceType type2) {
+ List<JReferenceType> types = new ArrayList<JReferenceType>(2);
+ types.add(type1);
+ types.add(type2);
+ return program.generalizeTypes(types);
+ }
+
+ private boolean isJso(JReferenceType type) {
+ return typeOracle.canTriviallyCast(type, classJso);
+ }
+
+ /**
+ * Return several types, for exhaustively testing basic properties.
+ */
+ private Collection<JReferenceType> severalTypes() {
+ List<JReferenceType> types = new ArrayList<JReferenceType>();
+ types.add(arrayOfA);
+ types.add(arrayOfB);
+ types.add(arrayOfArrayOfB);
+ types.add(arrayOfBSub);
+ types.add(arrayOfObject);
+ types.add(classA);
+ types.add(classB);
+ types.add(classBSub);
+ types.add(classBnn);
+ types.add(classBase);
+ types.add(classC);
+ types.add(classObject);
+ types.add(classString);
+ types.add(intfI);
+ types.add(intfJ);
+ types.add(intfIBase);
+ types.add(classJso1);
+ types.add(classJso2);
+ types.add(classJso);
+ types.add(typeNull);
+
+ return types;
+ }
+}