Cleanup array usage of class literals.

Array instantiations require the leaf type's class literal ot support
getClass(). Those class literals where stored in the JNewArray ast
node assuming that array istantiations are present in the ast.

JsInterop varargs normalizations might insert new array instantations
to support the semantics.

This patch simplifies handling of class literal for array instantiations
by tying the liveness of the class literal to that of the array type;
this way the code gets simpler without any considerable impact in output
size.

Change-Id: I9fc80a89148f5b8f43c3dc731b9d49b74529744d
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
index 1bc3877..8ed0104 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
@@ -28,36 +28,27 @@
       SourceInfo info, JArrayType arrayType, List<JExpression> dimensionExpressions) {
     // Produce all class literals that will eventually get generated.
     assert dimensionExpressions != null;
-    return new JNewArray(info, arrayType, dimensionExpressions, null,
-        new JClassLiteral(info.makeChild(), arrayType.getLeafType()));
+    return new JNewArray(info, arrayType, dimensionExpressions, null);
   }
 
   public static JNewArray createArrayWithInitializers(SourceInfo info, JArrayType arrayType,
       List<JExpression> initializers) {
     assert initializers != null;
-    return new JNewArray(info, arrayType, null, initializers,
-        new JClassLiteral(info.makeChild(), arrayType.getLeafType()));
+    return new JNewArray(info, arrayType, null, initializers);
   }
 
   private final List<JExpression> dimensionExpressions;
 
   private final List<JExpression> initializers;
 
-  /**
-   * The list of class literals that will be needed to support this expression.
-   */
-  private JClassLiteral leafTypeClassLiteral;
-
   private JArrayType type;
 
   public JNewArray(SourceInfo info, JArrayType type, List<JExpression> dimensionExpressions,
-      List<JExpression> initializers, JClassLiteral leafTypeClassLiteral) {
+      List<JExpression> initializers) {
     super(info);
     this.type = type;
     this.dimensionExpressions = dimensionExpressions;
     this.initializers = initializers;
-    this.leafTypeClassLiteral = leafTypeClassLiteral;
-    assert !(leafTypeClassLiteral.getRefType().isArrayType());
   }
 
   public JArrayType getArrayType() {
@@ -72,13 +63,6 @@
     return initializers;
   }
 
-  /**
-   * Return a class literal for the leaf type of the array.
-   */
-  public JClassLiteral getLeafTypeClassLiteral() {
-    return leafTypeClassLiteral;
-  }
-
   @Override
   public JReferenceType getType() {
     return type.strengthenToNonNull().strengthenToExact();
@@ -107,8 +91,6 @@
       assert ((dimensionExpressions != null) ^ (initializers != null));
 
       visitor.accept(initializers != null ? initializers : dimensionExpressions);
-
-      leafTypeClassLiteral = (JClassLiteral) visitor.accept(leafTypeClassLiteral);
     }
     visitor.endVisit(this, ctx);
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
index 226bfb8..498d227 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
@@ -22,6 +22,7 @@
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
 import com.google.gwt.dev.jjs.ast.JCastMap;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JIntLiteral;
 import com.google.gwt.dev.jjs.ast.JLiteral;
@@ -130,7 +131,7 @@
     private JExpression initializeUnidimensionalArray(JNewArray x, JArrayType arrayType) {
       // override the type of the called method with the array's type
       SourceInfo sourceInfo = x.getSourceInfo();
-      JLiteral classLit = x.getLeafTypeClassLiteral();
+      JLiteral classLiteral = getLeafTypeClassLiteral(x);
       JExpression castableTypeMap = getOrCreateCastMap(sourceInfo, arrayType);
       JRuntimeTypeReference arrayElementRuntimeTypeReference =
           getElementRuntimeTypeReference(sourceInfo, arrayType);
@@ -140,7 +141,7 @@
       JMethodCall call =
           new JMethodCall(sourceInfo, null, initializeUnidimensionalArrayMethod);
       call.overrideReturnType(arrayType);
-      call.addArgs(classLit, castableTypeMap, arrayElementRuntimeTypeReference, dim,
+      call.addArgs(classLiteral, castableTypeMap, arrayElementRuntimeTypeReference, dim,
           elementTypeCategory, program.getLiteralInt(arrayType.getDims()));
       return call;
     }
@@ -152,7 +153,7 @@
       JsonArray elementTypeReferences = new JsonArray(sourceInfo, program.getJavaScriptObject());
       JsonArray dimList = new JsonArray(sourceInfo, program.getJavaScriptObject());
       JType currentElementType = arrayType;
-      JLiteral classLit = x.getLeafTypeClassLiteral();
+      JLiteral classLit = getLeafTypeClassLiteral(x);
       for (int i = 0; i < x.getDimensionExpressions().size(); ++i) {
         // Walk down each type from most dims to least.
         JArrayType curArrayType = (JArrayType) currentElementType;
@@ -181,7 +182,7 @@
       // override the type of the called method with the array's type
       SourceInfo sourceInfo = x.getSourceInfo();
       JExpression classLitExpression = program.createArrayClassLiteralExpression(x.getSourceInfo(),
-          x.getLeafTypeClassLiteral(), arrayType.getDims());
+          getLeafTypeClassLiteral(x), arrayType.getDims());
       JExpression castableTypeMap = getOrCreateCastMap(sourceInfo, arrayType);
       JRuntimeTypeReference elementTypeIds = getElementRuntimeTypeReference(sourceInfo, arrayType);
       JsonArray initializers =
@@ -202,6 +203,13 @@
     }
   }
 
