Make Closure output more idiomatic

The following changes result in an 11% improvement in Inbox when
compiling with closure.

In Closure mode, a synthetic constructor is generated where the
generated prototype of the constructor is explicitly assigned to
real constructors.

All members are now assigned explicitly by setting on the prototype
of the synthesized constructor explicitly (e.g. Foo.prototoype.method)
instead of assigning via shared global (e.g. _.method).

This is because when the Closure compiler sees every method assigned
as _.method = expr;, it has no idea what '_' and how to associate
a given method with a given constructor. By changing this to
Foo.prototype.method = expr, the compiler can now associate
instantiations of Foo with liveness of methods assigned to it.

The GWT compiler now has responsibility for generating all
goog.provide statements and setting up empty functions on
goog.provided interface types.

The reason this is done is several fold. The closure compiler
will error if any goog.provide happens more than once, and it
will error if a goog.provide or a namespace is setup out of
order (enclosed type before enclosing type). Previously, a linker
woud generate all of the goog.provide statements as well as
setup stub functions for interfaces. However since linkers can't
(easily) insert code in the right order (like setting up a empty
interface stub function) in the topologically correct order with
those setup by the GWT compiler itself, pushing this reponsibility
to the compiler removes errors from bad sorting.

Also, double assigning any namespace causes deoptimization.
Previously the linker would pre-pend a bunch of empty stub
constructors which are written over by the main GWT module.

With this patch, now also Closure Mode correctly applies JsInterop
spec and reaches feature parity to non-Closure mode.

Finally, this is a step along the way to getting rid of the
linker, and having GWT output @JsDoc'ed class definitions.

Change-Id: I66aae7a745bb36059f1f0670d2820861527f2fc9
Review-Link: https://gwt-review.googlesource.com/#/c/12181/
diff --git a/dev/core/src/com/google/gwt/dev/DevMode.java b/dev/core/src/com/google/gwt/dev/DevMode.java
index 6d0499c..07d41b7 100644
--- a/dev/core/src/com/google/gwt/dev/DevMode.java
+++ b/dev/core/src/com/google/gwt/dev/DevMode.java
@@ -278,6 +278,7 @@
     private File moduleBaseDir;
     private String modulePathPrefix = "";
     private File warDir;
