Enforce JsPackage.GLOBAL to access global object.

Change-Id: I7e9c076a7f3e7bba7a818a206e2ec41c6b244928
Review-Link: https://gwt-review.googlesource.com/#/c/14021/
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
index 4b4a335..22a958b 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsInteropUtil.java
@@ -42,6 +42,10 @@
   public static final String UNUSABLE_BY_JS = "unusable-by-js";
   public static final String INVALID_JSNAME = "<invalid>";
 
+  public static boolean isGlobal(String jsNamespace) {
+    return "<global>".equals(jsNamespace);
+  }
+
   public static void maybeSetJsInteropProperties(JDeclaredType type, Annotation... annotations) {
     AnnotationBinding jsType = JdtUtil.getAnnotation(annotations, JSTYPE_CLASS);
     String namespace = maybeGetJsNamespace(annotations);
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 535ff13..4d26cec 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
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.javac.JsInteropUtil;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.impl.GwtAstBuilder;
 import com.google.gwt.dev.jjs.impl.JjsUtils;
@@ -623,7 +624,7 @@
 
   @Override
   public String getQualifiedJsName() {
-    return jsNamespace.isEmpty() ? getJsName() : jsNamespace + "." + getJsName();
+    return JsInteropUtil.isGlobal(jsNamespace) ? getJsName() : jsNamespace + "." + getJsName();
   }
 
   public NestedClassDisposition getClassDisposition() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
index c9ec78c..400e1c6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JField.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.javac.JsInteropUtil;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.SourceOrigin;
 import com.google.gwt.dev.util.StringInterner;
@@ -174,7 +175,7 @@
   @Override
   public String getQualifiedJsName() {
     String namespace = getJsNamespace();
-    return namespace.isEmpty() ? jsName : namespace + "." + jsName;
+    return JsInteropUtil.isGlobal(namespace) ? jsName : namespace + "." + jsName;
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index fa3def1..8e2af23 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.jjs.ast;
 
 import com.google.gwt.dev.common.InliningMode;
+import com.google.gwt.dev.javac.JsInteropUtil;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.SourceOrigin;
@@ -111,7 +112,7 @@
     if (jsName.isEmpty()) {
       assert !needsDynamicDispatch();
       return namespace;
-    } else if (namespace.isEmpty()) {
+    } else if (JsInteropUtil.isGlobal(namespace)) {
       assert !needsDynamicDispatch();
       return jsName;
     } else {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ClosureJsInteropExportsGenerator.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ClosureJsInteropExportsGenerator.java
index f6823f0..0ae3aa3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ClosureJsInteropExportsGenerator.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ClosureJsInteropExportsGenerator.java
@@ -13,6 +13,7 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.javac.JsInteropUtil;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.HasName;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
@@ -97,7 +98,7 @@
   }
 
   private void ensureGoogProvide(String namespace, SourceInfo info) {
-    if (!providedNamespaces.add(namespace) || namespace.isEmpty()) {
+    if (JsInteropUtil.isGlobal(namespace) || !providedNamespaces.add(namespace)) {
       return;
     }
 
@@ -114,6 +115,7 @@
   }
 
   private static JsExpression createExportQualifier(String namespace, SourceInfo sourceInfo) {
-    return JsUtils.createQualifiedNameRef(namespace.isEmpty() ? "window" : namespace, sourceInfo);
+    return JsUtils.createQualifiedNameRef(
+        JsInteropUtil.isGlobal(namespace) ? "window" : namespace, sourceInfo);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DefaultJsInteropExportsGenerator.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DefaultJsInteropExportsGenerator.java
index f4fb795..97083fe 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DefaultJsInteropExportsGenerator.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DefaultJsInteropExportsGenerator.java
@@ -15,6 +15,7 @@
 
 import static com.google.gwt.dev.js.JsUtils.createAssignment;
 
+import com.google.gwt.dev.javac.JsInteropUtil;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
@@ -85,6 +86,7 @@
 
   private void ensureProvideNamespace(JMember member, JsExpression ctor) {
     String namespace = member.getJsNamespace();
+    namespace = JsInteropUtil.isGlobal(namespace) ? "" : namespace;
     if (namespace.equals(lastExportedNamespace)) {
       return;
     }
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 bc9cc5b..3625162 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
@@ -387,18 +387,19 @@
   private <T extends HasJsName & HasSourceInfo> void checkJsName(T item) {
     if (item.getJsName().isEmpty()) {
       logError(item, "%s cannot have an empty name.", getDescription(item));
-      return;
-    }
-    if (!JsUtils.isValidJsIdentifier(item.getJsName())) {
+    } else if (!JsUtils.isValidJsIdentifier(item.getJsName())) {
       logError(item, "%s has invalid name '%s'.", getDescription(item), item.getJsName());
-      return;
     }
   }
 
   private <T extends HasJsName & HasSourceInfo> void checkJsNamespace(T item) {
-      String jsNamespace = item.getJsNamespace();
-    if (!jsNamespace.isEmpty() && !JsUtils.isValidJsQualifiedName(jsNamespace)) {
-      logError(item, "%s has invalid namespace '%s'.", getDescription(item), jsNamespace);
+    if (JsInteropUtil.isGlobal(item.getJsNamespace())) {
+      return;
+    }
+    if (item.getJsNamespace().isEmpty()) {
+      logError(item, "%s cannot have an empty namespace.", getDescription(item));
+    } else if (!JsUtils.isValidJsQualifiedName(item.getJsNamespace())) {
+      logError(item, "%s has invalid namespace '%s'.", getDescription(item), item.getJsNamespace());
     }
   }
 
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 9e479b4..00d0880 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
@@ -1000,6 +1000,7 @@
         "@JsType(namespace = \"a.b.\") public static class Buggy {",
         "   @JsMethod(namespace = \"34s\") public static void m() {}",
         "   @JsProperty(namespace = \"s^\") public static int  n;",
+        "   @JsMethod(namespace = \"\") public static void o() {}",
         "   @JsProperty(namespace = \"\") public int p;",
         "   @JsMethod(namespace = \"a\") public void q() {}",
         "}");
@@ -1008,18 +1009,20 @@
         "Line 6: 'EntryPoint.Buggy' has invalid namespace 'a.b.'.",
         "Line 7: 'void EntryPoint.Buggy.m()' has invalid namespace '34s'.",
         "Line 8: 'int EntryPoint.Buggy.n' has invalid namespace 's^'.",
-        "Line 9: Instance member 'int EntryPoint.Buggy.p' cannot declare a namespace.",
-        "Line 10: Instance member 'void EntryPoint.Buggy.q()' cannot declare a namespace.");
+        "Line 9: 'void EntryPoint.Buggy.o()' cannot have an empty namespace.",
+        "Line 10: Instance member 'int EntryPoint.Buggy.p' cannot declare a namespace.",
+        "Line 11: Instance member 'void EntryPoint.Buggy.q()' cannot declare a namespace.");
   }
 
-  public void testJsNameEmptyNamespacesSucceeds() throws Exception {
+  public void testJsNameGlobalNamespacesSucceeds() throws Exception {
     addSnippetImport("jsinterop.annotations.JsType");
     addSnippetImport("jsinterop.annotations.JsMethod");
     addSnippetImport("jsinterop.annotations.JsProperty");
+    addSnippetImport("jsinterop.annotations.JsPackage");
     addSnippetClassDecl(
-        "@JsType(namespace = \"\") public static class Buggy {",
-        "   @JsMethod(namespace = \"\") public static void m() {}",
-        "   @JsProperty(namespace = \"\") public static int  n;",
+        "@JsType(namespace = JsPackage.GLOBAL) public static class Buggy {",
+        "   @JsMethod(namespace = JsPackage.GLOBAL) public static void m() {}",
+        "   @JsProperty(namespace = JsPackage.GLOBAL) public static int  n;",
         "}");
 
     assertBuggySucceeds();
diff --git a/user/src/jsinterop/annotations/JsPackage.java b/user/src/jsinterop/annotations/JsPackage.java
index 92be8ec..d7c766c 100644
--- a/user/src/jsinterop/annotations/JsPackage.java
+++ b/user/src/jsinterop/annotations/JsPackage.java
@@ -29,7 +29,10 @@
 @Target(ElementType.PACKAGE)
 @Documented
 public @interface JsPackage {
-  String GLOBAL = "";
+  /**
+   * Namespace for the global JavaScript object.
+   */
+  String GLOBAL = "<global>";
 
   String namespace();
 }
diff --git a/user/test/com/google/gwt/core/client/interop/MyClassWithEmptyNamespace.java b/user/test/com/google/gwt/core/client/interop/MyClassWithEmptyNamespace.java
index 560d787..189ddfb 100644
--- a/user/test/com/google/gwt/core/client/interop/MyClassWithEmptyNamespace.java
+++ b/user/test/com/google/gwt/core/client/interop/MyClassWithEmptyNamespace.java
@@ -16,13 +16,14 @@
 package com.google.gwt.core.client.interop;
 
 import jsinterop.annotations.JsConstructor;
+import jsinterop.annotations.JsPackage;
 import jsinterop.annotations.JsProperty;
 import jsinterop.annotations.JsType;
 
 /**
  * Class with empty namespace.
  */
-@JsType(namespace = "")
+@JsType(namespace = JsPackage.GLOBAL)
 public class MyClassWithEmptyNamespace {
   @JsProperty
   public static final int DAN = 82;