| /* |
| * Copyright 2009 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.dev.jjs.test; |
| |
| import com.google.gwt.core.client.JavaScriptObject; |
| import com.google.gwt.dev.jjs.test.SingleJsoImplTest.JsoHasInnerJsoType.InnerType; |
| import com.google.gwt.dev.jjs.test.jsointfs.JsoInterfaceWithUnreferencedImpl; |
| import com.google.gwt.junit.client.GWTTestCase; |
| import com.google.gwt.user.client.rpc.AsyncCallback; |
| |
| import java.io.IOException; |
| |
| /** |
| * Ensures that JavaScriptObjects may implement interfaces with methods. |
| */ |
| public class SingleJsoImplTest extends GWTTestCase { |
| |
| interface Adder { |
| int ADDER_CONST = 6; |
| |
| /** |
| * @see SameDescriptors#SAME_NAME |
| */ |
| String SAME_NAME = "Same Name"; |
| |
| Object DIFFERENT_OBJECT = new Object(); |
| Object SAME_OBJECT = new Object(); |
| Object SAME_OBJECT2 = SAME_OBJECT; |
| |
| /* |
| * NB: Picking a mix of double and int because doubles have size 2 on the |
| * stack, so this ensures that we're handling that case correctly. |
| */ |
| double add(double a, int b); |
| |
| /* |
| * Ensure that we can return types whose sizes are larger than the size of |
| * the arguments. |
| */ |
| long returnLong(); |
| } |
| |
| interface CallsMethodInInnerType { |
| interface InnerInterface { |
| void call(int a); |
| |
| int get(); |
| } |
| |
| InnerInterface call(InnerInterface o, int a); |
| } |
| |
| interface CallsStaticMethodInSubclass { |
| String call(int a, int b); |
| } |
| |
| interface CreatedWithCast { |
| String foo(); |
| } |
| |
| interface CreatedWithCastToTag { |
| } |
| |
| interface CreatedWithCastToTagSub extends CreatedWithCastToTag { |
| String foo(); |
| } |
| |
| interface Divider extends Multiplier { |
| int divide(int a, int b); |
| } |
| |
| static class JavaAdder implements Adder { |
| @Override |
| public double add(double a, int b) { |
| return a + b; |
| } |
| |
| @Override |
| public long returnLong() { |
| return 5L; |
| } |
| } |
| |
| /** |
| * Even though this type is never instantiated, it's necessary to ensure that |
| * the CreatedWithCast test isn't short-circuited due to type tightening. |
| */ |
| static class JavaCreatedWithCast implements CreatedWithCast { |
| @Override |
| public String foo() { |
| return "foo"; |
| } |
| } |
| |
| static class JavaCreatedWithCastToTag implements CreatedWithCastToTagSub { |
| @Override |
| public String foo() { |
| return "foo"; |
| } |
| } |
| |
| /** |
| * The extra declaration of implementing Multiplier should still be legal. |
| */ |
| static class JavaDivider extends JavaMultiplier implements Divider, |
| Multiplier, Tag { |
| @Override |
| public int divide(int a, int b) { |
| return a / b; |
| } |
| } |
| |
| /** |
| * Ensure that SingleJsoImpl interfaces can be extended and implemented by |
| * regular Java types. |
| */ |
| static class JavaLog2 extends JavaDivider implements Log2 { |
| @Override |
| public double log2(int a) { |
| return Math.log(a) / Math.log(2); |
| } |
| } |
| |
| static class JavaMultiplier implements Multiplier { |
| @Override |
| public int multiply(int a, int b) { |
| return a * b; |
| } |
| } |
| |
| static class JavaUsesArrays implements UsesArrays { |
| @Override |
| public void acceptInt3Array(int[][][] arr) { |
| assertTrue(arr.length == 3); |
| } |
| |
| @Override |
| public void acceptIntArray(int[] arr) { |
| assertTrue(arr.length == 1); |
| } |
| |
| @Override |
| public void acceptObject3Array(Object[][][] arr) { |
| assertTrue(arr.length == 3); |
| } |
| |
| @Override |
| public void acceptObjectArray(Object[] arr) { |
| assertTrue(arr.length == 1); |
| } |
| |
| @Override |
| public void acceptString3Array(String[][][] arr) { |
| assertTrue(arr.length == 3); |
| } |
| |
| @Override |
| public void acceptStringArray(String[] arr) { |
| assertTrue(arr.length == 1); |
| } |
| |
| @Override |
| public int[][][] returnInt3Array() { |
| return new int[3][2][1]; |
| } |
| |
| @Override |
| public int[] returnIntArray() { |
| return new int[1]; |
| } |
| |
| @Override |
| public Object[][][] returnObject3Array() { |
| return new Object[3][2][1]; |
| } |
| |
| @Override |
| public Object[] returnObjectArray() { |
| return new Object[1]; |
| } |
| |
| @Override |
| public String[][][] returnString3Array() { |
| return new String[3][2][1]; |
| } |
| |
| @Override |
| public String[] returnStringArray() { |
| return new String[1]; |
| } |
| } |
| |
| static class JsoAdder extends JavaScriptObject implements Adder { |
| protected JsoAdder() { |
| } |
| |
| @Override |
| public final native double add(double a, int b) /*-{ |
| return this.offset * (a + b); |
| }-*/; |
| |
| @Override |
| public final long returnLong() { |
| return 5L; |
| } |
| } |
| |
| static class JsoCallsStaticMethodInSubclass extends JavaScriptObject |
| implements CallsStaticMethodInSubclass { |
| protected JsoCallsStaticMethodInSubclass() { |
| } |
| |
| @Override |
| public final native String call(int a, int b) /*-{ |
| return "foo" + @com.google.gwt.dev.jjs.test.SingleJsoImplTest.JsoCallsStaticMethodInSubclassSubclass::actual(II)(a, b); |
| }-*/; |
| } |
| |
| static class JsoCallsStaticMethodInSubclassSubclass extends |
| JsoCallsStaticMethodInSubclass { |
| public static String actual(int a, int b) { |
| return String.valueOf(a + b); |
| } |
| |
| protected JsoCallsStaticMethodInSubclassSubclass() { |
| } |
| } |
| |
| static class JsoCreatedWithCast extends JavaScriptObject implements |
| CreatedWithCast { |
| protected JsoCreatedWithCast() { |
| } |
| |
| @Override |
| public final String foo() { |
| return "foo"; |
| } |
| } |
| |
| static class JsoCreatedWithCastToTag extends JavaScriptObject implements |
| CreatedWithCastToTagSub { |
| protected JsoCreatedWithCastToTag() { |
| } |
| |
| @Override |
| public final String foo() { |
| return "foo"; |
| } |
| } |
| |
| static class JsoDivider extends JsoMultiplier implements Divider, Tag { |
| protected JsoDivider() { |
| } |
| |
| @Override |
| public final native int divide(int a, int b) /*-{ |
| return this.offset * a / b; |
| }-*/; |
| } |
| |
| static class JsoHasInnerJsoType extends JavaScriptObject implements |
| CallsMethodInInnerType { |
| static class InnerType extends JavaScriptObject implements InnerInterface { |
| protected InnerType() { |
| } |
| |
| @Override |
| public final native void call(int a) /*-{ |
| this.foo = a; |
| }-*/; |
| |
| @Override |
| public final native int get() /*-{ |
| return this.foo; |
| }-*/; |
| } |
| |
| protected JsoHasInnerJsoType() { |
| } |
| |
| @Override |
| public final InnerInterface call(InnerInterface o, int a) { |
| o.call(a); |
| return o; |
| } |
| } |
| |
| static class JsoMultiplier extends JavaScriptObject implements Multiplier { |
| protected JsoMultiplier() { |
| } |
| |
| @Override |
| public final native int multiply(int a, int b) /*-{ |
| return this.offset * a * b; |
| }-*/; |
| } |
| |
| /** |
| * Just a random JSO type for testing cross-casting. |
| */ |
| final static class JsoRandom extends JavaScriptObject implements |
| SameDescriptors, Tag { |
| protected JsoRandom() { |
| } |
| |
| @Override |
| public int add(int a, int b) { |
| return -1; |
| } |
| |
| @Override |
| public int divide(int a, int b) { |
| return -1; |
| } |
| |
| @Override |
| public int multiply(int a, int b) { |
| return -1; |
| } |
| |
| String b() { |
| return "b"; |
| } |
| } |
| |
| final static class JsoSimple extends JavaScriptObject implements Simple { |
| protected JsoSimple() { |
| } |
| |
| @Override |
| public String a() { |
| return "a"; |
| } |
| |
| @Override |
| public String a(boolean overload) { |
| return overload ? "Kaboom!" : "OK"; |
| } |
| |
| @Override |
| public String ex() throws IOException { |
| throw new IOException(); |
| } |
| |
| @Override |
| public String rte() { |
| throw new IllegalArgumentException(); |
| } |
| } |
| |
| static final class JsoUsesArrays extends JavaScriptObject implements |
| UsesArrays { |
| protected JsoUsesArrays() { |
| } |
| |
| @Override |
| public void acceptInt3Array(int[][][] arr) { |
| assertTrue(arr.length == 3); |
| } |
| |
| @Override |
| public void acceptIntArray(int[] arr) { |
| assertTrue(arr.length == 1); |
| } |
| |
| @Override |
| public void acceptObject3Array(Object[][][] arr) { |
| assertTrue(arr.length == 3); |
| } |
| |
| @Override |
| public void acceptObjectArray(Object[] arr) { |
| assertTrue(arr.length == 1); |
| } |
| |
| @Override |
| public void acceptString3Array(String[][][] arr) { |
| assertTrue(arr.length == 3); |
| } |
| |
| @Override |
| public void acceptStringArray(String[] arr) { |
| assertTrue(arr.length == 1); |
| } |
| |
| @Override |
| public int[][][] returnInt3Array() { |
| return new int[3][2][1]; |
| } |
| |
| @Override |
| public int[] returnIntArray() { |
| return new int[1]; |
| } |
| |
| @Override |
| public Object[][][] returnObject3Array() { |
| return new Object[3][2][1]; |
| } |
| |
| @Override |
| public Object[] returnObjectArray() { |
| return new Object[1]; |
| } |
| |
| @Override |
| public String[][][] returnString3Array() { |
| return new String[3][2][1]; |
| } |
| |
| @Override |
| public String[] returnStringArray() { |
| return new String[1]; |
| } |
| } |
| |
| static class JsoUsesGeneric extends JavaScriptObject implements UsesGenerics { |
| public static native <T extends CharSequence> JsoUsesGeneric create() /*-{ |
| return {suffix : "42"}; |
| }-*/; |
| |
| protected JsoUsesGeneric() { |
| } |
| |
| @Override |
| public final native <T> String acceptsGeneric(T chars) /*-{ |
| return chars + this.suffix; |
| }-*/; |
| |
| @Override |
| public final native <T> void callback(AsyncCallback<T> callback, T chars) /*-{ |
| callback.@com.google.gwt.user.client.rpc.AsyncCallback::onSuccess(Ljava/lang/Object;)(chars + this.suffix); |
| }-*/; |
| |
| /** |
| * What you're seeing here is would be achieved by an unsafe (T) cast and |
| * would break with a ClassCastException if accessed via JsoIsGenericFinal |
| * in normal Java. |
| */ |
| @Override |
| public final native <T> T returnsGeneric(String chars) /*-{ |
| return chars + this.suffix; |
| }-*/; |
| } |
| |
| /** |
| * Ensure that SingleJsoImpl interfaces can be extended and implemented by |
| * regular Java types. |
| */ |
| interface Log2 extends Divider { |
| double log2(int a); |
| } |
| |
| interface Multiplier { |
| int multiply(int a, int b); |
| } |
| |
| /** |
| * This interface makes sure that types with identical method signatures will |
| * still dispatch correctly. |
| */ |
| interface SameDescriptors { |
| int SAME_NAME = 6; |
| |
| int add(int a, int b); |
| |
| int divide(int a, int b); |
| |
| int multiply(int a, int b); |
| } |
| |
| interface Simple { |
| String a(); |
| |
| String a(boolean overload); |
| |
| String ex() throws IOException; |
| |
| String rte(); |
| } |
| |
| /** |
| * An interface that has dual JSO and non-JSO implementations. |
| */ |
| interface DualSimple { |
| String a(); |
| } |
| |
| static class JavaDualSimple implements DualSimple { |
| @Override |
| public String a() { |
| return "object"; |
| } |
| } |
| |
| static final class JsoDualSimple extends JavaScriptObject implements DualSimple { |
| protected JsoDualSimple() { |
| } |
| |
| @Override |
| public String a() { |
| return "jso"; |
| } |
| } |
| |
| /** |
| * Ensure that a Java-only implementation of a SingleJsoImpl interface works. |
| */ |
| static class SimpleOnlyJava implements SimpleOnlyJavaInterface { |
| @Override |
| public String simpleOnlyJava() { |
| return "simpleOnlyJava"; |
| } |
| } |
| |
| interface SimpleOnlyJavaInterface { |
| String simpleOnlyJava(); |
| } |
| interface Tag { |
| } |
| |
| /** |
| * Ensure that arrays are valid return and parameter types. |
| */ |
| interface UsesArrays { |
| void acceptInt3Array(int[][][] arr); |
| |
| void acceptIntArray(int[] arr); |
| |
| void acceptObject3Array(Object[][][] arr); |
| |
| void acceptObjectArray(Object[] arr); |
| |
| void acceptString3Array(String[][][] arr); |
| |
| void acceptStringArray(String[] arr); |
| |
| int[][][] returnInt3Array(); |
| |
| int[] returnIntArray(); |
| |
| Object[][][] returnObject3Array(); |
| |
| Object[] returnObjectArray(); |
| |
| String[][][] returnString3Array(); |
| |
| String[] returnStringArray(); |
| } |
| |
| interface UsesGenerics { |
| <T extends Object> String acceptsGeneric(T chars); |
| |
| <T> void callback(AsyncCallback<T> callback, T chars); |
| |
| <T> T returnsGeneric(String chars); |
| } |
| |
| private static native JsoAdder makeAdder(int offset) /*-{ |
| return {offset:offset}; |
| }-*/; |
| |
| private static native JsoDivider makeDivider(int offset) /*-{ |
| return {offset:offset}; |
| }-*/; |
| |
| private static native JsoMultiplier makeMultiplier(int offset) /*-{ |
| return {offset:offset}; |
| }-*/; |
| |
| private static native JsoSimple makeSimple() /*-{ |
| return {}; |
| }-*/; |
| |
| interface JsFunction { |
| int call(); |
| } |
| |
| final static class JsFunctionImpl extends JavaScriptObject implements JsFunction { |
| protected JsFunctionImpl() { |
| } |
| |
| static native JsFunction makeFunc() /*-{ |
| return function() { return 42; }; |
| }-*/; |
| |
| public native int call() /*-{ |
| return this(); |
| }-*/; |
| } |
| |
| @Override |
| public String getModuleName() { |
| return "com.google.gwt.dev.jjs.CompilerSuite"; |
| } |
| |
| /* |
| * These "testAssign*" tests below are inspired by the issue reported here: |
| * |
| * @see http://code.google.com/p/google-web-toolkit/issues/detail?id=6448 |
| */ |
| public void testAssignToDualJavaJsoImplInterfaceArray() { |
| int i = 0; |
| DualSimple[] dualSimples = new DualSimple[10]; |
| |
| DualSimple dualSimple = (DualSimple) JavaScriptObject.createObject(); |
| dualSimples[i] = dualSimple; |
| assertEquals("jso", dualSimples[i++].a()); |
| |
| JsoDualSimple jsoDualSimple = (JsoDualSimple) JavaScriptObject.createObject(); |
| dualSimples[i] = jsoDualSimple; |
| assertEquals("jso", dualSimples[i++].a()); |
| |
| DualSimple javaDualSimple = new JavaDualSimple(); |
| dualSimples[i] = javaDualSimple; |
| assertEquals("object", dualSimples[i++].a()); |
| |
| Object[] objects = dualSimples; |
| objects[i++] = dualSimple; |
| objects[i++] = jsoDualSimple; |
| objects[i++] = javaDualSimple; |
| try { |
| objects[i++] = new Object(); |
| fail("ArrayStoreException expected"); |
| } catch (ArrayStoreException expected) { |
| } |
| } |
| |
| public void testAssignToJsoArray() { |
| int i = 0; |
| JsoDualSimple[] jsoDualSimples = new JsoDualSimple[10]; |
| |
| JsoDualSimple jsoDualSimple = (JsoDualSimple) JavaScriptObject.createObject(); |
| jsoDualSimples[i] = jsoDualSimple; |
| assertEquals("jso", jsoDualSimples[i++].a()); |
| |
| DualSimple dualSimple = (DualSimple) JavaScriptObject.createObject(); |
| jsoDualSimples[i] = (JsoDualSimple) dualSimple; |
| assertEquals("jso", jsoDualSimples[i++].a()); |
| |
| DualSimple[] dualSimples = jsoDualSimples; |
| try { |
| DualSimple javaDualSimple = new JavaDualSimple(); |
| dualSimples[i++] = javaDualSimple; |
| fail("ArrayStoreException expected"); |
| } catch (ArrayStoreException expected) { |
| } |
| } |
| |
| public void testAssignToJavaArray() { |
| int i = 0; |
| JavaDualSimple[] javaDualSimples = new JavaDualSimple[10]; |
| |
| JavaDualSimple javaDualSimple = new JavaDualSimple(); |
| javaDualSimples[i] = javaDualSimple; |
| assertEquals("object", javaDualSimples[i++].a()); |
| |
| DualSimple[] dualSimples = javaDualSimples; |
| try { |
| DualSimple jsoDualSimple = (DualSimple) JavaScriptObject.createObject(); |
| dualSimples[i++] = jsoDualSimple; |
| fail("ArrayStoreException expected"); |
| } catch (ArrayStoreException expected) { |
| } |
| } |
| |
| public void testAssignToObjectArray() { |
| int i = 0; |
| Object[] objects = new Object[10]; |
| |
| Simple simple = (Simple) JavaScriptObject.createObject(); |
| objects[i++] = simple; |
| |
| JsoSimple jsoSimple = (JsoSimple) JavaScriptObject.createObject(); |
| objects[i++] = jsoSimple; |
| |
| JavaScriptObject jso = JavaScriptObject.createObject(); |
| objects[i++] = jso; |
| |
| Object object = new Object(); |
| objects[i++] = object; |
| |
| Integer integer = new Integer(1); |
| objects[i++] = integer; |
| } |
| |
| public void testAssignToSingleJsoImplInterfaceArray() { |
| int i = 0; |
| Simple[] simples = new Simple[10]; |
| Simple simple = (Simple) JavaScriptObject.createObject(); |
| simples[i] = simple; |
| assertEquals("a", simples[i++].a()); |
| |
| Simple jsoSimple = (JsoSimple) JavaScriptObject.createObject(); |
| simples[i] = jsoSimple; |
| assertEquals("a", simples[i++].a()); |
| |
| Object[] objects = simples; |
| try { |
| objects[i++] = new Object(); |
| fail("ArrayStoreException expected"); |
| } catch (ArrayStoreException expected) { |
| } |
| } |
| |
| public void testCallsToInnerTypes() { |
| CallsMethodInInnerType a = (CallsMethodInInnerType) JavaScriptObject.createObject(); |
| InnerType i = (InnerType) JavaScriptObject.createObject(); |
| assertEquals(5, a.call(i, 5).get()); |
| assertEquals(5, i.get()); |
| } |
| |
| public void testCallsWithArrays() { |
| UsesArrays a = JavaScriptObject.createObject().<JsoUsesArrays> cast(); |
| a.acceptIntArray(a.returnIntArray()); |
| a.acceptInt3Array(a.returnInt3Array()); |
| a.acceptStringArray(a.returnStringArray()); |
| a.acceptString3Array(a.returnString3Array()); |
| a.acceptObjectArray(a.returnStringArray()); |
| a.acceptObject3Array(a.returnString3Array()); |
| |
| a = new JavaUsesArrays(); |
| a.acceptIntArray(a.returnIntArray()); |
| a.acceptInt3Array(a.returnInt3Array()); |
| a.acceptStringArray(a.returnStringArray()); |
| a.acceptString3Array(a.returnString3Array()); |
| a.acceptObjectArray(a.returnStringArray()); |
| a.acceptObject3Array(a.returnString3Array()); |
| } |
| |
| /** |
| * Ensure that SingleJSO types that are referred to only via a cast to the |
| * interface type are retained. If the JsoCreatedWithCast type isn't rescued |
| * correctly, the cast in this test will throw a ClassCastException since the |
| * compiler would assume there are types that implement the interface. |
| */ |
| public void testCreatedWithCast() { |
| try { |
| Object a = (CreatedWithCast) JavaScriptObject.createObject(); |
| } catch (ClassCastException e) { |
| fail("a"); |
| } |
| try { |
| Object b = (CreatedWithCastToTag) JavaScriptObject.createObject(); |
| } catch (ClassCastException e) { |
| fail("b"); |
| } |
| } |
| |
| public void testDualCase() { |
| // Direct dispatch |
| { |
| JavaAdder java = new JavaAdder(); |
| assertEquals(2.0, java.add(1, 1)); |
| |
| JsoAdder jso = makeAdder(2); |
| assertEquals(4.0, jso.add(1, 1)); |
| assertEquals(5L, jso.returnLong()); |
| } |
| |
| // Just check dispatch via the interface |
| { |
| Adder a = new JavaAdder(); |
| assertEquals(2.0, a.add(1, 1)); |
| |
| a = makeAdder(2); |
| assertEquals(4.0, a.add(1, 1)); |
| assertEquals(5L, a.returnLong()); |
| } |
| |
| // Check casting |
| { |
| Object a = new JavaAdder(); |
| assertEquals(2.0, ((Adder) a).add(1, 1)); |
| assertEquals(2.0, ((JavaAdder) a).add(1, 1)); |
| assertEquals(5L, ((Adder) a).returnLong()); |
| assertEquals(5L, ((JavaAdder) a).returnLong()); |
| assertTrue(a instanceof JavaAdder); |
| assertFalse(a instanceof JsoAdder); |
| assertFalse(a instanceof Tag); |
| try { |
| ((JsoAdder) a).add(1, 1); |
| fail("Should have thrown CCE"); |
| } catch (ClassCastException e) { |
| // OK |
| } |
| |
| a = makeAdder(2); |
| assertEquals(4.0, ((Adder) a).add(1, 1)); |
| assertEquals(4.0, ((JsoAdder) a).add(1, 1)); |
| assertEquals(5L, ((Adder) a).returnLong()); |
| assertEquals(5L, ((JsoAdder) a).returnLong()); |
| assertTrue(a instanceof JsoAdder); |
| assertFalse(a instanceof JavaAdder); |
| // NB: This is unexpected until you consider JSO$ as a roll-up type |
| assertTrue(a instanceof Tag); |
| try { |
| ((JavaAdder) a).add(1, 1); |
| fail("Should have thrown CCE"); |
| } catch (ClassCastException e) { |
| // OK |
| } |
| } |
| } |
| |
| public void testFields() { |
| assertEquals(6, Adder.ADDER_CONST); |
| assertEquals("Same Name", Adder.SAME_NAME); |
| assertEquals(6, SameDescriptors.SAME_NAME); |
| assertSame(Adder.SAME_OBJECT, Adder.SAME_OBJECT2); |
| assertNotSame(Adder.DIFFERENT_OBJECT, Adder.SAME_OBJECT); |
| } |
| |
| public void testGenerics() { |
| UsesGenerics j = JsoUsesGeneric.create(); |
| assertEquals("Hello42", j.acceptsGeneric("Hello")); |
| assertEquals("Hello42", j.returnsGeneric("Hello")); |
| j.callback(new AsyncCallback<CharSequence>() { |
| @Override |
| public void onFailure(Throwable caught) { |
| } |
| |
| @Override |
| public void onSuccess(CharSequence result) { |
| assertEquals("Hello42", result); |
| } |
| }, "Hello"); |
| } |
| |
| @SuppressWarnings("cast") |
| public void testSimpleCase() { |
| { |
| JsoSimple asJso = makeSimple(); |
| assertTrue(asJso instanceof Object); |
| assertTrue(asJso instanceof Simple); |
| assertEquals("a", asJso.a()); |
| assertEquals("OK", asJso.a(false)); |
| try { |
| asJso.ex(); |
| fail("Should have thrown IOException"); |
| } catch (IOException e) { |
| // OK |
| } |
| try { |
| asJso.rte(); |
| fail("Should have thrown IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| // OK |
| } |
| } |
| |
| { |
| Simple asSimple = makeSimple(); |
| assertTrue(asSimple instanceof Object); |
| assertTrue(asSimple instanceof JavaScriptObject); |
| assertTrue(asSimple instanceof JsoSimple); |
| assertEquals("a", asSimple.a()); |
| assertEquals("OK", asSimple.a(false)); |
| assertEquals("a", ((JsoSimple) asSimple).a()); |
| assertEquals("OK", ((JsoSimple) asSimple).a(false)); |
| try { |
| asSimple.ex(); |
| fail("Should have thrown IOException"); |
| } catch (IOException e) { |
| // OK |
| } |
| try { |
| asSimple.rte(); |
| fail("Should have thrown IllegalArgumentException"); |
| } catch (IllegalArgumentException e) { |
| // OK |
| } |
| } |
| |
| { |
| Object asObject = "Defeat type-tightening"; |
| assertTrue(asObject instanceof String); |
| |
| asObject = makeSimple(); |
| assertTrue(asObject instanceof Object); |
| assertTrue(asObject instanceof JavaScriptObject); |
| assertTrue(asObject instanceof JsoSimple); |
| assertTrue(asObject instanceof Simple); |
| assertEquals("a", ((Simple) asObject).a()); |
| assertEquals("a", ((JsoSimple) asObject).a()); |
| assertEquals("OK", ((Simple) asObject).a(false)); |
| assertEquals("OK", ((JsoSimple) asObject).a(false)); |
| |
| // Test a cross-cast that's normally not allowed by the type system |
| assertTrue(asObject instanceof JsoRandom); |
| assertEquals("b", ((JsoRandom) asObject).b()); |
| assertEquals(-1, ((JsoRandom) asObject).add(1, 1)); |
| } |
| |
| { |
| Object o = "Defeat type-tightening"; |
| assertTrue(o instanceof String); |
| o = new SimpleOnlyJava(); |
| assertTrue(o instanceof SimpleOnlyJavaInterface); |
| assertEquals("simpleOnlyJava", ((SimpleOnlyJava) o).simpleOnlyJava()); |
| } |
| } |
| |
| public void testStaticCallsToSubclasses() { |
| Object o = "String"; |
| assertEquals(String.class, o.getClass()); |
| o = JavaScriptObject.createObject(); |
| assertTrue(o instanceof CallsStaticMethodInSubclass); |
| assertEquals("foo5", ((CallsStaticMethodInSubclass) o).call(2, 3)); |
| } |
| |
| @SuppressWarnings("cast") |
| public void testSubclassing() { |
| { |
| JsoDivider d = makeDivider(1); |
| assertTrue(d instanceof Divider); |
| assertTrue(d instanceof Multiplier); |
| assertTrue(d instanceof Tag); |
| assertEquals(5, d.divide(10, 2)); |
| assertEquals(10, d.multiply(5, 2)); |
| assertEquals(10, ((JsoMultiplier) d).multiply(5, 2)); |
| } |
| |
| { |
| Object d = makeDivider(1); |
| assertTrue(d instanceof Divider); |
| assertTrue(d instanceof Multiplier); |
| assertTrue(d instanceof JsoDivider); |
| assertTrue(d instanceof JsoMultiplier); |
| assertFalse(d instanceof JavaDivider); |
| assertFalse(d instanceof JavaMultiplier); |
| assertTrue(d instanceof Tag); |
| |
| assertEquals(5, ((Divider) d).divide(10, 2)); |
| assertEquals(10, ((Divider) d).multiply(5, 2)); |
| assertEquals(10, ((Multiplier) d).multiply(5, 2)); |
| assertEquals(5, ((JsoDivider) d).divide(10, 2)); |
| assertEquals(10, ((JsoMultiplier) d).multiply(5, 2)); |
| |
| d = new JavaDivider(); |
| assertTrue(d instanceof Divider); |
| assertTrue(d instanceof Multiplier); |
| assertTrue(d instanceof JavaDivider); |
| assertTrue(d instanceof JavaMultiplier); |
| assertFalse(d instanceof JsoDivider); |
| assertFalse(d instanceof JsoMultiplier); |
| assertTrue(d instanceof Tag); |
| |
| assertEquals(5, ((Divider) d).divide(10, 2)); |
| assertEquals(10, ((Divider) d).multiply(5, 2)); |
| assertEquals(10, ((Multiplier) d).multiply(5, 2)); |
| assertEquals(5, ((JavaDivider) d).divide(10, 2)); |
| assertEquals(10, ((JavaMultiplier) d).multiply(5, 2)); |
| } |
| |
| { |
| Object m = makeMultiplier(1); |
| // This only works because JSO$ implements every SingleJsoImpl interface |
| assertTrue(m instanceof Divider); |
| assertEquals(2, ((Divider) m).divide(10, 5)); |
| |
| m = new JavaMultiplier(); |
| // but this should fail, since there's proper type information |
| assertFalse(m instanceof Divider); |
| try { |
| assertEquals(2, ((Divider) m).divide(10, 5)); |
| fail("Should have thrown CCE"); |
| } catch (ClassCastException e) { |
| // OK |
| } |
| } |
| |
| { |
| Object l2 = "Prevent type-tightening"; |
| assertTrue(l2 instanceof String); |
| |
| l2 = new JavaLog2(); |
| assertTrue(l2 instanceof Log2); |
| assertTrue(l2 instanceof Multiplier); |
| assertTrue(l2 instanceof Divider); |
| assertEquals(4.0, ((Log2) l2).log2(16)); |
| } |
| } |
| |
| public void testUnreferencedType() { |
| JsoInterfaceWithUnreferencedImpl o = (JsoInterfaceWithUnreferencedImpl) JavaScriptObject.createObject(); |
| assertNotNull(o); |
| assertTrue(o.isOk()); |
| } |
| |
| public void testJsFunctionCastsAsJso() { |
| JsFunction func = JsFunctionImpl.makeFunc(); |
| assertTrue(func instanceof JavaScriptObject); |
| JavaScriptObject jso = (JavaScriptObject) func; |
| JsFunction func2 = (JsFunction) jso; |
| if (func2 instanceof JavaScriptObject) { |
| assertEquals(42, func2.call()); |
| } else { |
| fail("Function should return true for instanceof JSO"); |
| } |
| } |
| } |