Skip to content

Commit

Permalink
Handle correctly native member references in JSNI code.
Browse files Browse the repository at this point in the history
Bug: #9520
Bug-Link: #9520
Change-Id: I22dda291caea460e31738f3902fe2c3fda03be57
  • Loading branch information
rluble committed May 8, 2017
1 parent 1f6dfff commit 93508c6
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 24 deletions.
55 changes: 31 additions & 24 deletions dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
Expand Up @@ -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;
Expand Down Expand Up @@ -693,7 +694,7 @@ public JsNode transformDoStatement(JDoStatement doStatement) {

@Override
public JsNode transformExpressionStatement(JExpressionStatement statement) {
return ((JsExpression) transform(statement.getExpr())).makeStmt();
return transform(statement.getExpr()).makeStmt();
}

@Override
Expand Down Expand Up @@ -1562,15 +1563,12 @@ public void endVisit(JsInvocation x, JsContext ctx) {

// 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
Expand All @@ -1584,6 +1582,12 @@ public void endVisit(JsNameRef x, JsContext ctx) {
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);
Expand All @@ -1600,27 +1604,30 @@ public void endVisit(JsNameRef x, JsContext ctx) {
} 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);
Expand Down Expand Up @@ -1726,7 +1733,7 @@ private void checkForDuplicateMethods(JDeclaredType type) {
}

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);
Expand Down
Expand Up @@ -401,4 +401,58 @@ public void testVarargsNamedArguments() {
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));
}
}

0 comments on commit 93508c6

Please sign in to comment.