Allow names "*" and "?" on global native JsType interfaces.

Name '?' (aka. unknown type) should be preferred over 'Object' when the
type is unknown while '*' (aka. any) as the super type of any type in
JavaScript (incl. primitives).

For GWT there are no implications on the generated code however other
tools could utilize this value for better type information.

Bug: #9341
Bug-Link: https://github.com/gwtproject/gwt/issues/9341
Change-Id: If07125d9c7eab21e7b11858fdfde0b84eb2886bd
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
index 9be73e6..93417bd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropRestrictionChecker.java
@@ -878,6 +878,21 @@
     return JjsUtils.getNativeSuperClassOrNull(type) != null;
   }
 
+  private void checkJsNameOnType(JDeclaredType type) {
+    if (!type.getJsName().equals("*") && !type.getJsName().equals("?")) {
+      checkJsName(type);
+      return;
+    }
+
+    if (!type.isJsNative()
+        || !(type instanceof JInterfaceType)
+        || !JsInteropUtil.isGlobal(type.getJsNamespace())) {
+      logError(type,
+          "'%s' can only be used as a name for native interfaces in the global namespace.",
+          type.getJsName());
+    }
+  }
+
   private void checkType(JDeclaredType type) {
     minimalRebuildCache.removeExportedNames(type.getName());
 
@@ -885,7 +900,7 @@
       if (!checkJsType(type)) {
         return;
       }
-      checkJsName(type);
+      checkJsNameOnType(type);
       checkJsNamespace(type);
     }
 
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
index 36ad71d..cf2e147 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/JsInteropRestrictionCheckerTest.java
@@ -1184,7 +1184,14 @@
         "   @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.b\") static void o() {}",
         "   @JsProperty(namespace = JsPackage.GLOBAL, name = \"a.c\") static int q;",
         "}",
-        "@JsType(namespace=JsPackage.GLOBAL, name = \"a.b.d\") public static class OtherBuggy {",
+        "@JsType(namespace = JsPackage.GLOBAL, name = \"a.b.d\") public static class OtherBuggy {",
+        "}",
+        "@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = \"*\")",
+        "public static class BadGlobalStar {",
+        "}",
+        "@JsType(namespace = JsPackage.GLOBAL, name = \"?\") public interface BadGlobalWildcard {",
+        "}",
+        "@JsType(isNative = true, namespace = \"a.b\", name = \"*\") public interface BadStar {",
         "}"
         );
 
@@ -1195,7 +1202,11 @@
         "Line 10: 'int EntryPoint.Buggy.n' cannot have an empty name.",
         "Line 11: 'void EntryPoint.Buggy.o()' has invalid name 'a.b'.",
         "Line 12: 'int EntryPoint.Buggy.q' has invalid name 'a.c'.",
-        "Line 14: 'EntryPoint.OtherBuggy' has invalid name 'a.b.d'.");
+        "Line 14: 'EntryPoint.OtherBuggy' has invalid name 'a.b.d'.",
+        "Line 17: '*' can only be used as a name for native interfaces in the global namespace.",
+        "Line 19: '?' can only be used as a name for native interfaces in the global namespace.",
+        "Line 21: '*' can only be used as a name for native interfaces in the global namespace."
+        );
   }
 
   public void testJsNameInvalidNamespacesFails() {
@@ -1249,7 +1260,14 @@
         "   @JsMethod(namespace = \"<window>\", name = \"a.h\") static native void q();",
         "   @JsMethod(namespace = \"<window>\", name = \"a.i\") static native void getR();",
         "   @JsProperty(namespace = \"<window>\", name = \"a.j\") public static int s;",
-        "}");
+        "}",
+        "@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = \"*\")",
+        "public interface Star {",
+        "}",
+        "@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = \"?\")",
+        "public interface Wildcard {",
+        "}"
+    );
 
     assertBuggySucceeds();
   }
@@ -1346,7 +1364,8 @@
         "}",
         "static final class JsFunctionMultipleInterfaces implements Function, Cloneable {",
         "  public int getFoo() { return 0; }",
-        "}");
+        "}"
+        );
 
     assertBuggyFails(
         "Line 14: JsFunction implementation member 'int EntryPoint.Buggy.getFoo()' "
diff --git a/user/src/jsinterop/annotations/JsType.java b/user/src/jsinterop/annotations/JsType.java
index c6c55c8..fe11549 100644
--- a/user/src/jsinterop/annotations/JsType.java
+++ b/user/src/jsinterop/annotations/JsType.java
@@ -45,6 +45,10 @@
  * members are considered {@link JsProperty}/{@link JsMethod}/{@link JsConstructor} unless they are
  * explicitly marked with {@link JsOverlay}.
  *
+ * <p> For native interfaces with no particular JavaScript type associated with them (e.g.
+ * structural types) it is recommeded to use {@code namespace = JsPackage.GLOBAL} and
+ * {@code name = '?'}.
+ *
  * <p><b>Instanceof and Castability:</b>
  *
  * <p>If the JsType is native, the generated code will try to mimic Javascript semantics.
diff --git a/user/test/com/google/gwt/core/interop/JsTypeSpecialTypesTest.java b/user/test/com/google/gwt/core/interop/JsTypeSpecialTypesTest.java
index 5a65c2e..afea9ee 100644
--- a/user/test/com/google/gwt/core/interop/JsTypeSpecialTypesTest.java
+++ b/user/test/com/google/gwt/core/interop/JsTypeSpecialTypesTest.java
@@ -124,4 +124,30 @@
     assertNotNull((Object) nativeObject);
     assertTrue(nativeObject instanceof Object);
   }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "*")
+  interface Star {
+  }
+
+  public void testStar() {
+    Object object = new Object();
+
+    assertNotNull((Star) object);
+
+    object = Double.valueOf(3.0);
+    assertNotNull((Star) object);
+  }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "?")
+  interface Wildcard {
+  }
+
+  public void testWildcard() {
+    Object object = new Object();
+
+    assertNotNull((Wildcard) object);
+
+    object = Double.valueOf(3.0);
+    assertNotNull((Wildcard) object);
+  }
 }