+  private JClassLiteral getLeafTypeClassLiteral(JNewArray newArray) {
+    JType leafType = newArray.getArrayType().getLeafType();
+    JClassLiteral leafClassLiteral =  new JClassLiteral(newArray.getSourceInfo(), leafType);
+    leafClassLiteral.setField(program.getClassLiteralField(leafType));
+    return leafClassLiteral;
+  }
+
   private JArrayRef needsSetCheck(JBinaryOperation x) {
     if (x.getOp() != JBinaryOperator.ASG || !(x.getLhs() instanceof JArrayRef)) {
       return null;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
index a453256..5699666 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
@@ -217,8 +217,7 @@
   @Override
   public boolean visit(JNewArray x, Context ctx) {
     expression = new JNewArray(x.getSourceInfo(), x.getArrayType(),
-        cloneExpressions(x.getDimensionExpressions()), cloneExpressions(x.getInitializers()),
-        cloneExpression(x.getLeafTypeClassLiteral()));
+        cloneExpressions(x.getDimensionExpressions()), cloneExpressions(x.getInitializers()));
     return false;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index 1b97a34..1014218 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -577,6 +577,11 @@
     }
 
     private void maybeRescueClassLiteral(JReferenceType type) {
+      if (type.isArrayType()) {
+        JArrayType arrayType = (JArrayType) type.getUnderlyingType();
+        // Always rescue the leaf type class literal as it is needed for creating arrays.
+        rescue(program.getClassLiteralField(arrayType.getLeafType()));
+      }
       if (liveFieldsAndMethods.contains(getClassMethod) ||
           liveFieldsAndMethods.contains(getClassField)) {
         // getClass() already live so rescue class literal immediately
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
index d13ed82..6d1ddf2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowRecorder.java
@@ -15,6 +15,7 @@
 
 import com.google.gwt.dev.StringAnalyzableTypeEnvironment;
 import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
@@ -23,6 +24,8 @@
 import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JNewArray;
+import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVisitor;
@@ -67,22 +70,13 @@
 
   @Override
   public void endVisit(JClassLiteral x, Context ctx) {
-    JType type = x.getRefType();
-    if (type instanceof JDeclaredType) {
-      String typeName = type.getName();
-      stringAnalyzableTypeEnvironment.recordStaticReferenceInMethod(typeName, currentMethodName);
-      maybeRecordClinitCall(typeName);
-    }
-    // Any Enum subtype whose class literal is referenced might have its enumValueOfFunc
-    // reflectively called at runtime (see Enum.valueOf()). So to be safe the enumValueOfFunc
-    // must be assumed to be called.
-    if (type.isEnumOrSubclass() != null && !type.getName().equals("java.lang.Enum")) {
-      JMethod valueOfMethod = getValueOfMethod((JDeclaredType) type);
-      if (valueOfMethod != null) {
-        stringAnalyzableTypeEnvironment.recordMethodCallsMethod(currentMethodName,
-            computeName(valueOfMethod));
-      }
-    }
+    recordClassLiteralReferenced(x.getRefType());
+  }
+
+  @Override
+  public void endVisit(JNewArray x, Context ctx) {
+    JType type = x.getArrayType().getLeafType();
+    recordClassLiteralReferenced(type);
   }
 
   @Override
@@ -112,7 +106,6 @@
   @Override
   public boolean visit(JField x, Context ctx) {
     String typeName = x.getEnclosingType().getName();
-
     if (x.isJsInteropEntryPoint()) {
       stringAnalyzableTypeEnvironment.recordExportedStaticReferenceInType(typeName);
     }
@@ -150,6 +143,12 @@
       recordCurrentMethodInstantiatesType(x.getEnclosingType());
     }
 
+    for (JParameter parameter : x.getParams()) {
+      if (x.isJsMethodVarargs() && parameter.isVarargs()) {
+        recordClassLiteralReferenced(
+            ((JArrayType) parameter.getType().getUnderlyingType()).getLeafType());
+      }
+    }
     return true;
   }
 
@@ -179,6 +178,24 @@
     }
   }
 
