Fix rewriting of array types not created at the time of minification

Bug: 165783399
Change-Id: Iea5fcb44df55af9261e86cba8a8d36170b09e015
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index cfccc2b..00dfa0b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2261,6 +2261,7 @@
         });
   }
 
+  @Deprecated
   synchronized public void forAllTypes(Consumer<DexType> f) {
     new ArrayList<>(types.values()).forEach(f);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 602a6b7..b5d50f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -500,4 +500,11 @@
     }
     return arrayDim;
   }
+
+  public DexString toArrayDescriptor(int dimensions, DexItemFactory dexItemFactory) {
+    byte[] newContent = new byte[content.length + dimensions];
+    Arrays.fill(newContent, 0, dimensions, (byte) '[');
+    System.arraycopy(content, 0, newContent, dimensions, content.length);
+    return dexItemFactory.createString(size + dimensions, newContent);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index f21e965..b1892bf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -52,6 +52,10 @@
     this.descriptor = descriptor;
   }
 
+  public DexString getDescriptor() {
+    return descriptor;
+  }
+
   @Override
   public int computeHashCode() {
     return descriptor.hashCode();
@@ -431,11 +435,7 @@
   }
 
   public DexType toArrayType(int dimensions, DexItemFactory dexItemFactory) {
-    byte[] content = new byte[descriptor.content.length + dimensions];
-    Arrays.fill(content, 0, dimensions, (byte) '[');
-    System.arraycopy(descriptor.content, 0, content, dimensions, descriptor.content.length);
-    DexString newDesc = dexItemFactory.createString(descriptor.size + dimensions, content);
-    return dexItemFactory.createType(newDesc);
+    return dexItemFactory.createType(descriptor.toArrayDescriptor(dimensions, dexItemFactory));
   }
 
   public DexType toArrayElementType(DexItemFactory dexItemFactory) {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index b276437..20c918c 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -331,9 +331,7 @@
   }
 
   /** Lookup a rebound or non-rebound method reference using the current graph lens. */
-  public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
-    return internalLookupMethod(method, context, type, result -> result);
-  }
+  public abstract MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type);
 
   protected abstract MethodLookupResult internalLookupMethod(
       DexMethod reference, DexMethod context, Type type, LookupMethodContinuation continuation);
@@ -610,6 +608,10 @@
       this.previousLens = previousLens;
     }
 
+    public final DexItemFactory dexItemFactory() {
+      return dexItemFactory;
+    }
+
     public final GraphLens getPrevious() {
       return previousLens;
     }
@@ -622,6 +624,20 @@
     }
 
     @Override
+    public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+      if (method.getHolderType().isArrayType()) {
+        assert method.getName() == dexItemFactory.cloneMethodName;
+        assert method.getReturnType() == dexItemFactory.objectType;
+        return MethodLookupResult.builder(this)
+            .setReference(method.withHolder(lookupType(method.getHolderType()), dexItemFactory))
+            .setType(type)
+            .build();
+      }
+      assert method.getHolderType().isClassType();
+      return internalLookupMethod(method, context, type, result -> result);
+    }
+
+    @Override
     public final DexType lookupType(DexType type) {
       if (type.isPrimitiveType() || type.isVoidType() || type.isNullValueType()) {
         return type;
@@ -744,6 +760,11 @@
     }
 
     @Override
+    public MethodLookupResult lookupMethod(DexMethod method, DexMethod context, Type type) {
+      return MethodLookupResult.builder(this).setReference(method).setType(type).build();
+    }
+
+    @Override
     public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
         DexMethod method) {
       return RewrittenPrototypeDescription.none();
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 6f5a765..ac21dd8 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -32,8 +32,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
 import java.util.function.Predicate;
 
 class ClassNameMinifier {
@@ -114,14 +112,11 @@
     }
   }
 
-  ClassRenaming computeRenaming(Timing timing, ExecutorService executorService)
-      throws ExecutionException {
-    return computeRenaming(timing, executorService, Collections.emptyMap());
+  ClassRenaming computeRenaming(Timing timing) {
+    return computeRenaming(timing, Collections.emptyMap());
   }
 
-  ClassRenaming computeRenaming(
-      Timing timing, ExecutorService executorService, Map<DexType, DexString> syntheticClasses)
-      throws ExecutionException {
+  ClassRenaming computeRenaming(Timing timing, Map<DexType, DexString> syntheticClasses) {
     // Externally defined synthetic classes populate an initial renaming.
     renaming.putAll(syntheticClasses);
 
@@ -159,10 +154,6 @@
     }
     timing.end();
 
-    timing.begin("rename-arrays");
-    appView.dexItemFactory().forAllTypes(this::renameArrayTypeIfNeeded);
-    timing.end();
-
     return new ClassRenaming(Collections.unmodifiableMap(renaming), getPackageRenaming());
   }
 
@@ -359,23 +350,6 @@
     return state;
   }
 
