| /* |
| * 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 { |
| NativeJsType() { } |
| NativeJsType(int j, NativeJsType... args) { } |
| } |
| |
| @JsType(isNative = true, namespace = GLOBAL, |
| name = "JsTypeVarargsTest_MyNativeJsTypeVarargsConstructor") |
| static class NativeJsTypeWithVarargsConstructor extends NativeJsType { |
| public Object a; |
| public int b; |
| NativeJsTypeWithVarargsConstructor(int i, Object... args) { } |
| |
| NativeJsTypeWithVarargsConstructor(int i, NativeJsType... args) { |
| super(1, 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 NativeJsType[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 SubclassNativeWithNativeVarargsConstructor |
| extends NativeJsTypeWithVarargsConstructor { |
| public NativeJsType[] ctorargs; |
| SubclassNativeWithNativeVarargsConstructor(int i, NativeJsType... args) { |
| super(i, args); |
| ctorargs = args; |
| } |
| } |
| |
| public void testVarargsCall_nativeVarargs() { |
| SubclassNativeWithNativeVarargsConstructor object = |
| new SubclassNativeWithNativeVarargsConstructor(0, new NativeJsType[0]); |
| assertEquals(0, object.ctorargs.length); |
| } |
| |
| 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()); |
| }-*/; |
| } |