+    private boolean closureCompilerFormatEnabled;
 
     @Override
     public File getDeployDir() {
@@ -384,11 +385,12 @@
 
     @Override
     public boolean isClosureCompilerFormatEnabled() {
-      return false;
+      return closureCompilerFormatEnabled;
     }
 
     @Override
     public void setClosureCompilerFormatEnabled(boolean enabled) {
+      this.closureCompilerFormatEnabled = enabled;
     }
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index c6ffa93..6c6337a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -558,7 +558,8 @@
          */
         Event functionClusterEvent = SpeedTracerLogger.start(CompilerEventType.FUNCTION_CLUSTER);
         // TODO(cromwellian) move to the Js AST optimization, re-enable sourcemaps + clustering
-        if (!sourceMapsEnabled && options.shouldClusterSimilarFunctions()
+        if (!sourceMapsEnabled && !options.isClosureCompilerFormatEnabled()
+            && options.shouldClusterSimilarFunctions()
             && options.getNamespace() == JsNamespaceOption.NONE
             && options.getOutput() == JsOutputOption.OBFUSCATED) {
           transformer = new JsFunctionClusterer(transformer);
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
new file mode 100644
index 0000000..33054b8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ClosureJsInteropExportsGenerator.java
@@ -0,0 +1,122 @@
+/*
+ * 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.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.HasName;
+import com.google.gwt.dev.jjs.ast.JConstructor;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JMember;
+import com.google.gwt.dev.js.JsUtils;
+import com.google.gwt.dev.js.ast.JsExpression;
+import com.google.gwt.dev.js.ast.JsInvocation;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.js.ast.JsStringLiteral;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Responsible for handling @JsExport code generation for Closure formatted code.
+ * <p>
+ * In closure formatted mode, there are additional restrictions due to the way goog.provide() works:
+ * <li> You can't goog.provide something more than once
+ * <li> Enclosing namespaces must be setup and handled before enclosed namespaces
+ * <li> If the exported namespace is a @JsType with methods, than the namespace will need to have
+ * a function assigned to it, instead of an object literal returned by goog.provide.
+ * <p>
+ * In general, this implies code like the following:
+ * <pre>
+ * goog.provide('dotted.parent.namespace')
+ * dotted.parent.namespace = constructor (synthesized one or the exported one)
+ * dotted.parent.namespace.member = blah;
+ * goog.provide('dotted.parent.namespace.enclosed')
+ * dotted.parent.namespace.enclosed = (synthesized one or the exported one)
+ * dotted.parent.namespace.enclosed.member = blah;
+ * </pre>
+ */
+class ClosureJsInteropExportsGenerator implements JsInteropExportsGenerator {
+
+  private final List<JsStatement> exportStmts;
+  private final Map<HasName, JsName> names;
+  private final Set<String> providedNamespaces = new HashSet<String>();
+
+  public ClosureJsInteropExportsGenerator(List<JsStatement> exportStmts,
+      Map<HasName, JsName> names) {
+    this.exportStmts = exportStmts;
+    this.names = names;
+  }
+
+  /**
+   * Ensures that a @JsType without exported constructor is still defined by goog.provide using the
+   * synthesized constructor so that type declarations like
+   * {@code /* @returns {number} * / Foo.prototype.method;} that are added by linker works.
+   */
+  @Override
+  public void exportType(JDeclaredType x) {
+    // Note that synthesized constructors use the name of the declared types.
+    generateExport(x.getQualifiedExportName(), x.getQualifiedExportName(), x, x.getSourceInfo());
+  }
+
+  /*
+   * Exports a member as:
+   *  goog.provide('foo.bar');
+   *  foo.bar.memberName = <obfuscated-member-name>;
+   * or constructor as:
+   *  goog.provide('foo.bar.ClassSimpleName');
+   *  foo.bar.ClassSimpleName = <obfuscated-ctor-name>;
+   */
+  @Override
+  public void exportMember(JMember member) {
+    // TODO(goktug): fix export namespace for constructor to be same as qualified export name.
+    String namespace = member instanceof JConstructor ? member.getQualifiedExportName()
+        : member.getExportNamespace();
+
+    generateExport(namespace, member.getQualifiedExportName(), member, member.getSourceInfo());
+  }
+
+  private void generateExport(String exportNamespace, String qualifiedExportName, HasName nameRef,
+      SourceInfo sourceInfo) {
+    // goog.provide("a.b.c")
+    ensureGoogProvide(exportNamespace, sourceInfo);
+    // a.b.c = a_b_c_obf
+    generateAssignment(nameRef, qualifiedExportName, sourceInfo);
+  }
+
+  private void ensureGoogProvide(String namespace, SourceInfo info) {
+    if (!providedNamespaces.add(namespace) || namespace.isEmpty()) {
+      return;
+    }
+
+    JsNameRef provideFuncRef = JsUtils.createQualifier("goog.provide", info);
+    JsInvocation provideCall = new JsInvocation(info);
+    provideCall.setQualifier(provideFuncRef);
+    provideCall.getArguments().add(new JsStringLiteral(info, namespace));
+    exportStmts.add(provideCall.makeStmt());
+  }
+
+  private void generateAssignment(HasName nameRef, String exportName, SourceInfo sourceInfo) {
+    JsExpression lhs = createExportQualifier(exportName, sourceInfo);
+    JsNameRef rhs = names.get(nameRef).makeRef(sourceInfo);
+    exportStmts.add(JsUtils.createAssignment(lhs, rhs).makeStmt());
+  }
+
+  private static JsExpression createExportQualifier(String namespace, SourceInfo sourceInfo) {
+    return JsUtils.createQualifier(namespace.isEmpty() ? "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
new file mode 100644
index 0000000..0c23173
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DefaultJsInteropExportsGenerator.java
@@ -0,0 +1,94 @@
+/*
+ * 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.dev.jjs.impl;
+
+import static com.google.gwt.dev.js.JsUtils.createAssignment;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.HasName;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JMember;
+import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsInvocation;
+import com.google.gwt.dev.js.ast.JsName;
+import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsStatement;
+import com.google.gwt.dev.js.ast.JsStringLiteral;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Responsible for handling @JsExport code generation for non-Closure formatted code.
+ * <p>
+ * Generally, export of global namespaced members looks like this:
+ * <pre>
+ * _ = provide('dotted.namespace')
+ * _.memberName = original
+ * </pre>
+ * Essentially members are aliased into a global namespace.
+ */
+class DefaultJsInteropExportsGenerator implements JsInteropExportsGenerator {
+
+  private final List<JsStatement> exportStmts;
+  private final Map<HasName, JsName> names;
+  private final JsName globalTemp;
+  private final JsName provideFunc;
+  private String lastExportedNamespace;
+
+  public DefaultJsInteropExportsGenerator(List<JsStatement> exportStmts, Map<HasName, JsName> names,
+      JsName globalTemp, Map<String, JsFunction> indexedFunctions) {
+    this.exportStmts = exportStmts;
+    this.names = names;
+    this.globalTemp = globalTemp;
+    this.provideFunc = indexedFunctions.get("JavaClassHierarchySetupUtil.provide").getName();
+  }
+
+  @Override
+  public void exportType(JDeclaredType x) {
+    // non-Closure mode doesn't do anything special to export types
+  }
+
+  /*
+   * Exports a member as
+   *  _ = provide('foo.bar.ExportNamespace')
+   *  _.memberName = RHS
+   *
+   * TODO(goktug): optimizing provide calls shouldn't be difficult as exports are now sorted.
+   */
+  @Override
+  public void exportMember(JMember x) {
+    // _ = provide('foo.bar.ExportNamespace')
+    ensureProvideNamespace(x.getExportNamespace(), x.getSourceInfo());
+
+    // _.memberName = RHS
+    JsNameRef lhs = new JsNameRef(x.getSourceInfo(), x.getExportName());
+    lhs.setQualifier(globalTemp.makeRef(x.getSourceInfo()));
+    JsNameRef rhs = names.get(x).makeRef(x.getSourceInfo());
+    exportStmts.add(createAssignment(lhs, rhs).makeStmt());
+  }
+
+  private void ensureProvideNamespace(String namespace, SourceInfo sourceInfo) {
+    if (namespace.equals(lastExportedNamespace)) {
+      return;
+    }
+    lastExportedNamespace = namespace;
+
+    // _ = JCHSU.provide('foo.bar')
+    JsInvocation provideCall = new JsInvocation(sourceInfo);
+    provideCall.setQualifier(provideFunc.makeRef(sourceInfo));
+    provideCall.getArguments().add(new JsStringLiteral(sourceInfo, namespace));
+    exportStmts.add(createAssignment(globalTemp.makeRef(sourceInfo), provideCall).makeStmt());
+  }
+}
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 b7b475a..6bea4fd 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
@@ -103,6 +103,7 @@
 import com.google.gwt.dev.jjs.ast.js.JsonArray;
 import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeMapper;
 import com.google.gwt.dev.js.JsStackEmulator;
+import com.google.gwt.dev.js.JsUtils;
 import com.google.gwt.dev.js.ast.JsArrayAccess;
 import com.google.gwt.dev.js.ast.JsArrayLiteral;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
@@ -193,6 +194,7 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.TreeMap;
 
 /**
  * Creates a JavaScript AST from a <code>JProgram</code> node.
@@ -736,7 +738,7 @@
 
     private final Set<JClassType> alreadyRan = Sets.newLinkedHashSet();
 
-    private final List<JsStatement> exportStmts = new ArrayList<JsStatement>();
+    private final Map<String, Object> exportedMembersByExportName = new TreeMap<String, Object>();
 
     private final JsName arrayLength = objectScope.declareName("length");
 
@@ -746,8 +748,6 @@
 
     private JMethod currentMethod = null;
 
-    private String lastExportedNamespace = null;
-
     private final JsName globalTemp = topScope.declareName("_");
 
     private final JsName prototype = objectScope.declareName("prototype");
@@ -951,8 +951,7 @@
       if (typeOracle.isJsInteropEnabled() &&
           typeOracle.isInstantiatedType(x) && !typeOracle.isJavaScriptObject(x) &&
         x !=  program.getTypeJavaLangString()) {
-        // done after class setup because exports may rely on static vars
-        generateExports(x);
+        collectExports(x);
       }
 
       // TODO(zundel): Check that each unique method has a unique
@@ -1064,7 +1063,7 @@
         // for non-statics, only setup an assignment if needed
         if (rhs != null) {
           JsNameRef fieldRef = name.makeRef(x.getSourceInfo());
-          fieldRef.setQualifier(globalTemp.makeRef(x.getSourceInfo()));
+          fieldRef.setQualifier(getPrototypeQualifierOf(x));
           JsExpression asg = createAssignment(fieldRef, rhs);
           push(new JsExprStmt(x.getSourceInfo(), asg));
         } else {
@@ -1202,7 +1201,17 @@
       }
 
       if (typeOracle.isJsInteropEnabled()) {
-        generateExports(x);
+        /*
+         * If a @JsType is exported, but no constructors are, @JsDoc type declarations added by the
+         * linker will fail in uncompiled mode, as they will try to access the 'Foo.prototype' which
+         * is undefined even though the goog.provide('Foo') statement exists. Here we synthesize a
+         * simple constructor to aid the linker.
+         */
+        if (closureCompilerFormatEnabled && x.isJsType()) {
+          declareSynthesizedClosureConstructor(x, globalStmts);
+        }
+
+        collectExports(x);
       }
     }
 
@@ -1587,7 +1596,7 @@
         JsNameRef protoRef = prototype.makeRef(x.getSourceInfo());
         methodRef = new JsNameRef(methodRef.getSourceInfo(), method.getName());
         // add qualifier so we have jsPrototype.prototype.methodName.call(this, args)
-        protoRef.setQualifier(createQualifier(jsPrototype, x.getSourceInfo(), true));
+        protoRef.setQualifier(createJsQualifier(jsPrototype, x.getSourceInfo()));
         methodRef.setQualifier(protoRef);
         qualifier.setQualifier(methodRef);
         return jsInvocation;
@@ -1913,9 +1922,8 @@
       if (program.isReferenceOnly(program.getIndexedType("Class"))) {
         return Collections.emptyList();
       }
-
-      // These are the classes included in the preamble, i.e.
-      // all classes that are reachable for Class.createForClass, Class.createForInterface
+      // Include in the preamble all classes that are reachable for Class.createForClass,
+      // Class.createForInterface
       SortedSet<JDeclaredType> reachableClasses =
           computeReachableTypes(METHODS_PROVIDED_BY_PREAMBLE);
 
@@ -2004,7 +2012,7 @@
           Predicates.not(Predicates.<JType>in(alreadyRan))));
 
       // add all @JsExport assignments
-      globalStmts.addAll(exportStmts);
+      generateExports(globalStmts);
 
       // Generate entry methods. Needs to be after class literal insertion since class literal will
       // be referenced by runtime rebind and property provider bootstrapping.
@@ -2033,6 +2041,47 @@
       }
     }
 
+    private void generateExports(List<JsStatement> globalStmts) {
+      if (exportedMembersByExportName.isEmpty()) {
+        return;
+      }
+
+      JsInteropExportsGenerator exportGenerator;
+      if (closureCompilerFormatEnabled) {
+        exportGenerator = new ClosureJsInteropExportsGenerator(globalStmts, names);
+      } else {
+        exportGenerator = new DefaultJsInteropExportsGenerator(globalStmts, names, globalTemp,
+            indexedFunctions);
+      }
+
+      Set<JDeclaredType> generatedClinits = Sets.newHashSet();
+
+      for (Object exportedEntity : exportedMembersByExportName.values()) {
+        if (exportedEntity instanceof JDeclaredType) {
+          exportGenerator.exportType((JDeclaredType) exportedEntity);
+        } else {
+          JMember member = (JMember) exportedEntity;
+          maybeHoistClinit(globalStmts, generatedClinits, member);
+          exportGenerator.exportMember(member);
+        }
+      }
+    }
+
+    private void maybeHoistClinit(List<JsStatement> globalStmts,
+        Set<JDeclaredType> generatedClinits, JMember member) {
+      JDeclaredType enclosingType = member.getEnclosingType();
+      if (generatedClinits.contains(enclosingType)) {
+        return;
+      }
+
+      JsInvocation clinitCall = member instanceof JMethod ? maybeCreateClinitCall((JMethod) member)
+          : maybeCreateClinitCall((JField) member, true);
+      if (clinitCall != null) {
+        generatedClinits.add(enclosingType);
+        globalStmts.add(clinitCall.makeStmt());
+      }
+    }
+
     @Override
     public boolean visit(JsniMethodBody x, Context ctx) {
       final Map<String, JNode> jsniMap = Maps.newHashMap();
@@ -2548,10 +2597,8 @@
       }
     }
 
