Relax name checking native items on the JsPackage.GLOBAL namespace.

Allow qualifed names as the name component in native JsType, JsMethod,
JsProperty definitions.

Change-Id: I617b8ac0eb8bf935143c4c57667b9790399cc2d9
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/CanBeJsNative.java b/dev/core/src/com/google/gwt/dev/jjs/ast/CanBeJsNative.java
new file mode 100644
index 0000000..973604e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/CanBeJsNative.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2016 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.ast;
+
+/**
+ * Interface implemented by Java entities that can be declared native in JsInterop.
+ */
+public interface CanBeJsNative {
+  boolean isJsNative();
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java b/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java
index f0b70ab..23e0196 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsInfo.java
@@ -22,7 +22,7 @@
 /**
  * Abstracts JsInterop information for the AST nodes.
  */
-public interface HasJsInfo extends HasJsName {
+public interface HasJsInfo extends HasJsName, CanBeJsNative {
   /**
    * Indicates type of JsMember.
    */
@@ -127,8 +127,6 @@
 
   JsMemberType getJsMemberType();
 
-  boolean isJsNative();
-
   boolean isJsMethodVarargs();
 
   boolean isJsOverlay();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsName.java b/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsName.java
index bab517b..e3f66f1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsName.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/HasJsName.java
@@ -18,7 +18,7 @@
 /**
  * Abstracts JsInterop name related information for the AST nodes.
  */
-public interface HasJsName  {
+public interface HasJsName {
 
   String getJsName();
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index 86012c5..06b972e 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -52,7 +52,7 @@
  * initializer in the superclass chain needs to be called.
  */
 public abstract class JDeclaredType extends JReferenceType
-    implements CanHaveSuppressedWarnings, HasJsName {
+    implements CanHaveSuppressedWarnings, HasJsName, CanBeJsNative {
 
   private boolean isJsFunction;
   private boolean isJsType;
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 bd6885a..0b07d85 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
@@ -18,6 +18,7 @@
 import com.google.gwt.dev.MinimalRebuildCache;
 import com.google.gwt.dev.javac.JsInteropUtil;
 import com.google.gwt.dev.jjs.HasSourceInfo;
+import com.google.gwt.dev.jjs.ast.CanBeJsNative;
 import com.google.gwt.dev.jjs.ast.CanHaveSuppressedWarnings;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.HasJsInfo.JsMemberType;
@@ -563,9 +564,15 @@
     checkJsNamespace(member);
   }
 
-  private <T extends HasJsName & HasSourceInfo> void checkJsName(T item) {
+  private <T extends HasJsName & HasSourceInfo & CanBeJsNative> void checkJsName(T item) {
     if (item.getJsName().isEmpty()) {
       logError(item, "%s cannot have an empty name.", getDescription(item));
+    } else if (JsInteropUtil.isGlobal(item.getJsNamespace()) && item.isJsNative()) {
+      // Allow qualified names in the name field for JsPackage.GLOBAL native items for future
+      // compatibility
+      if (!JsUtils.isValidJsQualifiedName(item.getJsName())) {
+        logError(item, "%s has invalid name '%s'.", getDescription(item), item.getJsName());
+      }
     } else if (!JsUtils.isValidJsIdentifier(item.getJsName())) {
       logError(item, "%s has invalid name '%s'.", getDescription(item), item.getJsName());
     }
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 bf7fcce..7c53281 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
@@ -1174,19 +1174,28 @@
   public void testJsNameInvalidNamesFails() {
     addSnippetImport("jsinterop.annotations.JsType");
     addSnippetImport("jsinterop.annotations.JsMethod");
+    addSnippetImport("jsinterop.annotations.JsPackage");
     addSnippetImport("jsinterop.annotations.JsProperty");
     addSnippetClassDecl(
         "@JsType(name = \"a.b.c\") public static class Buggy {",
         "   @JsMethod(name = \"34s\") public void m() {}",
         "   @JsProperty(name = \"s^\") public int  m;",
         "   @JsProperty(name = \"\") public int n;",
-        "}");
+        "   @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 {",
+        "}"
+        );
 
     assertBuggyFails(
-        "Line 6: 'EntryPoint.Buggy' has invalid name 'a.b.c'.",
-        "Line 7: 'void EntryPoint.Buggy.m()' has invalid name '34s'.",
-        "Line 8: 'int EntryPoint.Buggy.m' has invalid name 's^'.",
-        "Line 9: 'int EntryPoint.Buggy.n' cannot have an empty name.");
+        "Line 7: 'EntryPoint.Buggy' has invalid name 'a.b.c'.",
+        "Line 8: 'void EntryPoint.Buggy.m()' has invalid name '34s'.",
+        "Line 9: 'int EntryPoint.Buggy.m' has invalid name 's^'.",
+        "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'.");
   }
 
   public void testJsNameInvalidNamespacesFails() {
@@ -1219,7 +1228,14 @@
     addSnippetClassDecl(
         "@JsType(namespace = JsPackage.GLOBAL) public static class Buggy {",
         "   @JsMethod(namespace = JsPackage.GLOBAL) public static void m() {}",
-        "   @JsProperty(namespace = JsPackage.GLOBAL) public static int  n;",
+        "   @JsProperty(namespace = JsPackage.GLOBAL) public static int n;",
+        "   @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.b\") public static native void o();",
+        "}",
+        "@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = \"a.c\")",
+        "public static class OtherBuggy {",
+        "   @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.d\") static native void o();",
+        "   @JsMethod(namespace = JsPackage.GLOBAL, name = \"a.e\") static native void getP();",
+        "   @JsProperty(namespace = JsPackage.GLOBAL, name = \"a.f\") public static int n;",
         "}");
 
     assertBuggySucceeds();