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);