-    private void generateClassDefinition(JClassType x, List<JsStatement> globalStmts) {
-      SourceInfo sourceInfo = x.getSourceInfo();
-      assert x != program.getTypeJavaLangString();
-
+    private void generateCallToDefineClass(JClassType x, List<JsStatement> globalStmts,
+        List<JsNameRef> constructorArgs) {
       JExpression typeId = getRuntimeTypeReference(x);
       JClassType superClass = x.getSuperClass();
       JExpression superTypeId = (superClass == null) ? JNullLiteral.INSTANCE :
@@ -2568,27 +2615,16 @@
         defineClassArguments.add(convertJavaLiteral(superTypeId));
       } else {
         // setup extension of native JS object
-        JsNameRef jsProtoClassRef = createQualifier(jsPrototype, x.getSourceInfo(), true);
-        JsNameRef jsProtoFieldRef = new JsNameRef(x.getSourceInfo(), "prototype");
-
-        jsProtoFieldRef.setQualifier(jsProtoClassRef);
-        defineClassArguments.add(jsProtoClassRef);
+        defineClassArguments.add(createJsQualifier(jsPrototype, x.getSourceInfo()));
       }
       JsExpression castMap = generateCastableTypeMap(x);
       defineClassArguments.add(castMap);
 
-      // Chain assign the same prototype to every live constructor.
-      for (JMethod method : x.getMethods()) {
-        if (!isMethodPotentiallyALiveConstructor(method)) {
-          // Some constructors are never newed hence don't need to be registered with defineClass.
-          continue;
-        }
-        defineClassArguments.add(names.get(method).makeRef(sourceInfo));
-      }
+      defineClassArguments.addAll(constructorArgs);
 
       // choose appropriate setup function
       // JavaClassHierarchySetupUtil.defineClass(typeId, superTypeId, castableMap, constructors)
-      JsStatement defineClassStatement = constructInvocation(sourceInfo,
+      JsStatement defineClassStatement = constructInvocation(x.getSourceInfo(),
           jsPrototype == null ? "JavaClassHierarchySetupUtil.defineClass" :
               "JavaClassHierarchySetupUtil.defineClassWithPrototype",
           defineClassArguments).makeStmt();
@@ -2596,6 +2632,86 @@
       typeForStatMap.put(defineClassStatement, x);
     }
 
+    private void generateClassDefinition(JClassType x, List<JsStatement> globalStmts) {
+      assert x != program.getTypeJavaLangString();
+
+      if (closureCompilerFormatEnabled) {
+        generateClosureClassDefinition(x, globalStmts);
+      } else {
+        generateJsClassDefinition(x, globalStmts);
+      }
+    }
+
+    /*
+     * Class definition for regular output looks like:
+     *
+     * defineClass(id, superId, castableTypeMap, ctor1, ctor2, ctor3);
+     * _.method1 = function() { ... }
+     * _.method2 = function() { ... }
+     */
+    private void generateJsClassDefinition(JClassType x, List<JsStatement> globalStmts) {
+      // Add constructors as varargs to define class.
+      List<JsNameRef> constructorArgs = Lists.newArrayList();
+      for (JMethod method : getPotentiallyAliveConstructors(x)) {
+        constructorArgs.add(names.get(method).makeRef(x.getSourceInfo()));
+      }
+
+      // defineClass(..., Ctor1, Ctor2, ...)
+      generateCallToDefineClass(x, globalStmts, constructorArgs);
+    }
+
+    /*
+     * Class definition for closure output looks like:
+     *
+     * defineClass(jsinterop.getUniqueId('FQTypeName'),
+     *     jsinterop.getUniqueId('FQSuperTypeName'), ctm);
+     * var ClassName = defineHiddenClosureConstructor();
+     * ClassName.prototype.method1 = function() { ... };
+     * ClassName.prototype.method2 = function() { ... };
+     * ctor1.prototype = ClassName.prototype;
+     * ctor2.prototype = ClassName.prototype;
+     * ctor3.prototype = ClassName.prototype;
+     *
+     * The primary change is to make the prototype assignment look like regular closure code to help
+     * the compiler disambiguate which methods belong to which type.
+     */
+    private void generateClosureClassDefinition(JClassType x, List<JsStatement> globalStmts) {
+      // defineClass(...)
+      generateCallToDefineClass(x, globalStmts, new ArrayList<JsNameRef>());
+
+      // var ClassName = defineHiddenClosureConstructor()
+      declareSynthesizedClosureConstructor(x, globalStmts);
+
+      // Ctor1.prototype = ClassName.prototype
+      // Ctor2.prototype = ClassName.prototype
+      // etc.
+      for (JMethod method : getPotentiallyAliveConstructors(x)) {
+        JsNameRef lhs = prototype.makeRef(method.getSourceInfo());
+        lhs.setQualifier(names.get(method).makeRef(method.getSourceInfo()));
+        JsNameRef rhsProtoRef = getPrototypeQualifierOf(method);
+        JsExprStmt stmt = createAssignment(lhs, rhsProtoRef).makeStmt();
+        globalStmts.add(stmt);
+        typeForStatMap.put(stmt, x);
+      }
+    }
+
+    /*
+     * Declare an empty synthesized constructor that looks like:
+     *  var ClassName = defineHiddenClosureConstructor()
+     *
+     * TODO(goktug): throw Error in the body to prevent instantiation via this constructor.
+     */
+    private void declareSynthesizedClosureConstructor(JDeclaredType x,
+        List<JsStatement> globalStmts) {
+      SourceInfo sourceInfo = x.getSourceInfo();
+      JsName classVar = topScope.declareName(JjsUtils.getNameString(x));
+      JsVar var = new JsVar(sourceInfo, classVar);
+      var.setInitExpr(constructInvocation(sourceInfo,
+          "JavaClassHierarchySetupUtil.defineHiddenClosureConstructor"));
+      globalStmts.add(new JsVars(sourceInfo, var));
+      names.put(x, classVar);
+    }
+
     /*
      * Sets up the catmap for String.
      */
@@ -2606,8 +2722,7 @@
       JsNameRef ctmRef = castableTypeMapName.makeRef(x.getSourceInfo());
 
       JsExpression castMapLit = generateCastableTypeMap(x);
-      JsExpression ctmAsg = createAssignment(ctmRef,
-          castMapLit);
+      JsExpression ctmAsg = createAssignment(ctmRef, castMapLit);
       JsExprStmt ctmAsgStmt = ctmAsg.makeStmt();
       globalStmts.add(ctmAsgStmt);
       typeForStatMap.put(ctmAsgStmt, x);
@@ -2620,7 +2735,7 @@
         // _.toString = function(){return this.java_lang_Object_toString();}
 
         // lhs
-        JsNameRef lhs = createNativeToStringRef(globalTemp.makeRef(sourceInfo));
+        JsNameRef lhs = createNativeToStringRef(getPrototypeQualifierOf(x, sourceInfo));
 
         // rhs
         JsNameRef toStringRef = new JsNameRef(sourceInfo, polymorphicNames.get(toStringMeth));
@@ -2648,7 +2763,7 @@
         JsName lhsName, JsExpression rhs) {
       SourceInfo sourceInfo = method.getSourceInfo();
       JsNameRef lhs = lhsName.makeRef(sourceInfo);
-      lhs.setQualifier(globalTemp.makeRef(sourceInfo));
+      lhs.setQualifier(getPrototypeQualifierOf(method));
       JsExprStmt polyAssignment = createAssignment(lhs, rhs).makeStmt();
       globalStmts.add(polyAssignment);
       vtableInitForMethodMap.put(polyAssignment, method);
@@ -2703,12 +2818,12 @@
           JsName polyJsName = polymorphicNames.get(method);
           generateVTableAssignment(globalStmts, method, polyJsName, rhs);
           if (typeOracle.needsJsInteropBridgeMethod(method)) {
-            JsName exportedName = polyJsName.getEnclosing().declareName(
-                method.getName(), method.getName());
+            JsName exportedName = polyJsName.getEnclosing().declareName(method.getName(),
+                method.getName());
             // _.exportedName = makeBridgeMethod(_.polyName)
             exportedName.setObfuscatable(false);
             JsNameRef polyRef = polyJsName.makeRef(sourceInfo);
-            polyRef.setQualifier(globalTemp.makeRef(sourceInfo));
+            polyRef.setQualifier(getPrototypeQualifierOf(method));
             generateVTableAssignment(globalStmts, method, exportedName,
                 createJsInteropBridgeMethod(method, polyRef));
           }
@@ -2742,8 +2857,7 @@
             // and hence this assignment will be present in the vtable setup for all subclasses.
 
             JsNameRef polyname = polyJsName.makeRef(sourceInfo);
-            polyname.setQualifier(globalTemp.makeRef(sourceInfo));
-
+            polyname.setQualifier(getPrototypeQualifierOf(method));
             generateVTableAssignment(globalStmts, method,
                 getPackagePrivateName(method),
                 polyname);
@@ -2752,27 +2866,51 @@
       }
     }
 
