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