-  private void renameArrayTypeIfNeeded(DexType type) {
-    if (type.isArrayType()) {
-      DexType base = type.toBaseType(appView.dexItemFactory());
-      DexString value = renaming.get(base);
-      if (value != null) {
-        int dimensions = type.descriptor.numberOfLeadingSquareBrackets();
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < dimensions; i++) {
-          builder.append('[');
-        }
-        builder.append(value.toString());
-        DexString descriptor = appView.dexItemFactory().createString(builder.toString());
-        renaming.put(type, descriptor);
-      }
-    }
-  }
-
   protected class Namespace implements InternalNamingState {
 
     private final String packageName;
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
index 42a9287..b7bfcdb 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
 import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
 import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableMap;
@@ -27,7 +28,7 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-class MinifiedRenaming extends NamingLens {
+class MinifiedRenaming extends NonIdentityNamingLens {
 
   final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final Map<String, String> packageRenaming;
@@ -38,6 +39,7 @@
       ClassRenaming classRenaming,
       MethodRenaming methodRenaming,
       FieldRenaming fieldRenaming) {
+    super(appView.dexItemFactory());
     this.appView = appView;
     this.packageRenaming = classRenaming.packageRenaming;
     renaming.putAll(classRenaming.classRenaming);
@@ -51,7 +53,7 @@
   }
 
   @Override
-  public DexString lookupDescriptor(DexType type) {
+  protected DexString internalLookupClassDescriptor(DexType type) {
     return renaming.getOrDefault(type, type.descriptor);
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 3d27387..b886f2b 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -58,7 +58,7 @@
             new MinificationPackageNamingStrategy(appView),
             // Use deterministic class order to make sure renaming is deterministic.
             appView.appInfo().classesWithDeterministicOrder());
-    ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing, executorService);
+    ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
     timing.end();
 
     assert new MinifiedRenaming(
diff --git a/src/main/java/com/android/tools/r8/naming/NamingLens.java b/src/main/java/com/android/tools/r8/naming/NamingLens.java
index 77b5b12..9147b84 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -45,6 +45,13 @@
 
   public abstract DexString lookupDescriptor(DexType type);
 
+  public DexString lookupClassDescriptor(DexType type) {
+    assert type.isClassType();
+    return internalLookupClassDescriptor(type);
+  }
+
+  protected abstract DexString internalLookupClassDescriptor(DexType type);
+
   public abstract DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options);
 
   public abstract DexString lookupName(DexMethod method);
@@ -116,7 +123,7 @@
       return type.replaceBaseType(newBaseType, dexItemFactory);
     }
     assert type.isClassType();
-    return dexItemFactory.createType(lookupDescriptor(type));
+    return dexItemFactory.createType(lookupClassDescriptor(type));
   }
 
   public boolean hasPrefixRewritingLogic() {
@@ -179,7 +186,34 @@
     return true;
   }
 