-    private void generateExports(JDeclaredType x) {
-      TreeLogger branch = logger.branch(TreeLogger.Type.INFO, "Exporting " + x.getName());
+    public JsNameRef createJsQualifier(String qualifier, SourceInfo sourceInfo) {
+      assert !qualifier.isEmpty();
+      return JsUtils.createQualifier("$wnd." + qualifier, sourceInfo);
+    }
 
-      boolean createdClinit = false;
+    /**
+     * Returns either _ or ClassCtor.prototype depending on output mode.
+     */
+    private JsNameRef getPrototypeQualifierOf(JMember f) {
+      return getPrototypeQualifierOf(f.getEnclosingType(), f.getSourceInfo());
+    }
+
+    /**
+     * Returns either _ or ClassCtor.prototype depending on output mode.
+     */
+    private JsNameRef getPrototypeQualifierOf(JDeclaredType type, SourceInfo info) {
+      if (closureCompilerFormatEnabled) {
+        JsNameRef protoRef = prototype.makeRef(info);
+        protoRef.setQualifier(names.get(type).makeRef(info));
+        return protoRef;
+      } else {
+        return globalTemp.makeRef(info);
+      }
+    }
+
+    private void collectExports(JDeclaredType x) {
+      if (x.isJsType()) {
+        // Note that this initial export may later be overridden by a real exported constructor.
+        exportedMembersByExportName.put(x.getQualifiedExportName(), x);
+      }
 
       for (JMethod m : x.getMethods()) {
         if (typeOracle.isExportedMethod(m)) {
-          createdClinit = maybeHoistClinit(createdClinit, maybeCreateClinitCall(m));
-          exportMember(x, m);
+          exportedMembersByExportName.put(m.getQualifiedExportName(), m);
         }
       }
 
       for (JField f : x.getFields()) {
         if (typeOracle.isExportedField(f)) {
           if (!f.isFinal()) {
-            branch.log(TreeLogger.Type.WARN, "Exporting effectively non-final field " + f.getName()
-                + " is discouraged. Due to the way exporting works, the value of the exported field"
-                + " will not be reflected across Java&JavaScript border.");
+            logger.log(TreeLogger.Type.WARN, "Exporting effectively non-final field "
+                + f.getQualifiedName() + ". Due to the way exporting works, the value of the"
+                + " exported field will not be reflected across Java/JavaScript border.");
           }
-          createdClinit = maybeHoistClinit(createdClinit, maybeCreateClinitCall(f, true));
-          exportMember(x, f);
+          exportedMembersByExportName.put(f.getQualifiedExportName(), f);
         }
       }
     }
@@ -2802,83 +2940,6 @@
       }
     }
 
-    private boolean maybeHoistClinit(boolean createdClinit, JsInvocation clInitJsInvocation) {
-      // Hoist clinit to first member that needs it
-      if (!createdClinit && clInitJsInvocation != null) {
-        exportStmts.add(clInitJsInvocation.makeStmt());
-        createdClinit = true;
-      }
-      return createdClinit;
-    }
-
-    private void exportMember(JDeclaredType x, JMember member) {
-      JsExpression exportRhs = names.get(member).makeRef(member.getSourceInfo());
-      if (closureCompilerFormatEnabled) {
-        exportMemberClosure(x, member, exportRhs);
-      } else {
-        exportMemberJs(x, member, exportRhs);
-      }
-    }
-
-    private void exportMemberJs(JDeclaredType x, JMember member, JsExpression exportRhs) {
-      SourceInfo sourceInfo = x.getSourceInfo();
-      String namespace = member.getExportNamespace();
-      if (!namespace.equals(lastExportedNamespace)) {
-        lastExportedNamespace = namespace;
-
-        JsName provideFunc = indexedFunctions.get("JavaClassHierarchySetupUtil.provide").getName();
-        JsNameRef provideFuncRef = provideFunc.makeRef(sourceInfo);
-        JsInvocation provideCall = new JsInvocation(sourceInfo);
-        provideCall.setQualifier(provideFuncRef);
-        provideCall.getArguments().add(new JsStringLiteral(sourceInfo, namespace));
-
-        // _ = JCHSU.provide('foo.bar')
-        exportStmts.add(createAssignment(globalTemp.makeRef(sourceInfo), provideCall).makeStmt());
-      }
-      JsNameRef leaf = new JsNameRef(sourceInfo, member.getExportName());
-      leaf.setQualifier(globalTemp.makeRef(sourceInfo));
-      JsExprStmt astStat = new JsExprStmt(sourceInfo, createAssignment(leaf, exportRhs));
-      exportStmts.add(astStat);
-    }
-
-    private void exportMemberClosure(JDeclaredType x, JMember member, JsExpression exportRhs) {
-      String namespace = member.getExportNamespace();
-      if (!namespace.equals(lastExportedNamespace)) {
-        lastExportedNamespace = namespace;
-        // goog.provide statements prepended by linker, so namespace already exists
-        // but enclosing constructor exports may have overwritten them
-        // so write foo.bar.Baz = foo.bar.Baz || {}
-        if (x.getEnclosingType() != null) {
-          JsNameRef lhs = createQualifier(x.getQualifiedExportName(), x.getSourceInfo(), false);
-          JsNameRef rhsRef = createQualifier(x.getQualifiedExportName(), x.getSourceInfo(), false);
-          exportStmts.add(createAssignment(lhs, new JsBinaryOperation(x.getSourceInfo(),
-              JsBinaryOperator.OR, rhsRef, new JsObjectLiteral(x.getSourceInfo()))).makeStmt());
-        }
-      }
-      SourceInfo sourceInfo = x.getSourceInfo();
-      JsNameRef leaf = new JsNameRef(sourceInfo, member.getExportName());
-      leaf.setQualifier(createQualifier(namespace, sourceInfo, false));
-      JsExprStmt astStat = new JsExprStmt(sourceInfo, createAssignment(leaf, exportRhs));
-      exportStmts.add(astStat);
-    }
-
-    private JsNameRef createQualifier(String namespace, SourceInfo sourceInfo,
-        boolean qualifyWithWnd) {
-      JsNameRef ref = null;
-      if (namespace.isEmpty() || qualifyWithWnd) {
-        ref = new JsNameRef(sourceInfo, "$wnd");
-      }
-
-      for (String part : namespace.split("\\.")) {
-        JsNameRef newRef = new JsNameRef(sourceInfo, part);
-        if (ref != null) {
-          newRef.setQualifier(ref);
-        }
-        ref = newRef;
-      }
-      return ref;
-    }
-
     /**
      * Returns the package private JsName for {@code method}.
      */
@@ -2908,6 +2969,15 @@
           || typeOracle.isExportedMethod(method) || typeOracle.isJsTypeMethod(method);
     }
 
