Add treatment for special native types.
Native JsTypes can model some of the standard builtin JavaScript
objects such as Object, Function, Number, Array and String. Due to the
quirks of JavaScript, where creating these objects in the current
IFrame result in different constructors than when creating them in the
window scope, casting and instanceof checks may return false when
they should not have.
This patch makes the aforementioned JavaScript "classes" behave
in a more coherent manner.
Change-Id: I111f2007f7d67968c74dbd8cb92321f4ddf24b27
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
index c1673b8..955834d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
@@ -92,7 +92,8 @@
refType = (JReferenceType) program.normalizeJsoType(refType);
}
- if (pruneTrivialCasts && program.typeOracle.castSucceedsTrivially(argType, refType)) {
+ if (pruneTrivialCasts && program.typeOracle.castSucceedsTrivially(argType, refType) ||
+ determineTypeCategoryForType(refType) == TypeCategory.TYPE_JAVA_LANG_OBJECT) {
// just remove the cast
ctx.replaceMe(curExpr);
return;
@@ -204,7 +205,8 @@
// don't depend on type-tightener having run
|| (program.typeOracle.isEffectivelyJavaScriptObject(argType)
&& program.typeOracle.isEffectivelyJavaScriptObject(toType));
- if (pruneTrivialCasts && isTrivialCast) {
+ if (pruneTrivialCasts && isTrivialCast ||
+ determineTypeCategoryForType(toType) == TypeCategory.TYPE_JAVA_LANG_OBJECT) {
// trivially true if non-null; replace with a null test
JBinaryOperation eq =
new JBinaryOperation(x.getSourceInfo(), program.getTypePrimitiveBoolean(),
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
index 66c8695..b0a9f1e 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.dev.jjs.impl;
+import com.google.gwt.dev.javac.JsInteropUtil;
+import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
@@ -83,7 +85,9 @@
assert type instanceof JReferenceType;
type = type.getUnderlyingType();
- if (program.isUntypedArrayType(type)) {
+ if (getJsSpecialType(type) != null) {
+ return getJsSpecialType(type);
+ } else if (program.isUntypedArrayType(type)) {
return TypeCategory.TYPE_NATIVE_ARRAY;
} else if (type == program.getTypeJavaLangObject()) {
return TypeCategory.TYPE_JAVA_LANG_OBJECT;
@@ -102,4 +106,29 @@
}
return TypeCategory.TYPE_JAVA_OBJECT;
}
+
+ private static TypeCategory getJsSpecialType(JType type) {
+ if (!(type instanceof JClassType) || !type.isJsNative()) {
+ return null;
+ }
+
+ JClassType classType = (JClassType) type;
+ if (!JsInteropUtil.isGlobal(classType.getJsNamespace())) {
+ return null;
+ }
+
+ switch (classType.getJsName()) {
+ case "Object":
+ return TypeCategory.TYPE_JAVA_LANG_OBJECT;
+ case "Function":
+ return TypeCategory.TYPE_JS_FUNCTION;
+ case "Array":
+ return TypeCategory.TYPE_NATIVE_ARRAY;
+ case "Number":
+ return TypeCategory.TYPE_JAVA_LANG_DOUBLE;
+ case "String":
+ return TypeCategory.TYPE_JAVA_LANG_STRING;
+ }
+ return null;
+ }
}
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
index d8d221d..b265669 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
@@ -239,19 +239,6 @@
return false;
}
- if (jsTypeStr == "Object") {
- // TODO(rluble): Handle this case in the compiler side.
- return (typeof obj) == "object";
- } else if (jsTypeStr == "Function") {
- // TODO(rluble): remove array special handling once
- // instanceOf can be customized for native classes.
- return (typeof obj) == "function";
- } else if (jsTypeStr == "Array" ) {
- // TODO(rluble): remove array special handling once
- // instanceOf can be customized for native classes.
- return Array.isArray(obj);
- }
-
var jsType = $wnd;
for (var i = 0, parts = jsTypeStr.split("."), l = parts.length; i < l ; i++) {
jsType = jsType && jsType[parts[i]];
diff --git a/user/test/com/google/gwt/core/CoreJsInteropSuite.java b/user/test/com/google/gwt/core/CoreJsInteropSuite.java
index 8c1d853..a14e887 100644
--- a/user/test/com/google/gwt/core/CoreJsInteropSuite.java
+++ b/user/test/com/google/gwt/core/CoreJsInteropSuite.java
@@ -22,6 +22,7 @@
import com.google.gwt.core.interop.JsPropertyTest;
import com.google.gwt.core.interop.JsTypeArrayTest;
import com.google.gwt.core.interop.JsTypeBridgeTest;
+import com.google.gwt.core.interop.JsTypeSpecialTypesTest;
import com.google.gwt.core.interop.JsTypeTest;
import com.google.gwt.core.interop.JsTypeVarargsTest;
import com.google.gwt.core.interop.NativeJsTypeTest;
@@ -40,6 +41,7 @@
suite.addTestSuite(JsExportOptimizationTest.class);
suite.addTestSuite(JsTypeTest.class);
suite.addTestSuite(JsTypeBridgeTest.class);
+ suite.addTestSuite(JsTypeSpecialTypesTest.class);
suite.addTestSuite(JsPropertyTest.class);
suite.addTestSuite(JsMethodTest.class);
suite.addTestSuite(JsTypeArrayTest.class);
diff --git a/user/test/com/google/gwt/core/interop/JsTypeSpecialTypesTest.java b/user/test/com/google/gwt/core/interop/JsTypeSpecialTypesTest.java
new file mode 100644
index 0000000..5a65c2e
--- /dev/null
+++ b/user/test/com/google/gwt/core/interop/JsTypeSpecialTypesTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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 com.google.gwt.junit.client.GWTTestCase;
+
+import jsinterop.annotations.JsFunction;
+import jsinterop.annotations.JsPackage;
+import jsinterop.annotations.JsType;
+
+/**
+ * Tests special JsType functionality.
+ */
+@SuppressWarnings("cast")
+public class JsTypeSpecialTypesTest extends GWTTestCase {
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.core.Interop";
+ }
+
+ @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Array")
+ static class NativeArray {
+ }
+
+ public void testNativeArray() {
+ Object object = new Object[10];
+
+ assertNotNull((NativeArray) object);
+ assertTrue(object instanceof NativeArray);
+
+ Object nativeArray = new NativeArray();
+ assertNotNull((NativeArray[]) nativeArray);
+ assertTrue(nativeArray instanceof NativeArray[]);
+ }
+
+ @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Function")
+ static class NativeFunction {
+ }
+
+ @JsFunction
+ interface SomeFunctionalInterface {
+ void m();
+ }
+
+ public void testNativeFunction() {
+ Object object = new SomeFunctionalInterface() {
+ @Override
+ public void m() {
+ }
+ };
+
+ assertNotNull((NativeFunction) object);
+ assertTrue(object instanceof NativeFunction);
+
+ SomeFunctionalInterface nativeFunction = (SomeFunctionalInterface) new NativeFunction();
+ assertTrue(nativeFunction instanceof SomeFunctionalInterface);
+ }
+
+ @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Number")
+ static class NativeNumber {
+ public NativeNumber(double number) { }
+ public native NativeNumber valueOf();
+ }
+
+ public void testNativeNumber() {
+ Object object = new Double(1);
+
+ assertNotNull((NativeNumber) object);
+ assertTrue(object instanceof NativeNumber);
+
+ // new NativeString() returns a boxed JS number. Java Double object are only interchangeable
+ // with unboxed JS numbers.
+ Object nativeNumber = new NativeNumber(10.0).valueOf();
+ assertNotNull((Double) nativeNumber);
+ assertTrue(nativeNumber instanceof Double);
+ assertEquals(10.0, (Double) nativeNumber);
+ }
+
+ @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "String")
+ static class NativeString {
+ public NativeString(String someString) { }
+ public native NativeString valueOf();
+ }
+
+ public void testNativeString() {
+ Object object = "Hello";
+
+ assertNotNull((NativeString) object);
+ assertTrue(object instanceof NativeString);
+
+ // new NativeString() returns a boxed JS string. Java String objects are only interchangeable
+ // with unboxed JS strings.
+ Object nativeString = new NativeString("Hello").valueOf();
+ assertNotNull((String) nativeString);
+ assertTrue(nativeString instanceof String);
+ assertEquals("Hello", nativeString);
+ }
+
+ @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
+ static class NativeObject {
+ }
+
+ public void testNativeObject() {
+ Object object = new Object();
+
+ assertNotNull((NativeObject) object);
+ assertTrue(object instanceof NativeObject);
+
+ Object nativeObject = new NativeObject();
+ assertNotNull((Object) nativeObject);
+ assertTrue(nativeObject instanceof Object);
+ }
+}