blob: 1b7267435ec7a2a00ec86e7425777ef3b2b49f86 [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.core.interop;
import static jsinterop.annotations.JsPackage.GLOBAL;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.junit.client.GWTTestCase;
import javaemul.internal.annotations.DoNotInline;
import jsinterop.annotations.JsFunction;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsPackage;
import jsinterop.annotations.JsType;
/**
* Tests JsType functionality.
*/
@SuppressWarnings("cast")
public class JsTypeVarargsTest extends GWTTestCase {
@Override
public String getModuleName() {
return "com.google.gwt.core.Interop";
}
@Override
protected void gwtSetUp() throws Exception {
ScriptInjector.fromString(
"function JsTypeVarargsTest_MyNativeJsType() {}\n"
+ "function JsTypeVarargsTest_MyNativeJsTypeVarargsConstructor(i) {"
+ " this.a = arguments[i]; this.b = arguments.length; }\n")
.setWindow(ScriptInjector.TOP_WINDOW)
.inject();
setupGlobal();
}
// $global always points to scope of exports
private native void setupGlobal() /*-{
$global = window.goog && window.goog.global || $wnd;
$wnd.$global = $global;
}-*/;
@JsMethod
@DoNotInline
public static native int varargsLengthThruArguments(Object... varargs) /*-{
return arguments.length;
}-*/;
@JsMethod
@DoNotInline
public static int varargsLength(Object... varargs) {
return varargs.length;
}
@JsMethod
@DoNotInline
public static int stringVarargsLength(String... varargs) {
return varargs.length;
}
@JsMethod
@DoNotInline
public static int stringVarargsLengthV2(int i, String... varargs) {
return varargs.length;
}
@JsMethod(namespace = JsPackage.GLOBAL)
@DoNotInline
public static Object getVarargsSlot(int slot, Object... varargs) {
return varargs[slot];
}
@JsMethod(namespace = JsPackage.GLOBAL)
@DoNotInline
public static Object[] clrearVarargsSlot(int slot, Object... varargs) {
varargs[slot] = null;
return varargs;
}
@JsMethod(namespace = JsPackage.GLOBAL)
@DoNotInline
public static Class<?> getVarargsArrayClass(String... varargs) {
return varargs.getClass();
}
private static native Object callGetVarargsSlotUsingJsName() /*-{
return $global.getVarargsSlot(2, "1", "2", "3", "4");
}-*/;
@JsType(isNative = true, namespace = GLOBAL, name = "Object")
static class NativeJsType {
}
@JsType(isNative = true, namespace = GLOBAL,
name = "JsTypeVarargsTest_MyNativeJsTypeVarargsConstructor")
static class NativeJsTypeWithVarargsConstructor {
public Object a;
public int b;
NativeJsTypeWithVarargsConstructor(int i, Object... args) { }
}
static class SubclassNativeWithVarargsConstructor extends NativeJsTypeWithVarargsConstructor {
SubclassNativeWithVarargsConstructor(int i, Object... args) {
super(i, args);
}
@JsMethod
Object varargsMethod(int i, Object... args) {
return args[i];
}
}
static class SubSubclassNativeWithVarargsConstructor
extends SubclassNativeWithVarargsConstructor {
SubSubclassNativeWithVarargsConstructor() {
super(0, new Object[0]);
}
Object varargsMethod(int i, Object... args) {
return super.varargsMethod(i, args);
}
Object nonJsVarargsMethod() {
return super.varargsMethod(1, null ,this);
}
}
public void testVarargsCall_regularMethods() {
assertEquals(3, varargsLengthThruArguments("A", "B", "C"));
assertEquals(4, varargsLength("A", "B", "C", "D"));
assertEquals(2, varargsLengthThruArguments(new NativeJsType[]{null, null}));
assertEquals(5, varargsLength(new NativeJsType[]{null, null, null, null, null}));
assertEquals("C", getVarargsSlot(2, "A", "B", "C", "D"));
assertEquals("3", callGetVarargsSlotUsingJsName());
assertNull(clrearVarargsSlot(1, "A", "B", "C")[1]);
assertEquals("A", clrearVarargsSlot(1, "A", "B", "C")[0]);
assertEquals(3, clrearVarargsSlot(1, "A", "B", "C").length);
assertSame(String[].class, getVarargsArrayClass("A", "B", "C"));
}
public void testVarargsCall_edgeCases() {
assertSame(String[].class, getVarargsArrayClass());
assertSame(String[].class, getVarargsArrayClass(new String[0]));
assertSame(String[].class, getVarargsArrayClass((String) null));
try {
assertSame(String[].class, getVarargsArrayClass(null));
fail("Should have thrown exception");
} catch (NullPointerException expected) {
}
try {
assertSame(String[].class, getVarargsArrayClass((String[]) null));
fail("Should have thrown exception");
} catch (NullPointerException expected) {
}
assertEquals(0, stringVarargsLength());
assertEquals(0, stringVarargsLength(new String[0]));
assertEquals(1, stringVarargsLength((String) null));
try {
assertEquals(0, stringVarargsLength(null));
fail("Should have thrown exception");
} catch (NullPointerException expected) {
}
try {
assertEquals(0, stringVarargsLength((String[]) null));
fail("Should have thrown exception");
} catch (NullPointerException expected) {
}
// Test with an additional parameter as it results in a slightly different call site.
assertEquals(0, stringVarargsLengthV2(0));
assertEquals(0, stringVarargsLengthV2(0, new String[0]));
assertEquals(1, stringVarargsLengthV2(0, (String) null));
try {
assertEquals(0, stringVarargsLengthV2(0, null));
fail("Should have thrown exception");
} catch (NullPointerException expected) {
}
try {
assertEquals(0, stringVarargsLengthV2(0, (String[]) null));
fail("Should have thrown exception");
} catch (NullPointerException expected) {
}
}
public void testVarargsCall_constructors() {
NativeJsType someNativeObject = new NativeJsType();
NativeJsTypeWithVarargsConstructor object =
new NativeJsTypeWithVarargsConstructor(1, someNativeObject, null);
assertSame(someNativeObject, object.a);
assertEquals(3, object.b);
Object[] params = new Object[] { someNativeObject, null };
object = new NativeJsTypeWithVarargsConstructor(1, params);
assertSame(someNativeObject, object.a);
assertEquals(3, object.b);
object = new SubclassNativeWithVarargsConstructor(1, someNativeObject, null);
assertSame(someNativeObject, object.a);
assertEquals(3, object.b);
}
@JsMethod(namespace = JsPackage.GLOBAL)
public static Double sumAndMultiply(Double multiplier, Double... numbers) {
double result = 0.0d;
for (double d : numbers) {
result += d;
}
result *= multiplier;
return result;
}
@JsMethod(namespace = JsPackage.GLOBAL)
public static int sumAndMultiplyInt(int multiplier, int... numbers) {
int result = 0;
for (int d : numbers) {
result += d;
}
result *= multiplier;
return result;
}
@JsFunction
interface Function {
Object f(int i, Object... args);
}
static final class AFunction implements Function {
@Override
public Object f(int i, Object... args) {
return args[i];
}
static Function create() {
return new AFunction();
}
}
public native void testVarargsCall_fromJavaScript() /*-{
@GWTTestCase::assertEquals(DDD)(60, $global.sumAndMultiply(2, 10, 20), 0);
@GWTTestCase::assertEquals(II)(30, $global.sumAndMultiplyInt(3, 2, 8));
var f = @JsTypeVarargsTest.AFunction::create()()
@GWTTestCase::assertSame(Ljava/lang/Object;Ljava/lang/Object;)(
f, f(2, null, null, f, null));
}-*/;
public void testVarargsCall_jsFunction() {
Function function = new AFunction();
assertSame(function, function.f(2, null, null, function, null));
assertSame(null, function.f(1, null, null, function, null));
}
public void testVarargsCall_superCalls() {
SubSubclassNativeWithVarargsConstructor object = new SubSubclassNativeWithVarargsConstructor();
assertSame(object, object.nonJsVarargsMethod());
assertSame(object, object.varargsMethod(1, null, object, null));
}
private static int sideEffectCount;
private SubclassNativeWithVarargsConstructor doSideEffect(
SubclassNativeWithVarargsConstructor obj) {
sideEffectCount++;
return obj;
}
public void testVarargsCall_sideEffectingInstance() {
SubclassNativeWithVarargsConstructor object =
new SubclassNativeWithVarargsConstructor(0, new Object[0]);
sideEffectCount = 0;
Object[] params = new Object[] { object, null };
assertSame(object, doSideEffect(object).varargsMethod(0, params));
assertSame(1, sideEffectCount);
}
static class UninstantiatedClass {
}
@JsMethod(namespace = JsPackage.GLOBAL)
public static int varargJsMethodUninstantiatedVararg(
UninstantiatedClass... varargs) {
return varargs.length;
}
public native void testVarargsCall_uninstantiatedVararg() /*-{
@GWTTestCase::assertEquals(II)(0, $global.varargJsMethodUninstantiatedVararg());
}-*/;
}