+    private Iterable<JMethod> getPotentiallyAliveConstructors(JClassType x) {
+      return Iterables.filter(x.getMethods(), new Predicate<JMethod>() {
+        @Override
+        public boolean apply(JMethod m) {
+          return isMethodPotentiallyALiveConstructor(m);
+        }
+      });
+    }
+
     /**
      * Whether a method is a constructor that is actually newed. Note that in absence of whole
      * world knowledge evey constructor is potentially live.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropExportsGenerator.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropExportsGenerator.java
new file mode 100644
index 0000000..9740058
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsInteropExportsGenerator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JMember;
+
+/**
+ * Generates codes to handle @JsExport.
+ */
+public interface JsInteropExportsGenerator {
+  /**
+   * Makes sure the type is exported even there are no exported constructors for the type but is
+   * still marked with JsType.
+   * <p>
+   * This is essentially needed by Closure formatted output so that type declarations can be
+   * provided for JsTypes that are not exported via a constructor.
+   */
+  void exportType(JDeclaredType x);
+
+  /**
+   * Exports a member to the namespace that is provided by its qualified export name.
+   */
+  void exportMember(JMember member);
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsUtils.java b/dev/core/src/com/google/gwt/dev/js/JsUtils.java
index fa8bbd6..9107f1a 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsUtils.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsUtils.java
@@ -15,12 +15,17 @@
  */
 package com.google.gwt.dev.js;
 
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.js.ast.JsBinaryOperation;
+import com.google.gwt.dev.js.ast.JsBinaryOperator;
+import com.google.gwt.dev.js.ast.JsBlock;
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFunction;
 import com.google.gwt.dev.js.ast.JsInvocation;
 import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsNameRef;
 import com.google.gwt.dev.js.ast.JsNode;
+import com.google.gwt.dev.js.ast.JsScope;
 import com.google.gwt.dev.util.StringInterner;
 
 /**
@@ -79,7 +84,35 @@
     return null;
   }
 
+  public static JsExpression createAssignment(JsExpression lhs, JsExpression rhs) {
+    return new JsBinaryOperation(lhs.getSourceInfo(), JsBinaryOperator.ASG, lhs, rhs);
+  }
+
+  public static JsFunction createEmptyFunctionLiteral(SourceInfo info, JsScope scope) {
+    JsFunction func = new JsFunction(info, scope);
+    func.setBody(new JsBlock(info));
+    return func;
+  }
+
+  /**
+   * Given a string qualifier such as 'foo.bar.Baz', returns a chain of JsNameRef's representing
+   * this qualifier.
+   */
+  public static JsNameRef createQualifier(String namespace, SourceInfo sourceInfo) {
+    assert !namespace.isEmpty();
+    JsNameRef ref = null;
+    for (String part : namespace.split("\\.")) {
+      JsNameRef newRef = new JsNameRef(sourceInfo, part);
+      if (ref != null) {
+        newRef.setQualifier(ref);
+      }
+      ref = newRef;
+    }
+    return ref;
+  }
+
   private static final String CALL_STRING = StringInterner.get().intern("call");
+
   private JsUtils() {
   }
 }
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
index f3360db..cc58ae1 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/JavaClassHierarchySetupUtil.java
@@ -28,6 +28,17 @@
   private static JavaScriptObject prototypesByTypeId = JavaScriptObject.createObject();
 
   /**
+   * Defines a hidden constructor for closure using the prototype set to globalTemp (i.e. '_'). The
+   * constructor is intentionally hidden as Closure -for some unknown reason right now- having
+   * trouble dealing with an explicit one and increases code size.
+   */
+  public static native void defineHiddenClosureConstructor()/*-{
+    function F() {};
+    F.prototype = _;
+    return F;
+  }-*/;
+
+  /**
    * If not already created it creates the prototype for the class and stores it in
    * {@code prototypesByTypeId}. If superTypeId is null, it means that the class being defined
    * is the topmost class (i.e. java.lang.Object) and creates an empty prototype for it.
@@ -37,12 +48,15 @@
    * Finally adds the class literal if it was created before the call to {@code defineClass}.
    * Class literals might be created before the call to {@code defineClass} if they are in separate
    * code-split fragments. In that case Class.createFor* methods will have created a placeholder and
-   * stored in {@code prototypesByTypeId} the class literal.<p></p>
-   *
-   * As a prerequisite if superSeed is not null, it is assumed that defineClass for the supertype
+   * stored in {@code prototypesByTypeId} the class literal.
+   * <p>
+   * As a prerequisite if superTypeId is not null, it is assumed that defineClass for the supertype
    * has already been called.
+   * <p>
+   * This method has the effect of assigning the newly created prototype to the global temp variable
+   * '_'.
    */