-  private static class IdentityLens extends NamingLens {
+  public abstract static class NonIdentityNamingLens extends NamingLens {
+
+    private final DexItemFactory dexItemFactory;
+
+    protected NonIdentityNamingLens(DexItemFactory dexItemFactory) {
+      this.dexItemFactory = dexItemFactory;
+    }
+
+    protected DexItemFactory dexItemFactory() {
+      return dexItemFactory;
+    }
+
+    @Override
+    public final DexString lookupDescriptor(DexType type) {
+      if (type.isPrimitiveType() || type.isVoidType() || type.isNullValueType()) {
+        return type.getDescriptor();
+      }
+      if (type.isArrayType()) {
+        DexType baseType = type.toBaseType(dexItemFactory);
+        DexString desc = lookupDescriptor(baseType);
+        return desc.toArrayDescriptor(type.getNumberOfLeadingSquareBrackets(), dexItemFactory);
+      }
+      assert type.isClassType();
+      return lookupClassDescriptor(type);
+    }
+  }
+
+  private static final class IdentityLens extends NamingLens {
 
     private IdentityLens() {
       // Intentionally left empty.
@@ -191,6 +225,11 @@
     }
 
     @Override
+    protected DexString internalLookupClassDescriptor(DexType type) {
+      return type.descriptor;
+    }
+
+    @Override
     public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
       return attribute.getInnerName();
     }
diff --git a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
index a1451fc..e987def 100644
--- a/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/PrefixRewritingNamingLens.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.Collections;
 import java.util.HashMap;
@@ -21,7 +22,7 @@
 import java.util.stream.Stream;
 
 // Naming lens for rewriting type prefixes.
-public class PrefixRewritingNamingLens extends NamingLens {
+public class PrefixRewritingNamingLens extends NonIdentityNamingLens {
 
   final NamingLens namingLens;
   final InternalOptions options;
@@ -40,6 +41,7 @@
   }
 
   public PrefixRewritingNamingLens(NamingLens namingLens, AppView<?> appView) {
+    super(appView.dexItemFactory());
     this.appView = appView;
     this.namingLens = namingLens;
     this.options = appView.options();
@@ -68,7 +70,7 @@
   }
 
   @Override
-  public DexString lookupDescriptor(DexType type) {
+  protected DexString internalLookupClassDescriptor(DexType type) {
     DexString renaming = getRenaming(type);
     return renaming != null ? renaming : namingLens.lookupDescriptor(type);
   }
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index a29f8ef..fab1147 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -143,7 +143,7 @@
             new MinificationPackageNamingStrategy(appView),
             mappedClasses);
     ClassRenaming classRenaming =
-        classNameMinifier.computeRenaming(timing, executorService, syntheticCompanionClasses);
+        classNameMinifier.computeRenaming(timing, syntheticCompanionClasses);
     timing.end();
 
     ApplyMappingMemberNamingStrategy nameStrategy =
@@ -578,9 +578,9 @@
     }
 
     @Override
-    public DexString lookupDescriptor(DexType type) {
+    protected DexString internalLookupClassDescriptor(DexType type) {
       checkForUseOfNotMappedReference(type);
-      return super.lookupDescriptor(type);
+      return super.internalLookupClassDescriptor(type);
     }
 
     private void checkForUseOfNotMappedReference(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
index 50a6e69..09ca5bd 100644
--- a/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
+++ b/src/main/java/com/android/tools/r8/relocator/SimplePackagesRewritingMapper.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.naming.NamingLens.NonIdentityNamingLens;
 import com.android.tools.r8.references.PackageReference;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -95,23 +96,18 @@
     return new RelocatorNamingLens(typeMappings, packingMappings.build(), appView.dexItemFactory());
   }
 
-  Map<DexType, DexString> getTypeMappings() {
-    return typeMappings;
-  }
-
-  private static class RelocatorNamingLens extends NamingLens {
+  private static class RelocatorNamingLens extends NonIdentityNamingLens {
 
     private final Map<DexType, DexString> typeMappings;
     private final Map<String, String> packageMappings;
-    private final DexItemFactory factory;
 
     private RelocatorNamingLens(
         Map<DexType, DexString> typeMappings,
         Map<String, String> packageMappings,
         DexItemFactory factory) {
+      super(factory);
       this.typeMappings = typeMappings;
       this.packageMappings = packageMappings;
-      this.factory = factory;
     }
 
     @Override
@@ -120,20 +116,7 @@
     }
 
     @Override
-    public DexString lookupDescriptor(DexType type) {
-      if (type.isPrimitiveType() || type.isVoidType()) {
-        return type.descriptor;
-      }
-      if (type.isArrayType()) {
-        DexType baseType = type.toBaseType(factory);
-        if (baseType == null || baseType.isPrimitiveType()) {
-          return type.descriptor;
-        }
-        String baseDescriptor = typeMappings.getOrDefault(baseType, baseType.descriptor).toString();
-        return factory.createString(
-            DescriptorUtils.toArrayDescriptor(
-                type.getNumberOfLeadingSquareBrackets(), baseDescriptor));
-      }
+    protected DexString internalLookupClassDescriptor(DexType type) {
       return typeMappings.getOrDefault(type, type.descriptor);
     }
 
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithArrayMethodTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithArrayMethodTest.java
new file mode 100644
index 0000000..0216990
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithArrayMethodTest.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageWithArrayMethodTest extends RepackageTestBase {
+
+  public RepackageWithArrayMethodTest(
+      String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+    super(flattenPackageHierarchyOrRepackageClasses, parameters);
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .apply(this::configureRepackaging)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  private void inspect(CodeInspector inspector) {}
+
+  public static class TestClass {
+
+    public static void main(String[] args) {
+      A[] array = new A[1];
+      array[0] = new A();
+      array.clone()[0].greet();
+    }
+  }
+
+  public static class A {
+
+    @NeverInline
+    public void greet() {
+      System.out.println("Hello world!");
+    }
+  }
+}