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