-  public static native JavaScriptObject defineClass(JavaScriptObject typeId,
+  public static native void defineClass(JavaScriptObject typeId,
       JavaScriptObject superTypeId, JavaScriptObject castableTypeMap) /*-{
     // Setup aliases for (horribly long) JSNI references.
     var prototypesByTypeId = @com.google.gwt.lang.JavaClassHierarchySetupUtil::prototypesByTypeId;
@@ -80,7 +94,7 @@
   /**
    * Like defineClass() but second parameter is a native JS prototype reference.
    */
-  public static native JavaScriptObject defineClassWithPrototype(int typeId,
+  public static native void defineClassWithPrototype(int typeId,
       JavaScriptObject jsSuperClass, JavaScriptObject castableTypeMap) /*-{
       // Setup aliases for (horribly long) JSNI references.
       var prototypesByTypeId = @com.google.gwt.lang.JavaClassHierarchySetupUtil::prototypesByTypeId;
diff --git a/user/src/com/google/gwt/junit/JUnit.gwt.xml b/user/src/com/google/gwt/junit/JUnit.gwt.xml
index 59b5171..4bfaac9 100644
--- a/user/src/com/google/gwt/junit/JUnit.gwt.xml
+++ b/user/src/com/google/gwt/junit/JUnit.gwt.xml
@@ -43,6 +43,8 @@
 
   <!-- Override the regular symbolMaps linker to put the data somewhere we can find it -->
   <define-linker name="symbolMaps" class="com.google.gwt.junit.linker.JUnitSymbolMapsLinker" />
+  <define-linker name="closureHelpers"
+                 class="com.google.gwt.junit.linker.ClosureHelpersLinker" />
 
   <!--
     Note that only one servlet will be instantiated for both following paths which makes it
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java
index b43614a..fd5f552 100644
--- a/user/src/com/google/gwt/junit/JUnitShell.java
+++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -38,6 +38,7 @@
 import com.google.gwt.dev.jjs.JsOutputOption;
 import com.google.gwt.dev.shell.CheckForUpdates;
 import com.google.gwt.dev.shell.jetty.JettyLauncher;
+import com.google.gwt.dev.util.arg.ArgHandlerClosureFormattedOutput;
 import com.google.gwt.dev.util.arg.ArgHandlerDeployDir;
 import com.google.gwt.dev.util.arg.ArgHandlerDisableCastChecking;
 import com.google.gwt.dev.util.arg.ArgHandlerDisableClassMetadata;
@@ -290,6 +291,7 @@
       registerHandler(new ArgHandlerIncrementalCompile(options));
       registerHandler(new ArgHandlerJsInteropMode(options));
       registerHandler(new ArgHandlerSetProperties(options));
+      registerHandler(new ArgHandlerClosureFormattedOutput(options));
 
       /*
        * ----- Options specific to JUnitShell -----
@@ -1085,6 +1087,10 @@
       throw new UnableToCompleteException();
     }
 
+    if (options.isClosureCompilerFormatEnabled()) {
+      module.addLinker("closureHelpers");
+    }
+
     boolean success = false;
     try {
       success = new Compiler(options).run(getTopLogger(), module);
diff --git a/user/src/com/google/gwt/junit/linker/ClosureHelpersLinker.java b/user/src/com/google/gwt/junit/linker/ClosureHelpersLinker.java
new file mode 100644
index 0000000..b299c9c
--- /dev/null
+++ b/user/src/com/google/gwt/junit/linker/ClosureHelpersLinker.java
@@ -0,0 +1,105 @@
+/*
+ * 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.junit.linker;
+
+import com.google.gwt.core.ext.LinkerContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.TreeLogger.Type;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.AbstractLinker;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.DelegatingCompilationResult;
+import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.Shardable;
+import com.google.gwt.core.ext.linker.StatementRanges;
+import com.google.gwt.core.ext.linker.impl.StatementRangesBuilder;
+import com.google.gwt.core.linker.SymbolMapsLinker;
+import com.google.gwt.util.tools.Utility;
+
+import java.io.IOException;
+
+/**
+ * A linker which prepends the code to bootstrap closurehelpers.js into the primary fragment.
+ */
+@Shardable
+@LinkerOrder(LinkerOrder.Order.PRE)
+public class ClosureHelpersLinker extends AbstractLinker {
+
+  @Override
+  public String getDescription() {
+    return "ClosureHelpersLinker";
+  }
+
+  @Override
+  public ArtifactSet link(final TreeLogger logger, LinkerContext context, ArtifactSet artifacts,
+    boolean onePermutation) throws UnableToCompleteException {
+    if (!onePermutation) {
+      // only do work on final link
+      return artifacts;
+    }
+
+    ArtifactSet updatedArtifacts = new ArtifactSet(artifacts);
+    for (final CompilationResult compilationResult : artifacts.find(CompilationResult.class)) {
+      final String closureHelpers = getClosureHelpers(logger);
+      DelegatingCompilationResult updatedResult = new DelegatingCompilationResult(
+          ClosureHelpersLinker.class, compilationResult) {
+        String rewrittenJs[] = null;
+        StatementRanges ranges[] = null;
+
+        @Override
+        public String[] getJavaScript() {
+          if (rewrittenJs == null) {
+            rewrittenJs = compilationResult.getJavaScript().clone();
+            rewrittenJs[0] = closureHelpers + rewrittenJs[0];
+          }
+          return rewrittenJs;
+        }
+
+        @Override
+        public StatementRanges[] getStatementRanges() {
+          if (ranges == null) {
+            ranges = compilationResult.getStatementRanges().clone();
+            StatementRanges oldStmtRange = ranges[0];
+            StatementRangesBuilder builder = new StatementRangesBuilder();
+            builder.addStartPosition(0);
+            builder.addEndPosition(closureHelpers.length());
+            builder.append(oldStmtRange);
+            ranges[0] = builder.build();
+          }
+          return ranges;
+        }
+      };
+      updatedArtifacts.remove(compilationResult);
+      updatedArtifacts.add(updatedResult);
+      SymbolMapsLinker.ScriptFragmentEditsArtifact editArtifact =
+          new SymbolMapsLinker.ScriptFragmentEditsArtifact(null, 0);
+      editArtifact.prefixLines(closureHelpers);
+      updatedArtifacts.add(editArtifact);
+      break;
+    }
+    return updatedArtifacts;
+  }
+
+  private String getClosureHelpers(TreeLogger logger) throws UnableToCompleteException {
+    try {
+      return Utility.getFileFromClassPath("com/google/gwt/junit/linker/closurehelpers.js");
+    } catch (IOException e) {
+      logger.log(Type.ERROR, "Can't load closurehelpers.js", e);
+      throw new UnableToCompleteException();
+    }
+  }
+}
diff --git a/user/src/com/google/gwt/junit/linker/closurehelpers.js b/user/src/com/google/gwt/junit/linker/closurehelpers.js
new file mode 100644
index 0000000..af1d220
--- /dev/null
+++ b/user/src/com/google/gwt/junit/linker/closurehelpers.js
@@ -0,0 +1,100 @@
+// Helper functions copied from closure base.js
+
+var goog = {};
+var jsinterop = {};
+
+goog.global = this;
+goog.implicitNamespaces_ = {};
+
+goog.object = {};
+goog.object.createSet = function () {
+  var result = {};
+  for (var i = 0; i < arguments.length; i++) {
+    result[arguments[i]] = true;
+  }
+  return result;
+};
+
+goog.isProvided_ = function (name) {
+  return !goog.implicitNamespaces_[name] && !!goog.getObjectByName(name);
+};
+
+goog.getObjectByName = function (name, opt_obj) {
+  var parts = name.split('.');
+  var cur = opt_obj || goog.global;
+  for (var part; part = parts.shift();) {
+    if (cur[part] != null) {
+      cur = cur[part];
+    } else {
+      return null;
+    }
+  }
+  return cur;
+};
+
+// no-op
+goog.require = function () {
+};
+
+goog.provide = function (name) {
+  // Ensure that the same namespace isn't provided twice. This is intended
+  // to teach new developers that 'goog.provide' is effectively a variable
+  // declaration. And when JSCompiler transforms goog.provide into a real
+  // variable declaration, the compiled JS should work the same as the raw
+  // JS--even when the raw JS uses goog.provide incorrectly.
+  if (goog.isProvided_(name)) {
+    throw Error('Namespace "' + name + '" already declared.');
+  }
+  delete goog.implicitNamespaces_[name];
+
+  var namespace = name;
+  while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
+    if (goog.getObjectByName(namespace)) {
+      break;
+    }
+    goog.implicitNamespaces_[namespace] = true;
+  }
+
+  goog.exportPath_(name);
+};
+
+goog.exportPath_ = function (name, opt_object, opt_objectToExportTo) {
+  var parts = name.split('.');
+  var cur = opt_objectToExportTo || goog.global;
+
+  // Internet Explorer exhibits strange behavior when throwing errors from
+  // methods externed in this manner.  See the testExportSymbolExceptions in
+  // base_test.html for an example.
+  if (!(parts[0] in cur) && cur.execScript) {
+    cur.execScript('var ' + parts[0]);
+  }
+
+  // Certain browsers cannot parse code in the form for((a in b); c;);
+  // This pattern is produced by the JSCompiler when it collapses the
+  // statement above into the conditional loop below. To prevent this from
+  // happening, use a for-loop and reserve the init logic as below.
+
+  // Parentheses added to eliminate strict JS warning in Firefox.
+  for (var part; parts.length && (part = parts.shift());) {
+    if (!parts.length && opt_object !== undefined) {
+      // last part and we have an object; use it
+      cur[part] = opt_object;
+    } else if (cur[part]) {
+      cur = cur[part];
+    } else {
+      cur = cur[part] = {};
+    }
+  }
+};
+
+jsinterop.closure = {};
+jsinterop.closure.uniqueIds_ = {};
+jsinterop.closure.uniqueIdCounter_ = 0;
+
+jsinterop.closure.getUniqueId = function (identifier) {
+  if (!(identifier in jsinterop.closure.uniqueIds_)) {
+    var newIdent = identifier + "_" + jsinterop.closure.uniqueIdCounter_++;
+    jsinterop.closure.uniqueIds_[identifier] = newIdent;
+  }
+  return jsinterop.closure.uniqueIds_[identifier];
+};
diff --git a/user/test/com/google/gwt/core/client/interop/JsExportTest.java b/user/test/com/google/gwt/core/client/interop/JsExportTest.java
index 3251aae..3b671ea 100644
--- a/user/test/com/google/gwt/core/client/interop/JsExportTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsExportTest.java
@@ -30,27 +30,39 @@
     return "com.google.gwt.core.Core";
   }
 
+  @Override
+  public void gwtSetUp() throws Exception {
+    setupGlobal();
+  }
+
+  // $global always points to scope of exports
+  private native void setupGlobal() /*-{
+    $global = window.goog && window.goog.global || $wnd;
+    $wnd.$global = $global;
+  }-*/;
+
   public void testMethodExport() {
     // Test exported method can be called from JS in host page
-    ScriptInjector.fromString("exportedFromJava();").setWindow(TOP_WINDOW).inject();
+    ScriptInjector.fromString("$global.exportedFromJava();").setWindow(TOP_WINDOW).inject();
     assertTrue(MyClassExportsMethod.calledFromJs);
 
     MyClassExportsMethod.calledFromJs = false;
     // Test exported constructor called from JS in module window
-    ScriptInjector.fromString("$wnd.exportedFromJava();").inject();
+    ScriptInjector.fromString("$global.exportedFromJava();").inject();
     assertTrue(MyClassExportsMethod.calledFromJs);
   }
 
   public void testMethodExport_noTypeTightenParams() {
+
     // If we type-tighten, java side will see no calls and think that parameter could only be null.
     // As a result, it will be optimized to null.nullMethod().
-    ScriptInjector.fromString("$wnd.callBar($wnd.newA());").inject();
+    ScriptInjector.fromString("$global.callBar($global.newA());").inject();
     assertTrue(MyClassExportsMethod.calledFromBar);
 
     // If we type-tighten, java side will only see a call to subclass and think that parameter could
     // be optimized to that one. As a result, the method call will be inlined.
     MyClassExportsMethod.callFoo(new MyClassExportsMethod.SubclassOfA());
-    ScriptInjector.fromString("$wnd.callFoo($wnd.newA());").inject();
+    ScriptInjector.fromString("$global.callFoo($global.newA());").inject();
     assertTrue(MyClassExportsMethod.calledFromFoo);
   }
 
