Handle correctly native member references in JSNI code.

Bug: #9520
Bug-Link: https://github.com/gwtproject/gwt/issues/9520
Change-Id: I22dda291caea460e31738f3902fe2c3fda03be57
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 c9cf9a4..8a8b69f 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
@@ -17,6 +17,7 @@
 
 import static com.google.gwt.dev.js.JsUtils.createAssignment;
 import static com.google.gwt.dev.js.JsUtils.createInvocationOrPropertyAccess;
+import static com.google.gwt.dev.js.JsUtils.createQualifiedNameRef;
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
@@ -693,7 +694,7 @@
 
     @Override
     public JsNode transformExpressionStatement(JExpressionStatement statement) {
-      return ((JsExpression) transform(statement.getExpr())).makeStmt();
+      return transform(statement.getExpr()).makeStmt();
     }
 
     @Override
@@ -1562,15 +1563,12 @@
 
           // Replace invocation to ctor with a new op.
           String ident = ref.getIdent();
-          JNode node = nodeByJsniReference.get(ident);
-          assert node instanceof JConstructor;
           assert ref.getQualifier() == null;
-          JsName jsName = names.get(node);
-          assert (jsName != null);
-          ref.resolve(jsName);
-          JsNew jsNew = new JsNew(x.getSourceInfo(), ref);
-          jsNew.getArguments().addAll(x.getArguments());
-          ctx.replaceMe(jsNew);
+
+          JConstructor constructor = (JConstructor) nodeByJsniReference.get(ident);
+          JsNameRef constructorJsName = createStaticReference(constructor, x.getSourceInfo());
+
+          ctx.replaceMe(new JsNew(x.getSourceInfo(), constructorJsName, x.getArguments()));
         }
 
         @Override
@@ -1584,6 +1582,12 @@
           assert (node != null);
           if (node instanceof JField) {
             JField field = (JField) node;
+
+            if (field.isStatic() && field.isJsNative()) {
+              ctx.replaceMe(createQualifiedNameRef(field.getQualifiedJsName(), x.getSourceInfo()));
+              return;
+            }
+
             JsName jsName = names.get(field);
             assert (jsName != null);
             x.resolve(jsName);
@@ -1600,27 +1604,30 @@
             } else {
               // Replace with a local closure function.
               // function(a,b,c){return new Obj(a,b,c);}
-              JConstructor ctor = (JConstructor) node;
-              JsName jsName = names.get(ctor);
-              assert (jsName != null);
-              x.resolve(jsName);
+              JConstructor constructor = (JConstructor) node;
               SourceInfo info = x.getSourceInfo();
-              JsFunction closureFunc = new JsFunction(info, function.getScope());
-              for (JParameter p : ctor.getParams()) {
-                JsName name = closureFunc.getScope().declareName(p.getName());
-                closureFunc.getParameters().add(new JsParameter(info, name));
+
+              JsNameRef constructorJsNameRef = createStaticReference(constructor, info);
+
+              JsFunction anonymousFunction = new JsFunction(info, function.getScope());
+              for (JParameter p : constructor.getParams()) {
+                JsName name = anonymousFunction.getScope().declareName(p.getName());
+                anonymousFunction.getParameters().add(new JsParameter(info, name));
               }
-              JsNew jsNew = new JsNew(info, x);
-              for (JsParameter p : closureFunc.getParameters()) {
+              JsNew jsNew = new JsNew(info, constructorJsNameRef);
+              for (JsParameter p : anonymousFunction.getParameters()) {
                 jsNew.getArguments().add(p.getName().makeRef(info));
               }
-              JsBlock block = new JsBlock(info);
-              block.getStatements().add(new JsReturn(info, jsNew));
-              closureFunc.setBody(block);
-              ctx.replaceMe(closureFunc);
+              anonymousFunction.setBody(new JsBlock(info));
+              anonymousFunction.getBody().getStatements().add(new JsReturn(info, jsNew));
+              ctx.replaceMe(anonymousFunction);
             }
           } else {
             JMethod method = (JMethod) node;
+            if (!method.needsDynamicDispatch() && method.isJsNative()) {
+              ctx.replaceMe(createGlobalQualifier(method.getQualifiedJsName(), x.getSourceInfo()));
+              return;
+            }
             if (x.getQualifier() == null) {
               JsName jsName = names.get(method);
               assert (jsName != null);
@@ -1726,7 +1733,7 @@
     }
 
     private JsNameRef createStaticReference(JMember member, SourceInfo sourceInfo) {
-      assert member.isStatic();
+      assert !member.needsDynamicDispatch();
       return member.isJsNative()
           ? createGlobalQualifier(member.getQualifiedJsName(), sourceInfo)
           : names.get(member).makeRef(sourceInfo);
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
index d9897a6..7f9d57e 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
@@ -401,4 +401,58 @@
     assertEquals("Hello", argumentsParameterClasher(1, "Hello", "GoodBye").get(0));
     assertEquals("Hello", argumentsVariableClasher(1, "Hello", "GoodBye").get(0));
   }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Array")
+  private static class NativeArray {
+    @JsProperty(name = "length")
+    public int len;
+    @JsMethod(name = "push")
+    public native void add(double element);
+  }
+
+  private static native Object newArray() /*-{
+    return @NativeArray::new()();
+  }-*/;
+
+  private static native int arrayLength(Object array) /*-{
+    return array.@NativeArray::len;
+  }-*/;
+
+  private static native void arrayAdd(Object array, double d) /*-{
+    array.@NativeArray::add(D)(d);
+    return array;
+  }-*/;
+
+  private static native Object newArrayThroughCtorReference() /*-{
+    var ctor =  @NativeArray::new();
+    return ctor();
+  }-*/;
+
+  private static native boolean isNan(double number) /*-{
+    return @Global::isNan(D)(number);
+  }-*/;
+
+  private static native double getNan() /*-{
+    return @Global::Nan;
+  }-*/;
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
+  private static class Global {
+    @JsProperty(namespace = JsPackage.GLOBAL, name = "NaN")
+    public static double Nan;
+    @JsMethod(namespace = JsPackage.GLOBAL, name = "isNaN")
+    public static native boolean isNan(double number);
+  }
+
+  // Regression tests for issue #9520.
+  public void testNativeConstructorJSNI() {
+    Object nativeArray = newArray();
+    arrayAdd(nativeArray, 0);
+    arrayAdd(nativeArray, 1);
+    assertTrue(nativeArray instanceof NativeArray);
+    assertEquals(2, arrayLength(nativeArray));
+    assertTrue(newArrayThroughCtorReference() instanceof NativeArray);
+    assertTrue(Double.isNaN(getNan()));
+    assertTrue(isNan(Double.NaN));
+  }
 }