Introduces special handling of namespace = "window".

Namespace JsPackage.GLOBAL references elements in the main window
scope,  which in GWT applications refer through '$wnd'.

This cl introduces namespace = "window" that allows to reference
the (iframe) top scope.

This is mainly done so that some operations (like String.fromCharCode)
are called through the iframe definition to address performance
implications in some browsers if the accesses where made through the
main window.

Bug: #9405
Bug-Link: https://github.com/gwtproject/gwt/issues/9405
Change-Id: Ic5b7e9d492aa0795ed8d3f6e3041bc7915e3c99a
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index d44f2d0..8d29d1a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -527,7 +527,6 @@
     private final JsName arrayLength = objectScope.declareUnobfuscatableName("length");
     private final JsName globalTemp = topScope.declareUnobfuscatableName("_");
     private final JsName prototype = objectScope.declareUnobfuscatableName("prototype");
-    private final JsName call = objectScope.declareUnobfuscatableName("call");
 
     @Override
     public JsExpression transformArrayLength(JArrayLength expression) {
@@ -898,7 +897,7 @@
       JsNameRef methodNameRef;
       if (method.isJsNative()) {
         // Construct Constructor.prototype.jsname or Constructor.
-        methodNameRef = createGlobalQualifier(method.getQualifiedJsName(), sourceInfo);
+        methodNameRef = createGlobalQualifier(method, sourceInfo);
       } else if (method.isConstructor()) {
         /*
          * Constructor calls through {@code this} and {@code super} are always dispatched statically
@@ -980,7 +979,7 @@
       JConstructor ctor = newInstance.getTarget();
       JsName ctorName = names.get(ctor);
       JsNameRef  reference = ctor.isJsNative()
-          ? createGlobalQualifier(ctor.getQualifiedJsName(), sourceInfo)
+          ? createGlobalQualifier(ctor, sourceInfo)
           : ctorName.makeRef(sourceInfo);
       List<JsExpression> arguments = transform(newInstance.getArgs());
 
@@ -1105,7 +1104,7 @@
       JMethod method = jsniMethodRef.getTarget();
       if (method.isJsNative()) {
         // Construct Constructor.prototype.jsname or Constructor.
-        return createGlobalQualifier(method.getQualifiedJsName(), jsniMethodRef.getSourceInfo());
+        return createGlobalQualifier(method, jsniMethodRef.getSourceInfo());
       }
       return names.get(method).makeRef(jsniMethodRef.getSourceInfo());
     }
@@ -1688,7 +1687,7 @@
     }
 
     private JsExpression buildClosureStyleCastMapFromArrayLiteral(
-            List<JsExpression> runtimeTypeIdLiterals, SourceInfo sourceInfo) {
+        List<JsExpression> runtimeTypeIdLiterals, SourceInfo sourceInfo) {
       /*
        * goog.object.createSet('foo', 'bar', 'baz') is optimized by closure compiler into
        * {'foo': !0, 'bar': !0, baz: !0}
@@ -1720,7 +1719,7 @@
     private JsNameRef createStaticReference(JMember member, SourceInfo sourceInfo) {
       assert member.isStatic();
       return member.isJsNative()
-          ? createGlobalQualifier(member.getQualifiedJsName(), sourceInfo)
+          ? createGlobalQualifier(member, sourceInfo)
           : names.get(member).makeRef(sourceInfo);
     }
 
@@ -2015,8 +2014,7 @@
     private void maybeCopyObjProperties(
         JDeclaredType type, JsExpression toPrototype, JsExpression fromPrototype) {
       if (getSuperPrototype(type) != null && !type.isJsFunctionImplementation()) {
-        JsStatement statement =
-        constructInvocation(type.getSourceInfo(),
+        JsStatement statement = constructInvocation(type.getSourceInfo(),
             RuntimeConstants.RUNTIME_COPY_OBJECT_PROPERTIES,
             fromPrototype,
             toPrototype)
@@ -2037,7 +2035,7 @@
     }
 
     private void generateClassDefinition(JDeclaredType type) {
-        assert !program.isRepresentedAsNativeJsPrimitive(type);
+      assert !program.isRepresentedAsNativeJsPrimitive(type);
 
       if (closureCompilerFormatEnabled) {
         generateClosureTypeDefinition(type);
@@ -2242,10 +2240,10 @@
       generatePrototypeAssignment(method, name, rhs, method.getJsMemberType());
     }
 
-     /**
-      * Create a vtable assignment of the form _.polyname = rhs; and register the line as
-      * created for {@code method}.
-      */
+    /**
+     * Create a vtable assignment of the form _.polyname = rhs; and register the line as
+     * created for {@code method}.
+     */
     private void generatePrototypeAssignment(JMethod method, JsName name, JsExpression rhs,
         JsMemberType memberType) {
       SourceInfo sourceInfo = method.getSourceInfo();
@@ -2275,9 +2273,9 @@
 
       JsObjectLiteral definePropertyLiteral =
           JsObjectLiteral.builder(sourceInfo)
-               // {name: {get: function() { ..... }} or {set : function (v) {....}}}
+              // {name: {get: function() { ..... }} or {set : function (v) {....}}}
               .add(name, JsObjectLiteral.builder(sourceInfo)
-                      // {get: function() { ..... }} or {set : function (v) {....}}
+                  // {get: function() { ..... }} or {set : function (v) {....}}
                   .add(method.getJsMemberType().getPropertyAccessorKey(), methodDefinitionStatement)
                   .build())
               .build();
@@ -2335,7 +2333,7 @@
      * variable _ points the JavaScript prototype for {@code type}.
      */
     private void generatePrototypeDefinitions(JDeclaredType type) {
-        assert !program.isRepresentedAsNativeJsPrimitive(type);
+      assert !program.isRepresentedAsNativeJsPrimitive(type);
 
       // Emit synthetic methods first. In JsInterop we allow a more user written method to be named
       // with the same name as a synthetic bridge (required due to generics) relying that the
@@ -2392,24 +2390,6 @@
       }
     }
 
-    public JsNameRef createGlobalQualifier(String qualifier, SourceInfo sourceInfo) {
-      assert !qualifier.isEmpty();
-
-      return JsUtils.createQualifiedNameRef(
-          isQualifiedThroughSpecialGlobalName(qualifier) ? qualifier : ("$wnd." + qualifier),
-          sourceInfo);
-    }
-
-    /*
-     * Some global names are considered special and should be used unqualified. Such names should
-     * not be emitted with the "$wnd." prefix as there seems to be runtime performance implications.
-     * {@see TypeCategory} for the full list.
-     */
-    private boolean isQualifiedThroughSpecialGlobalName(String qualifiedName) {
-      String topLevelName = qualifiedName.split("\\.")[0];
-      return TypeCategory.isSpecialGlobalName(topLevelName);
-    }
-
     /**
      * Returns either _ or ClassCtor.prototype depending on output mode.
      */
@@ -3088,4 +3068,25 @@
   private JsName getIndexedFieldJsName(String indexedName) {
     return names.get(program.getIndexedField(indexedName));
   }
+
+  private static final String WINDOW = "window";
+
+  private static boolean isWindow(String jsNamespace) {
+    return jsNamespace != null
+        && (WINDOW.equals(jsNamespace) || jsNamespace.startsWith(WINDOW + "."));
+  }
+
+  private static JsNameRef createGlobalQualifier(JMember member, SourceInfo sourceInfo) {
+    if (isWindow(member.getJsNamespace())) {
+      return JsUtils.createQualifiedNameRef(
+          member.getQualifiedJsName().substring(WINDOW.length() + 1), sourceInfo);
+    }
+    return createGlobalQualifier(member.getQualifiedJsName(), sourceInfo);
+  }
+
+  private static JsNameRef createGlobalQualifier(String qualifier, SourceInfo sourceInfo) {
+    assert !qualifier.isEmpty();
+
+    return JsUtils.createQualifiedNameRef("$wnd." + qualifier, sourceInfo);
+  }
 }
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 0b07d85..a23d114 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
@@ -1,11 +1,11 @@
 /*
  * 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
@@ -567,13 +567,10 @@
   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()) {
+    } else if ((item.isJsNative() && !JsUtils.isValidJsQualifiedName(item.getJsName()))
+        || (!item.isJsNative() && !JsUtils.isValidJsIdentifier(item.getJsName()))) {
       // 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/src/com/google/gwt/dev/jjs/impl/TypeCategory.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
index 6ae81a0..bd4db2c 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
@@ -135,10 +135,6 @@
     return TypeCategory.TYPE_JAVA_OBJECT;
   }
 
-  public static boolean isSpecialGlobalName(String name) {
-    return specialGlobalNames.containsKey(name);
-  }
-
   private static Map<String, TypeCategory> specialGlobalNames =
       ImmutableMap.<String,TypeCategory>builder()
           .put("Object", TYPE_JS_OBJECT)
diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java
index 3722ce2..3beff7c 100644
--- a/user/super/com/google/gwt/emul/java/lang/String.java
+++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -157,12 +157,12 @@
   }
 
   @JsType(isNative = true, name = "Function", namespace = JsPackage.GLOBAL)
-  private static class NativeFunction {
-    public native String apply(String thisContext, Object[] argsArray);
+  private static class NativeFunction<T> {
+    public native T apply(Object thisContext, Object[] argsArray);
   }
 
-  @JsProperty(name = "fromCharCode", namespace = "String")
-  private static native NativeFunction getFromCharCodeFunction();
+  @JsProperty(name = "String.fromCharCode", namespace = "window")
+  private static native NativeFunction<String> getFromCharCodeFunction();
 
   public static String valueOf(char[] x) {
     return valueOf(x, 0, x.length);
@@ -410,7 +410,7 @@
   public boolean equals(Object other) {
     // Java equality is translated into triple equality which is a quick to compare strings for
     // equality without any instanceOf checks.
-    return checkNotNull(this) == other;
+    return this == other;
   }
 
   public boolean equalsIgnoreCase(String other) {
@@ -489,11 +489,11 @@
   public int lastIndexOf(String str) {
     return asNativeString().lastIndexOf(str);
   }
-  
+
   public int lastIndexOf(String str, int start) {
     return asNativeString().lastIndexOf(str, start);
   }
-  
+
   @Override
   public int length() {
     return asNativeString().length;
@@ -724,7 +724,7 @@
   public String toUpperCase() {
     return asNativeString().toLocaleUpperCase();
   }
-  
+
   // See the notes in lowerCase pair.
   public String toUpperCase(Locale locale) {
     return locale == Locale.getDefault()
@@ -753,7 +753,7 @@
     return start > 0 || end < length ? substring(start, end) : this;
   }
 
-  @JsType(isNative = true, name = "String", namespace = JsPackage.GLOBAL)
+  @JsType(isNative = true, name = "String", namespace = "window")
   private static class NativeString {
     public static native String fromCharCode(char x);
     public int length;
diff --git a/user/test/com/google/gwt/dev/jjs/optimized/StringOptimizationTest.java b/user/test/com/google/gwt/dev/jjs/optimized/StringOptimizationTest.java
index a42b7a9..e529a00 100644
--- a/user/test/com/google/gwt/dev/jjs/optimized/StringOptimizationTest.java
+++ b/user/test/com/google/gwt/dev/jjs/optimized/StringOptimizationTest.java
@@ -31,7 +31,7 @@
   private static native String getGeneratedFunctionDefintionThatTriggersStringClinit() /*-{
     return function() {
       tmp = @StringOptimizationTest::createString()();
-     }.toString();
+    }.toString();
   }-*/;
 
   public void testStringClinitIsRemoved() throws Exception {
@@ -53,5 +53,4 @@
     String functionDef = getGeneratedFunctionDefintionThatCallsStringFromCharCode();
     assertFunctionMatches(functionDef, "tmp=String.fromCharCode('c')");
   }
-
 }