@@ -61,11 +73,11 @@
   }
 
   private native int onlyCalledFromJs() /*-{
-    return $wnd.onlyCalledFromJs();
+    return $global.onlyCalledFromJs();
   }-*/;
 
   public void testClinit() {
-    ScriptInjector.fromString("new $wnd.MyClassExportsMethodWithClinit();").inject();
+    ScriptInjector.fromString("new $global.MyClassExportsMethodWithClinit();").inject();
     assertEquals(23, MyClassExportsMethodWithClinit.magicNumber);
   }
 
@@ -76,15 +88,15 @@
   }
 
   private native Object getStaticInitializerStaticField1() /*-{
-    return $wnd.woo.StaticInitializerStaticField.EXPORTED_1;
+    return $global.woo.StaticInitializerStaticField.EXPORTED_1;
   }-*/;
 
   private native Object getStaticInitializerStaticField2() /*-{
-    return $wnd.woo.StaticInitializerStaticField.EXPORTED_2;
+    return $global.woo.StaticInitializerStaticField.EXPORTED_2;
   }-*/;
 
   private native Object getExportedFieldOnInterface() /*-{
-    return $wnd.woo.StaticInitializerStaticField.InterfaceWithField.STATIC;
+    return $global.woo.StaticInitializerStaticField.InterfaceWithField.STATIC;
   }-*/;
 
   public void testClinit_staticMethod() {
@@ -92,7 +104,7 @@
   }
 
   private native Object getStaticInitializerStaticMethod() /*-{
-    return $wnd.woo.StaticInitializerStaticMethod.getInstance();
+    return $global.woo.StaticInitializerStaticMethod.getInstance();
   }-*/;
 
   public void testClinit_virtualMethod() {
@@ -100,7 +112,7 @@
   }
 
   private native Object getStaticInitializerVirtualMethod() /*-{
-    var obj = new $wnd.woo.StaticInitializerVirtualMethod();
+    var obj = new $global.woo.StaticInitializerVirtualMethod();
     return obj.getInstance();
   }-*/;
 
@@ -109,7 +121,7 @@
   }
 
   private native Object createMyExportedClassWithImplicitConstructor() /*-{
-    return new $wnd.woo.MyExportedClassWithImplicitConstructor();
+    return new $global.woo.MyExportedClassWithImplicitConstructor();
   }-*/;
 
   public void testExportClass_multipleConstructors() {
@@ -118,12 +130,12 @@
   }
 
   private native int getSumByDefaultConstructor() /*-{
-    var obj = new $wnd.MyClassConstructor1();
+    var obj = new $global.MyClassConstructor1();
     return obj.sum();
   }-*/;
 
   private native int getSumByConstructor() /*-{
-    var obj = new $wnd.MyClassConstructor2(10, 20);
+    var obj = new $global.MyClassConstructor2(10, 20);
     return obj.sum();
   }-*/;
 
@@ -135,25 +147,24 @@
   }
 
   private native Object createMyExportedClassWithMultipleConstructors1() /*-{
-    return new $wnd.MyClassConstructor1();
+    return new $global.MyClassConstructor1();
   }-*/;
 
   private native Object createMyExportedClassWithMultipleConstructors2() /*-{
-    return new $wnd.MyClassConstructor2(10, 20);
+    return new $global.MyClassConstructor2(10, 20);
   }-*/;
 
   public void testExportConstructors() {
-    assertEquals(4, getFooByConstructorWithExportSymbol());
+    assertEquals(4, createMyClassExportsConstructors().foo());
     assertNull(getNotExportedConstructor());
   }
 
-  private native int getFooByConstructorWithExportSymbol() /*-{
-    var obj = new $wnd.MyClassExportsConstructors1(2);
-    return obj.foo();
+  private native MyClassExportsConstructors createMyClassExportsConstructors() /*-{
+    return new $global.MyClassExportsConstructors1(2);
   }-*/;
 
   private native Object getNotExportedConstructor() /*-{
-    return $wnd.woo.MyClassExportsConstructors;
+    return $global.woo.MyClassExportsConstructors;
   }-*/;
 
   public void testExportedField() {
@@ -165,11 +176,11 @@
   }
 
   private native int getExportedField() /*-{
-    return $wnd.woo.MyExportedClass.EXPORTED_1;
+    return $global.woo.MyExportedClass.EXPORTED_1;
   }-*/;
 
   private native void setExportedField(int a) /*-{
-    $wnd.woo.MyExportedClass.EXPORTED_1 = a;
+    $global.woo.MyExportedClass.EXPORTED_1 = a;
   }-*/;
 
   public void testExportedMethod() {
@@ -181,11 +192,11 @@
   }
 
   private native int callExportedMethod() /*-{
-    return $wnd.woo.MyExportedClass.foo();
+    return $global.woo.MyExportedClass.foo();
   }-*/;
 
   private native int setExportedMethod() /*-{
-    $wnd.woo.MyExportedClass.foo = function () {
+    $global.woo.MyExportedClass.foo = function () {
       return 1000;
     };
   }-*/;
@@ -194,21 +205,22 @@
     assertEquals(5, MyExportedClass.bar(0, 0));
     assertEquals(5, callExportedFieldByExportedMethod(0, 0));
     setExportedField2(10);
+
     assertEquals(10, getExportedField2());
     assertEquals(7, MyExportedClass.bar(1, 1));
     assertEquals(7, callExportedFieldByExportedMethod(1, 1));
   }
 
   private native int callExportedFieldByExportedMethod(int a, int b) /*-{
-    return $wnd.woo.MyExportedClass.bar(a, b);
+    return $global.woo.MyExportedClass.bar(a, b);
   }-*/;
 
   private native void setExportedField2(int a) /*-{
-    $wnd.woo.MyExportedClass.EXPORTED_2 = $wnd.newInnerClass(a);
+    $global.woo.MyExportedClass.EXPORTED_2 = $global.newInnerClass(a);
   }-*/;
 
   private native int getExportedField2() /*-{
-    return $wnd.woo.MyExportedClass.EXPORTED_2.field;
+    return $global.woo.MyExportedClass.EXPORTED_2.field;
   }-*/;
 
   public void testNoExport() {
@@ -217,16 +229,16 @@
   }
 
   private native Object getNotExportedFields() /*-{
-    return $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_1
-        || $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_2
-        || $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_3
-        || $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_4
-        || $wnd.woo.StaticInitializerStaticField.NOT_EXPORTED_5;
+    return $global.woo.StaticInitializerStaticField.NOT_EXPORTED_1
+        || $global.woo.StaticInitializerStaticField.NOT_EXPORTED_2
+        || $global.woo.StaticInitializerStaticField.NOT_EXPORTED_3
+        || $global.woo.StaticInitializerStaticField.NOT_EXPORTED_4
+        || $global.woo.StaticInitializerStaticField.NOT_EXPORTED_5;
   }-*/;
 
   private native Object getNotExportedMethods() /*-{
-    return $wnd.woo.StaticInitializerStaticMethod.notExported_1
-        || $wnd.woo.StaticInitializerStaticMethod.notExported_2;
+    return $global.woo.StaticInitializerStaticMethod.notExported_1
+        || $global.woo.StaticInitializerStaticMethod.notExported_2;
   }-*/;
 
   public static void testInheritClassNamespace() {
@@ -234,7 +246,7 @@
   }
 
   private static native int getBAR() /*-{
-    return $wnd.foo.MyExportedClassWithNamespace.BAR;
+    return $global.foo.MyExportedClassWithNamespace.BAR;
   }-*/;
 
   public static void testInheritClassNamespace_empty() {
@@ -243,11 +255,11 @@
   }
 
   private static native int getDAN() /*-{
-    return $wnd.MyClassWithEmptyNamespace.DAN;
+    return $global.MyClassWithEmptyNamespace.DAN;
   }-*/;
 
   private static native Object createNestedExportedClassWithEmptyNamespace() /*-{
-    return new $wnd.MyClassWithEmptyNamespace();
+    return new $global.MyClassWithEmptyNamespace();
   }-*/;
 
   public static void testInheritClassNamespace_noExport() {
@@ -255,7 +267,7 @@
   }
 
   private static native int getBAZ() /*-{
-    return $wnd.foobaz.MyClassWithNamespace.BAZ;
+    return $global.foobaz.MyClassWithNamespace.BAZ;
   }-*/;
 
   public static void testInheritClassNamespace_nested() {
@@ -264,11 +276,11 @@
   }
 
   private static native int getWOOZ() /*-{
-    return $wnd.zoo.InnerWithNamespace.WOOZ;
+    return $global.zoo.InnerWithNamespace.WOOZ;
   }-*/;
 
   private static native Object createNestedExportedClassWithNamespace() /*-{
-    return new $wnd.zoo.InnerWithNamespace();
+    return new $global.zoo.InnerWithNamespace();
   }-*/;
 
   public void testInheritPackageNamespace() {
@@ -276,7 +288,7 @@
   }
 
   private static native int getWOO() /*-{
-    return $wnd.woo.MyExportedClassWithPackageNamespace.WOO;
+    return $global.woo.MyExportedClassWithPackageNamespace.WOO;
   }-*/;
 
   public void testInheritPackageNamespace_nestedClass() {
@@ -285,11 +297,11 @@
   }
 
   private static native int getNestedWOO() /*-{
-    return $wnd.woo.MyClassWithNestedExportedClass.Inner.WOO;
+    return $global.woo.MyClassWithNestedExportedClass.Inner.WOO;
   }-*/;
 
   private static native Object createNestedExportedClass() /*-{
-    return new $wnd.woo.MyClassWithNestedExportedClass.Inner();
+    return new $global.woo.MyClassWithNestedExportedClass.Inner();
   }-*/;
 
   public void testInheritPackageNamespace_nestedEnum() {
@@ -297,7 +309,7 @@
   }
 
   private static native Object getNestedEnum() /*-{
-    return $wnd.woo.MyClassWithNestedExportedClass.InnerEnum.AA;
+    return $global.woo.MyClassWithNestedExportedClass.InnerEnum.AA;
   }-*/;
 
   public void testInheritPackageNamespace_subpackage() {
@@ -306,11 +318,11 @@
   }
 
   private static native Object getNestedSubpackage() /*-{
-    return $wnd.woo.subpackage;
+    return $global.woo.subpackage;
   }-*/;
 
   private static native Object getNestedSubpackageCorrect() /*-{
-    return $wnd.com.google.gwt.core.client.interop.subpackage.
+    return $global.com.google.gwt.core.client.interop.subpackage.
         MyNestedExportedClassSansPackageNamespace;
   }-*/;
 