+  private void recordClassLiteralReferenced(JType type) {
+    if (type instanceof JDeclaredType) {
+      String typeName = type.getName();
+      stringAnalyzableTypeEnvironment.recordStaticReferenceInMethod(typeName, currentMethodName);
+      maybeRecordClinitCall(typeName);
+    }
+    // Any Enum subtype whose class literal is referenced might have its enumValueOfFunc
+    // reflectively called at runtime (see Enum.valueOf()). So to be safe the enumValueOfFunc
+    // must be assumed to be called.
+    if (type.isEnumOrSubclass() != null && !type.getName().equals("java.lang.Enum")) {
+      JMethod valueOfMethod = getValueOfMethod((JDeclaredType) type);
+      if (valueOfMethod != null) {
+        stringAnalyzableTypeEnvironment.recordMethodCallsMethod(currentMethodName,
+            computeName(valueOfMethod));
+      }
+    }
+  }
+
   private void processJFieldRef(JFieldRef x) {
     if (x.getTarget() instanceof JField) {
       JField field = (JField) x.getTarget();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
index 11d373a..0eaae52 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
@@ -396,6 +396,11 @@
         resolveClassLiteralField(type);
       }
     }
+    // Class literals for array types are implicitly used in array creation, so they must be
+    // considered reachable.
+    for (JArrayType arrayType : program.getAllArrayTypes()) {
+      resolveClassLiteralField(arrayType.getLeafType());
+    }
     NormalizeVisitor visitor = new NormalizeVisitor();
     visitor.accept(program);
     program.recordClassLiteralFields(classLiteralFields);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementJsVarargs.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementJsVarargs.java
index 0938544..a0bd259 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementJsVarargs.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementJsVarargs.java
@@ -374,8 +374,6 @@
               new JIntLiteral(sourceInfo, varargsIndex));
       JNewArray arrayVariable = JNewArray.createArrayWithDimensionExpressions(sourceInfo,
           varargsArrayType, Collections.singletonList(lengthMinusVarargsIndex));
-      arrayVariable.getLeafTypeClassLiteral().setField(
-          program.getClassLiteralField(varargsArrayType.getLeafType()));
       preamble.addStmt(new JDeclarationStatement(
           sourceInfo, argumentsCopyVariable.createRef(sourceInfo), arrayVariable));
 
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzerTest.java
index deaa8f5..a435dfd 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzerTest.java
@@ -252,11 +252,15 @@
 
     // Returning a JSO from a JSNI method rescues.
     analyzeSnippet("Foo.create_array();").assertOnlyInstantiatedTypes(
-        "int[]", "Object");
+        "int[]", "Object",
+        // Classes rescued due to rescueing classliterals for instantiated arrays.
+        "Class", "String", "Serializable", "Comparable", "CharSequence");
 
     // Returning a JSO from a JSNI method rescues.
     analyzeSnippet("Foo.create_2d_array();").assertOnlyInstantiatedTypes(
-        "int[][]", "Object[]", "Object");
+        "int[][]", "Object[]", "Object",
+        // Classes rescued due to rescueing classliterals for instantiated arrays.
+        "Class", "String", "Serializable", "Comparable", "CharSequence");
   }
 
   private Result analyzeSnippet(String codeSnippet)
diff --git a/user/test/com/google/gwt/core/interop/JsTypeVarargsTest.java b/user/test/com/google/gwt/core/interop/JsTypeVarargsTest.java
index 9ab1c19..68322a7 100644
--- a/user/test/com/google/gwt/core/interop/JsTypeVarargsTest.java
+++ b/user/test/com/google/gwt/core/interop/JsTypeVarargsTest.java
@@ -287,6 +287,7 @@
     sideEffectCount++;
     return obj;
   }
+
   public void testVarargsCall_sideEffectingInstance() {
     SubclassNativeWithVarargsConstructor object =
         new SubclassNativeWithVarargsConstructor(0, new Object[0]);
@@ -295,4 +296,16 @@
     assertSame(object, doSideEffect(object).varargsMethod(0, params));
     assertSame(1, sideEffectCount);
   }
+
+  static class UninstantiatedClass {
+  }
+
+  @JsMethod(namespace = JsPackage.GLOBAL)
+  public static UninstantiatedClass[] varargJsMethodUninstantiatedVararg(UninstantiatedClass... varargs) {
+    return varargs;
+  }
+
+  public native void testVarargsCall_uninstantiatedVararg() /*-{
+    @GWTTestCase::assertEquals(II)(0, $global.varargJsMethodUninstantiatedVararg().length);
+  }-*/;
 }