Fix NPE in SDM due to Boolean/Double/String devirtualization.
Devirtualization of these types requires that their methods be traversed
and available for rewriting.
Bug: #9424
Bug-Link: https://github.com/gwtproject/gwt/issues/9424
Change-Id: I3addaae1de1e178c5b1276c7804fbf29686ff8f1
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 f749516..65aca8c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -328,9 +328,6 @@
// TODO(stalcup): hide metrics gathering in a callback or subclass
logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "...");
- // Rewrite calls to from boxed constructor types to specialized unboxed methods
- RewriteConstructorCallsForUnboxedTypes.exec(jprogram);
-
// (2) Transform unresolved Java AST to resolved Java AST
ResolvePermutationDependentValues
.exec(jprogram, properties, permutation.getPropertyAndBindingInfos());
@@ -351,6 +348,9 @@
// record references to runtime classes like LongLib).
maybeRecordReferencesAndControlFlow(false);
+ // Rewrite calls to from boxed constructor types to specialized unboxed methods
+ RewriteConstructorCallsForUnboxedTypes.exec(jprogram);
+
// Replace compile time constants by their values.
// TODO(rluble): eventually move to normizeSemantics.
CompileTimeConstantsReplacer.exec(jprogram);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/RewriteConstructorCallsForUnboxedTypes.java b/dev/core/src/com/google/gwt/dev/jjs/impl/RewriteConstructorCallsForUnboxedTypes.java
index b9a9a72..f79311d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/RewriteConstructorCallsForUnboxedTypes.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/RewriteConstructorCallsForUnboxedTypes.java
@@ -23,10 +23,14 @@
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+import com.google.gwt.thirdparty.guava.common.base.Function;
+import com.google.gwt.thirdparty.guava.common.base.Joiner;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import java.util.HashMap;
import java.util.Map;
@@ -49,7 +53,7 @@
createMethodsByType.put(unboxedType, createMethods);
for (JMethod method : unboxedType.getMethods()) {
if (method.getName().startsWith(NATIVE_TYPE_CREATEMETHOD_PREFIX)) {
- createMethods.put(method.getOriginalParamTypes().toString(), method);
+ createMethods.put(getParametersAsString(method), method);
}
}
}
@@ -67,7 +71,7 @@
JMethod createMethod =
createMethodsByType
.get(ctor.getEnclosingType())
- .get(ctor.getOriginalParamTypes().toString());
+ .get(getParametersAsString(ctor));
assert createMethod != null;
JMethodCall createCall = new JMethodCall(x.getSourceInfo(), null, createMethod);
@@ -84,7 +88,7 @@
JMethod createMethod =
createMethodsByType
.get(ctor.getEnclosingType())
- .get(ctor.getOriginalParamTypes().toString());
+ .get(getParametersAsString(ctor));
assert createMethod != null;
JsniMethodRef newJsniMethodRef = new JsniMethodRef(x.getSourceInfo(),
@@ -93,6 +97,16 @@
}
}
+ private static String getParametersAsString(JMethod method) {
+ return Joiner.on(",").join(Iterables.transform(method.getOriginalParamTypes(),
+ new Function<JType, String>() {
+ @Override
+ public String apply(JType type) {
+ return type.getJsniSignatureName();
+ }
+ }));
+ }
+
private static final String NAME = RewriteConstructorCallsForUnboxedTypes.class
.getSimpleName();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
index 94a6c6c..9e902b0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
@@ -825,12 +825,13 @@
// visitor execution after unification. Since we don't want those fields are methods to be
// prematurely pruned here we defensively trace them now.
for (JClassType type : program.codeGenTypes) {
- for (JMethod method : type.getMethods()) {
- flowInto(method);
- }
- for (JField field : type.getFields()) {
- flowInto(field);
- }
+ flowInto(type);
+ }
+
+ // Make sure that the rewriting pass for the types that are represented as natives have the
+ // needed members available.
+ for (JDeclaredType type : program.getRepresentedAsNativeTypes()) {
+ flowInto(type);
}
if (incrementalCompile) {
@@ -1124,12 +1125,16 @@
// attempt is shorter.
processedStaleTypeNames.add(typeName);
instantiate(type);
- for (JField field : type.getFields()) {
- flowInto(field);
- }
+ flowInto(type);
+ }
+
+ private void flowInto(JDeclaredType type) {
for (JMethod method : type.getMethods()) {
flowInto(method);
}
+ for (JField field : type.getFields()) {
+ flowInto(field);
+ }
}
private void flowInto(JField field) {
diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java
index 51b0275..196e568 100644
--- a/dev/core/test/com/google/gwt/dev/CompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java
@@ -1047,6 +1047,7 @@
public void testChangeJsNamespaceOnMethod() throws Exception {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setGenerateJsInteropExports(true);
MockJavaResource jsNamespaceFooResource =
JavaResourceBase.createMockJavaResource(
@@ -1086,6 +1087,7 @@
public void testChangeJsNamespaceOnClass() throws Exception {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setGenerateJsInteropExports(true);
MockJavaResource jsNamespaceFooResource =
JavaResourceBase.createMockJavaResource(
@@ -1124,6 +1126,7 @@
public void testChangeJsFunction() throws Exception {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setGenerateJsInteropExports(true);
MockJavaResource jsFunctionIFooResource =
JavaResourceBase.createMockJavaResource(
@@ -1170,6 +1173,7 @@
public void testChangeJsProperty() throws Exception {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setGenerateJsInteropExports(true);
MockJavaResource jsPropertyIFooResource =
JavaResourceBase.createMockJavaResource(
@@ -1222,6 +1226,7 @@
public void testChangeJsType() throws Exception {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setGenerateJsInteropExports(true);
MockJavaResource jsTypeFooResource =
JavaResourceBase.createMockJavaResource(
@@ -1256,6 +1261,7 @@
public void testChangeJsTypeNative() throws Exception {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setGenerateJsInteropExports(true);
MockJavaResource nativeFooResource =
JavaResourceBase.createMockJavaResource(
@@ -1295,6 +1301,7 @@
public void testChangeJsIgnore() throws Exception {
CompilerOptions compilerOptions = new CompilerOptionsImpl();
compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setGenerateJsInteropExports(true);
MockJavaResource jsIgnoreFooResource =
JavaResourceBase.createMockJavaResource(
@@ -1329,6 +1336,7 @@
MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache();
File applicationDir = Files.createTempDir();
CompilerOptions compilerOptions = new CompilerOptionsImpl();
+ compilerOptions.setGenerateJsInteropExports(true);
// Simple compile with one dialog.alert() export succeeds.
compileToJs(compilerOptions, applicationDir, "com.foo.SimpleModule", Lists.newArrayList(
@@ -1590,7 +1598,6 @@
"<module>",
" <source path=''/>",
" <entry-point class='com.foo.ErrorsEntryPoint'/>",
- " <add-linker name='xsiframe'/>",
"</module>");
MockJavaResource entryPointResource =
@@ -1652,6 +1659,63 @@
}
}
+ public void testIncrementalRecompile_representedAsNative()
+ throws UnableToCompleteException, IOException, InterruptedException {
+ MockResource moduleResource =
+ JavaResourceBase.createMockResource(
+ "com/foo/RepresentedAsNative.gwt.xml",
+ "<module>",
+ " <source path=''/>",
+ " <entry-point class='com.foo.RepresentedAsNativeEntryPoint'/>",
+ "</module>");
+
+ MockResource entryPointResource =
+ JavaResourceBase.createMockJavaResource(
+ "com.foo.RepresentedAsNativeEntryPoint",
+ "package com.foo;",
+ "import com.google.gwt.core.client.EntryPoint;",
+ "public class RepresentedAsNativeEntryPoint implements EntryPoint {",
+ " public void onModuleLoad() {",
+ " Double d = new Double(1d);",
+ " }",
+ "}");
+
+ MockResource modifiedEntryPointResource =
+ JavaResourceBase.createMockJavaResource(
+ "com.foo.RepresentedAsNativeEntryPoint",
+ "package com.foo;",
+ "import com.google.gwt.core.client.EntryPoint;",
+ "public class RepresentedAsNativeEntryPoint implements EntryPoint {",
+ " public void onModuleLoad() {",
+ " Double d = new Double(\"1\");",
+ " }",
+ "}");
+
+ PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
+ logger.setMaxDetail(TreeLogger.ERROR);
+
+ MinimalRebuildCache minimalRebuildCache = new MinimalRebuildCache();
+ File applicationDir = Files.createTempDir();
+ CompilerOptions compilerOptions = new CompilerOptionsImpl();
+ compilerOptions.setUseDetailedTypeIds(true);
+ compilerOptions.setSourceLevel(SourceLevel.JAVA8);
+ compilerOptions.setGenerateJsInteropExports(false);
+
+ // Compile the application with no errors.
+ compileToJs(logger, compilerOptions, applicationDir, "com.foo.RepresentedAsNative",
+ Lists.newArrayList(moduleResource, entryPointResource), minimalRebuildCache,
+ emptySet, JsOutputOption.OBFUSCATED);
+
+ // Recompile but now the changed file has an error
+ compileToJs(logger, compilerOptions, applicationDir, "com.foo.RepresentedAsNative",
+ Lists.newArrayList(modifiedEntryPointResource),
+ minimalRebuildCache,
+ stringSet(
+ "com.foo.RepresentedAsNativeEntryPoint",
+ getEntryMethodHolderTypeName("com.foo.RepresentedAsNative")),
+ JsOutputOption.OBFUSCATED);
+ }
+
public void testIncrementalRecompile_functionSignatureChange() throws UnableToCompleteException,
IOException, InterruptedException {
// Not testing recompile equality with Pretty/Obfuscated output since the JsIncrementalNamer's
@@ -2570,7 +2634,6 @@
// Setup options to perform a per-file compile, output to this new application directory and
// compile the given module.
compilerOptions.setIncrementalCompileEnabled(true);
- compilerOptions.setGenerateJsInteropExports(true);
compilerOptions.setWarDir(applicationDir);
compilerOptions.setModuleNames(ImmutableList.of(moduleName));
compilerOptions.setOutput(output);