@@ -320,11 +332,11 @@
   }
 
   private static native Object getEnumerationTEST1() /*-{
-    return $wnd.woo.MyExportedEnum.TEST1;
+    return $global.woo.MyExportedEnum.TEST1;
   }-*/;
 
   private static native Object getEnumerationTEST2() /*-{
-    return $wnd.woo.MyExportedEnum.TEST2;
+    return $global.woo.MyExportedEnum.TEST2;
   }-*/;
 
   public void testEnum_exportedMethods() {
@@ -334,15 +346,15 @@
   }
 
   private static native Object getPublicStaticMethodInEnum() /*-{
-    return $wnd.woo.MyExportedEnum.publicStaticMethod;
+    return $global.woo.MyExportedEnum.publicStaticMethod;
   }-*/;
 
   private static native Object getValuesMethodInEnum() /*-{
-    return $wnd.woo.MyExportedEnum.values;
+    return $global.woo.MyExportedEnum.values;
   }-*/;
 
   private static native Object getValueOfMethodInEnum() /*-{
-    return $wnd.woo.MyExportedEnum.valueOf;
+    return $global.woo.MyExportedEnum.valueOf;
   }-*/;
 
   public void testEnum_exportedFields() {
@@ -354,11 +366,11 @@
   }
 
   private static native int getPublicStaticFinalFieldInEnum() /*-{
-    return $wnd.woo.MyExportedEnum.publicStaticFinalField;
+    return $global.woo.MyExportedEnum.publicStaticFinalField;
   }-*/;
 
   private static native int getPublicStaticFieldInEnum() /*-{
-    return $wnd.woo.MyExportedEnum.publicStaticField;
+    return $global.woo.MyExportedEnum.publicStaticField;
   }-*/;
 
   public void testEnum_notExported() {
@@ -367,17 +379,17 @@
   }
 
   private native Object getNotExportedFieldsInEnum() /*-{
-    return $wnd.woo.MyExportedEnum.publicFinalField
-        || $wnd.woo.MyExportedEnum.privateStaticFinalField
-        || $wnd.woo.MyExportedEnum.protectedStaticFinalField
-        || $wnd.woo.MyExportedEnum.defaultStaticFinalField;
+    return $global.woo.MyExportedEnum.publicFinalField
+        || $global.woo.MyExportedEnum.privateStaticFinalField
+        || $global.woo.MyExportedEnum.protectedStaticFinalField
+        || $global.woo.MyExportedEnum.defaultStaticFinalField;
   }-*/;
 
   private native Object getNotExportedMethodsInEnum() /*-{
-    return $wnd.woo.MyExportedEnum.publicMethod
-        || $wnd.woo.MyExportedEnum.protectedStaticMethod
-        || $wnd.woo.MyExportedEnum.privateStaticMethod
-        || $wnd.woo.MyExportedEnum.defaultStaticMethod;
+    return $global.woo.MyExportedEnum.publicMethod
+        || $global.woo.MyExportedEnum.protectedStaticMethod
+        || $global.woo.MyExportedEnum.privateStaticMethod
+        || $global.woo.MyExportedEnum.defaultStaticMethod;
   }-*/;
 
   public void testEnum_subclassEnumerations() {
@@ -387,15 +399,15 @@
   }
 
   private static native Object getEnumerationA() /*-{
-    return $wnd.woo.MyEnumWithSubclassGen.A;
+    return $global.woo.MyEnumWithSubclassGen.A;
   }-*/;
 
   private static native Object getEnumerationB() /*-{
-    return $wnd.woo.MyEnumWithSubclassGen.B;
+    return $global.woo.MyEnumWithSubclassGen.B;
   }-*/;
 
   private static native Object getEnumerationC() /*-{
-    return $wnd.woo.MyEnumWithSubclassGen.C;
+    return $global.woo.MyEnumWithSubclassGen.C;
   }-*/;
 
   public void testEnum_subclassMethodCallFromExportedEnumerations() {
@@ -405,14 +417,14 @@
   }
 
   private static native int callPublicMethodFromEnumerationA() /*-{
-    return $wnd.woo.MyEnumWithSubclassGen.A.foo();
+    return $global.woo.MyEnumWithSubclassGen.A.foo();
   }-*/;
 
   private static native int callPublicMethodFromEnumerationB() /*-{
-    return $wnd.woo.MyEnumWithSubclassGen.B.foo();
+    return $global.woo.MyEnumWithSubclassGen.B.foo();
   }-*/;
 
   private static native int callPublicMethodFromEnumerationC() /*-{
-    return $wnd.woo.MyEnumWithSubclassGen.C.foo();
+    return $global.woo.MyEnumWithSubclassGen.C.foo();
   }-*/;
 }
diff --git a/user/test/com/google/gwt/core/client/interop/MyClassExportsConstructors.java b/user/test/com/google/gwt/core/client/interop/MyClassExportsConstructors.java
index 2384b65..138e091 100644
--- a/user/test/com/google/gwt/core/client/interop/MyClassExportsConstructors.java
+++ b/user/test/com/google/gwt/core/client/interop/MyClassExportsConstructors.java
@@ -16,12 +16,10 @@
 package com.google.gwt.core.client.interop;
 
 import com.google.gwt.core.client.js.JsExport;
-import com.google.gwt.core.client.js.JsType;
 
 /**
  * A test class that exhibits a variety of @JsExports on constructors.
  */
-@JsType
 public class MyClassExportsConstructors {
   private int a;
 
@@ -31,7 +29,7 @@
   }
 
   public MyClassExportsConstructors() {
-    a = 1;
+    this.a = 1;
   }
 
   public int foo() {