blob: e250b97e8d43563c53f3b39417f1546918f5b278 [file] [log] [blame]
/*
* Copyright 2015 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 com.google.gwt.core.client.ScriptInjector.TOP_WINDOW;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.junit.client.GWTTestCase;
import javaemul.internal.annotations.DoNotInline;
import jsinterop.annotations.JsMethod;
import jsinterop.annotations.JsType;
/**
* Tests JsExport.
*/
public class JsExportTest extends GWTTestCase {
@Override
public String getModuleName() {
return "com.google.gwt.core.Interop";
}
@Override
public void gwtSetUp() throws Exception {
setupGlobal();
}
// $global always points to scope of exports
private native void setupGlobal() /*-{
$global = window.goog && window.goog.global || $wnd;
$wnd.$global = $global;
}-*/;
public void testMethodExport() {
// Test exported method can be called from JS in host page
ScriptInjector.fromString("$global.exported();").setWindow(TOP_WINDOW).inject();
assertTrue(MyClassExportsMethod.calledFromCallMe1);
ScriptInjector.fromString("$global.exportNamespace.exported();").setWindow(TOP_WINDOW).inject();
assertTrue(MyClassExportsMethod.calledFromCallMe2);
ScriptInjector.fromString("$global.exportNamespace.callMe3();").setWindow(TOP_WINDOW).inject();
assertTrue(MyClassExportsMethod.calledFromCallMe3);
ScriptInjector.fromString("$global.woo.MyClassExportsMethod.exported();").setWindow(TOP_WINDOW)
.inject();
assertTrue(MyClassExportsMethod.calledFromCallMe4);
ScriptInjector.fromString("$global.woo.MyClassExportsMethod.callMe5();").setWindow(TOP_WINDOW)
.inject();
assertTrue(MyClassExportsMethod.calledFromCallMe5);
MyClassExportsMethod.calledFromCallMe1 = false;
// Test exported constructor called from JS in module window
ScriptInjector.fromString("$global.exported();").inject();
assertTrue(MyClassExportsMethod.calledFromCallMe1);
}
public void testMethodExportWithLong() {
assertEquals(42.0, callLongMethod(40.0, 2.0));
assertEquals(82.0, callStaticLongMethod(80.0, 2.0));
}
private native double callLongMethod(double a, double b) /*-{
var obj = new $global.woo.MyJsTypeThatUsesLongType();
return obj.addLong(a,b);
}-*/;
private native double callStaticLongMethod(double a, double b) /*-{
return $global.woo.MyJsTypeThatUsesLongType.addLongStatic(a,b);
}-*/;
public void testMethodExport_noTypeTightenParams() {
// If we type-tighten, java side will see no calls and think that parameter could only be null.
// As a result, it will be optimized to null.nullMethod().
ScriptInjector.fromString("$global.callBar($global.newA());").inject();
assertTrue(MyClassExportsMethod.calledFromBar);
// If we type-tighten, java side will only see a call to subclass and think that parameter could
// be optimized to that one. As a result, the method call will be inlined.
MyClassExportsMethod.callFoo(new MyClassExportsMethod.SubclassOfA());
ScriptInjector.fromString("$global.callFoo($global.newA());").inject();
assertTrue(MyClassExportsMethod.calledFromFoo);
}
public void testMethodExport_notReferencedFromJava() {
// Exported by MyClassExportsMethodWithoutReference which is not referenced by Java. This
// ensures that we correctly collect root types.
assertEquals(42, onlyCalledFromJs());
}
private native int onlyCalledFromJs() /*-{
return $global.woo.MyClassExportsMethodWithoutReference.onlyCalledFromJs();
}-*/;
public void testClinit() {
ScriptInjector.fromString("new $global.woo.MyClassExportsMethodWithClinit();").inject();
assertEquals(23, MyClassExportsMethodWithClinit.magicNumber);
}
public void testClinit_staticField() {
assertNotNull(getStaticInitializerStaticField1());
assertNotNull(getStaticInitializerStaticField2());
assertNotNull(getExportedFieldOnInterface());
}
private native Object getStaticInitializerStaticField1() /*-{
return $global.woo.StaticInitializerStaticField.EXPORTED_1;
}-*/;
private native Object getStaticInitializerStaticField2() /*-{
return $global.woo.StaticInitializerStaticField.EXPORTED_2;
}-*/;
private native Object getExportedFieldOnInterface() /*-{
return $global.woo.StaticInitializerStaticField.InterfaceWithField.STATIC;
}-*/;
public void testClinit_staticMethod() {
assertNotNull(getStaticInitializerStaticMethod());
}
private native Object getStaticInitializerStaticMethod() /*-{
return $global.woo.StaticInitializerStaticMethod.getInstance();
}-*/;
public void testClinit_virtualMethod() {
assertNotNull(getStaticInitializerVirtualMethod());
}
private native Object getStaticInitializerVirtualMethod() /*-{
var obj = new $global.woo.StaticInitializerVirtualMethod();
return obj.getInstance();
}-*/;
@JsType(namespace = "bar.foo.baz")
static class MyExportedClassCorrectNamespace {
public MyExportedClassCorrectNamespace() { }
}
public void testExportClass_correctNamespace() {
assertNull(getGlobalByQualifiedName("bar.MyExportedClassCorrectNamespace"));
assertNull(getGlobalByQualifiedName("bar.foo.MyExportedClassCorrectNamespace"));
assertTrue(isFunction(getGlobalByQualifiedName("bar.foo.baz.MyExportedClassCorrectNamespace")));
Object o = newInstance(getGlobalByQualifiedName("bar.foo.baz.MyExportedClassCorrectNamespace"));
assertNotNull(o);
assertTrue(o instanceof MyExportedClassCorrectNamespace);
}
private native Object getGlobalByQualifiedName(String qualifiedName) /*-{
var components = qualifiedName.split('\.');
var scope = $global;
for (var i = 0; i < components.length; i++) {
scope = scope[components[i]];
}
return scope;
}-*/;
private native boolean isFunction(Object object) /*-{
return typeof object == "function";
}-*/;
private native Object newInstance(Object jsConstructor) /*-{
return new jsConstructor;
}-*/;
public void testExportClass_implicitConstructor() {
Object o = createMyExportedClassWithImplicitConstructor();
assertNotNull(o);
assertTrue(o instanceof MyExportedClassWithImplicitConstructor);
}
private native Object createMyExportedClassWithImplicitConstructor() /*-{
return new $global.woo.MyExportedClassWithImplicitConstructor();
}-*/;
public void testExportConstructors() {
assertEquals(4, createMyClassExportsConstructor().foo());
assertEquals(2, new MyClassExportsConstructor().foo());
}
private native MyClassExportsConstructor createMyClassExportsConstructor() /*-{
return new $global.woo.MyClassExportsConstructor(2);
}-*/;
public void testExportedField() {
assertEquals(100, MyExportedClass.EXPORTED_1);
assertEquals(100, getExportedField());
setExportedField(1000);
assertEquals(100, MyExportedClass.EXPORTED_1);
assertEquals(1000, getExportedField());
}
private native int getExportedField() /*-{
return $global.woo.MyExportedClass.EXPORTED_1;
}-*/;
private native void setExportedField(int a) /*-{
$global.woo.MyExportedClass.EXPORTED_1 = a;
}-*/;
public void testExportedMethod() {
assertEquals(200, MyExportedClass.foo());
assertEquals(200, callExportedMethod());
setExportedMethod();
assertEquals(200, MyExportedClass.foo());
assertEquals(1000, callExportedMethod());
}
private native int callExportedMethod() /*-{
return $global.woo.MyExportedClass.foo();
}-*/;
private native int setExportedMethod() /*-{
$global.woo.MyExportedClass.foo = function () {
return 1000;
};
}-*/;
public void testExportedFieldRefInExportedMethod() {
assertEquals(5, MyExportedClass.bar(0, 0));
assertEquals(5, callExportedFieldByExportedMethod(0, 0));
setExportedField2(10);
assertEquals(10, getExportedField2());
assertEquals(7, MyExportedClass.bar(1, 1));
assertEquals(7, callExportedFieldByExportedMethod(1, 1));
}
private native int callExportedFieldByExportedMethod(int a, int b) /*-{
return $global.woo.MyExportedClass.bar(a, b);
}-*/;
private native void setExportedField2(int a) /*-{
$global.woo.MyExportedClass.EXPORTED_2 = $global.woo.MyExportedClass.newInnerClass(a);
}-*/;
private native int getExportedField2() /*-{
return $global.woo.MyExportedClass.EXPORTED_2.field;
}-*/;
public void testNoExport() {
assertNull(getNotExportedMethods());
assertNull(getNotExportedFields());
}
private native Object getNotExportedFields() /*-{
return $global.woo.StaticInitializerStaticField.NOT_EXPORTED_1
|| $global.woo.StaticInitializerStaticField.NOT_EXPORTED_2
|| $global.woo.StaticInitializerStaticField.NOT_EXPORTED_3
|| $global.woo.StaticInitializerStaticField.NOT_EXPORTED_4
|| $global.woo.StaticInitializerStaticField.NOT_EXPORTED_5;
}-*/;
private native Object getNotExportedMethods() /*-{
return $global.woo.StaticInitializerStaticMethod.notExported_1
|| $global.woo.StaticInitializerStaticMethod.notExported_2;
}-*/;
public static void testInheritClassNamespace() {
assertEquals(42, getBAR());
}
private static native int getBAR() /*-{
return $global.foo.MyExportedClassWithNamespace.BAR;
}-*/;
public static void testInheritClassNamespace_empty() {
assertEquals(82, getDAN());
assertNotNull(createNestedExportedClassWithEmptyNamespace());
}
private static native int getDAN() /*-{
return $global.MyClassWithEmptyNamespace.DAN;
}-*/;
private static native Object createNestedExportedClassWithEmptyNamespace() /*-{
return new $global.MyClassWithEmptyNamespace();
}-*/;
public static void testInheritClassNamespace_withName() {
assertEquals(42, getBooBAR());
}
private static native int getBooBAR() /*-{
return $global.foo.boo.BAR;
}-*/;
public static void testInheritClassNamespace_noExport() {
assertEquals(99, getBAZ());
}
private static native int getBAZ() /*-{
return $global.foobaz.MyClassWithNamespace.BAZ;
}-*/;
public static void testInheritClassNamespace_nested() {
assertEquals(99, getLOO());
assertNotNull(createNestedExportedClassInExportedClass());
}
private static native int getLOO() /*-{
return $global.woo.Bloo.Inner.LOO;
}-*/;
private static native Object createNestedExportedClassInExportedClass() /*-{
return new $global.woo.Bloo.Inner();
}-*/;
public static void testInheritClassNamespace_nestedNoExport() {
assertEquals(999, getWOOZ());
assertNotNull(createNestedExportedClassWithNamespace());
}
private static native int getWOOZ() /*-{
return $global.zoo.InnerWithNamespace.WOOZ;
}-*/;
private static native Object createNestedExportedClassWithNamespace() /*-{
return new $global.zoo.InnerWithNamespace();
}-*/;
public void testInheritPackageNamespace() {
assertEquals(1001, getWOO());
}
private static native int getWOO() /*-{
return $global.woo.MyExportedClassWithPackageNamespace.WOO;
}-*/;
public void testInheritPackageNamespace_nestedClass() {
assertEquals(99, getNestedWOO());
assertNotNull(createNestedExportedClass());
}
private static native int getNestedWOO() /*-{
return $global.woo.MyClassWithNestedExportedClass.Inner.WOO;
}-*/;
private static native Object createNestedExportedClass() /*-{
return new $global.woo.MyClassWithNestedExportedClass.Inner();
}-*/;
public void testInheritPackageNamespace_nestedEnum() {
assertNotNull(getNestedEnum());
}
private static native Object getNestedEnum() /*-{
return $global.woo.MyClassWithNestedExportedClass.InnerEnum.AA;
}-*/;
public void testInheritPackageNamespace_subpackage() {
assertNull(getNestedSubpackage());
assertNotNull(getNestedSubpackageCorrect());
}
private static native Object getNestedSubpackage() /*-{
return $global.woo.subpackage;
}-*/;
private static native Object getNestedSubpackageCorrect() /*-{
return $global.com.google.gwt.core.interop.subpackage.
MyNestedExportedClassSansPackageNamespace;
}-*/;
public void testEnum_enumerations() {
assertNotNull(getEnumerationTEST1());
assertNotNull(getEnumerationTEST2());
}
private static native Object getEnumerationTEST1() /*-{
return $global.woo.MyExportedEnum.TEST1;
}-*/;
private static native Object getEnumerationTEST2() /*-{
return $global.woo.MyExportedEnum.TEST2;
}-*/;
public void testEnum_exportedMethods() {
assertNotNull(getPublicStaticMethodInEnum());
assertNotNull(getValuesMethodInEnum());
assertNotNull(getValueOfMethodInEnum());
}
private static native Object getPublicStaticMethodInEnum() /*-{
return $global.woo.MyExportedEnum.publicStaticMethod;
}-*/;
private static native Object getValuesMethodInEnum() /*-{
return $global.woo.MyExportedEnum.values;
}-*/;
private static native Object getValueOfMethodInEnum() /*-{
return $global.woo.MyExportedEnum.valueOf;
}-*/;
public void testEnum_exportedFields() {
assertEquals(1, getPublicStaticFinalFieldInEnum());
// explicitly marked @JsType() fields must be final
// but ones that are in a @JsType()ed class don't need to be final
assertEquals(2, getPublicStaticFieldInEnum());
}
private static native int getPublicStaticFinalFieldInEnum() /*-{
return $global.woo.MyExportedEnum.publicStaticFinalField;
}-*/;
private static native int getPublicStaticFieldInEnum() /*-{
return $global.woo.MyExportedEnum.publicStaticField;
}-*/;
public void testEnum_notExported() {
assertNull(getNotExportedFieldsInEnum());
assertNull(getNotExportedMethodsInEnum());
}
private native Object getNotExportedFieldsInEnum() /*-{
return $global.woo.MyExportedEnum.publicFinalField
|| $global.woo.MyExportedEnum.privateStaticFinalField
|| $global.woo.MyExportedEnum.protectedStaticFinalField
|| $global.woo.MyExportedEnum.defaultStaticFinalField;
}-*/;
private native Object getNotExportedMethodsInEnum() /*-{
return $global.woo.MyExportedEnum.publicMethod
|| $global.woo.MyExportedEnum.protectedStaticMethod
|| $global.woo.MyExportedEnum.privateStaticMethod
|| $global.woo.MyExportedEnum.defaultStaticMethod;
}-*/;
public void testEnum_subclassEnumerations() {
assertNotNull(getEnumerationA());
assertNotNull(getEnumerationB());
assertNotNull(getEnumerationC());
}
private static native Object getEnumerationA() /*-{
return $global.woo.MyEnumWithSubclassGen.A;
}-*/;
private static native Object getEnumerationB() /*-{
return $global.woo.MyEnumWithSubclassGen.B;
}-*/;
private static native Object getEnumerationC() /*-{
return $global.woo.MyEnumWithSubclassGen.C;
}-*/;
public void testEnum_subclassMethodCallFromExportedEnumerations() {
assertEquals(100, callPublicMethodFromEnumerationA());
assertEquals(200, callPublicMethodFromEnumerationB());
assertEquals(1, callPublicMethodFromEnumerationC());
}
private static native int callPublicMethodFromEnumerationA() /*-{
return $global.woo.MyEnumWithSubclassGen.A.foo();
}-*/;
private static native int callPublicMethodFromEnumerationB() /*-{
return $global.woo.MyEnumWithSubclassGen.B.foo();
}-*/;
private static native int callPublicMethodFromEnumerationC() /*-{
return $global.woo.MyEnumWithSubclassGen.C.foo();
}-*/;
static class X {
@JsMethod
@DoNotInline
public static String m(String s) {
return s;
}
}
private native String callM(String s) /*-{
return $global.woo.JsExportTest.X.m(s);
}-*/;
public void testSameParameterValueOptimization() {
assertEquals("L", X.m("L"));
assertEquals("M", callM("M"));
}
}