Assert that the repackaging tree-fixer can always be applied.

Change-Id: I8f0abbb7f5f474c53506469e54d84a02c2fdea80
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 089c747..f2e9746 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -849,6 +849,7 @@
               lens, appBuilder.build(), memberRebindingLens.getPrevious());
         }
       }
+      assert Repackaging.verifyIdentityRepackaging(appView);
 
       // Add automatic main dex classes to an eventual manual list of classes.
       if (!options.mainDexKeepRules.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 8e0800b..603e994 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -90,7 +90,7 @@
   // TODO(b/169115389): Remove
   private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of();
 
-  private Map<DexClass, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
+  private Map<DexType, DexValueString> sourceDebugExtensions = new IdentityHashMap<>();
 
   // When input has been (partially) desugared these are the classes which has been library
   // desugared. This information is populated in the IR converter.
@@ -251,11 +251,11 @@
   }
 
   public void setSourceDebugExtensionForType(DexClass clazz, DexValueString sourceDebugExtension) {
-    this.sourceDebugExtensions.put(clazz, sourceDebugExtension);
+    sourceDebugExtensions.put(clazz.type, sourceDebugExtension);
   }
 
   public DexValueString getSourceDebugExtensionForType(DexClass clazz) {
-    return this.sourceDebugExtensions.get(clazz);
+    return sourceDebugExtensions.get(clazz.type);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 0faef1b..1ace989 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -46,9 +46,6 @@
 
   public static final DexProgramClass[] EMPTY_ARRAY = {};
 
-  private static final DexEncodedArray SENTINEL_NOT_YET_COMPUTED =
-      new DexEncodedArray(DexValue.EMPTY_ARRAY);
-
   private final ProgramResource.Kind originKind;
   private final Collection<DexProgramClass> synthesizedFrom;
   private CfVersion initialClassFileVersion = null;
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 79b2e1e..120c580 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -8,11 +8,13 @@
 import static com.android.tools.r8.utils.DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR;
 import static com.android.tools.r8.utils.DescriptorUtils.INNER_CLASS_SEPARATOR;
 
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.DirectMappedDexApplication.Builder;
 import com.android.tools.r8.graph.InnerClassAttribute;
 import com.android.tools.r8.graph.ProgramPackage;
 import com.android.tools.r8.graph.ProgramPackageCollection;
@@ -23,6 +25,7 @@
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
@@ -63,6 +66,34 @@
     return lens;
   }
 
+  public static boolean verifyIdentityRepackaging(
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
+    // Running the tree fixer with an identity mapping helps ensure that the fixup of of items is
+    // complete as the rewrite replaces all items regardless of repackaging.
+    Builder builder = appView.appInfo().app().asDirect().builder();
+    RepackagingTreeFixer treeFixer =
+        new RepackagingTreeFixer(builder, appView, Collections.emptyMap());
+    assert treeFixer.verifyRun();
+    if (appView.appInfo().hasLiveness()) {
+      appView
+          .withLiveness()
+          .setAppInfo(
+              appView
+                  .withLiveness()
+                  .appInfo()
+                  .rebuildWithLiveness(
+                      appView.getSyntheticItems().commit(builder.build()), Collections.emptySet()));
+    } else {
+      appView
+          .withClassHierarchy()
+          .setAppInfo(
+              appView
+                  .appInfo()
+                  .rebuildWithClassHierarchy(appView.getSyntheticItems().commit(builder.build())));
+    }
+    return true;
+  }
+
   private RepackagingLens run(
       DirectMappedDexApplication.Builder appBuilder, ExecutorService executorService)
       throws ExecutionException {
@@ -80,7 +111,7 @@
     if (mappings.isEmpty()) {
       return null;
     }
-    return new RepackagingTreeFixer(appBuilder, appView, mappings).run();
+    return new RepackagingTreeFixer(appBuilder, appView, mappings).run(appView);
   }
 
   private void processPackagesInDesiredLocation(
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
index 17d06a3..ca82712 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -30,7 +30,7 @@
 public class RepackagingTreeFixer {
 
   private final DirectMappedDexApplication.Builder appBuilder;
-  private final AppView<AppInfoWithLiveness> appView;
+  private final AppView<?> appView;
   private final DexItemFactory dexItemFactory;
   private final RepackagingLens.Builder lensBuilder = new RepackagingLens.Builder();
   private final Map<DexType, DexType> repackagedClasses;
@@ -41,7 +41,7 @@
 
   public RepackagingTreeFixer(
       DirectMappedDexApplication.Builder appBuilder,
-      AppView<AppInfoWithLiveness> appView,
+      AppView<?> appView,
       Map<DexType, DexType> repackagedClasses) {
     this.appBuilder = appBuilder;
     this.appView = appView;
@@ -49,15 +49,24 @@
     this.repackagedClasses = repackagedClasses;
   }
 
-  public RepackagingLens run() {
+  public RepackagingLens run(AppView<AppInfoWithLiveness> appView) {
     // Globally substitute repackaged class types.
+    fixupApplication();
+    RepackagingLens lens = lensBuilder.build(appView);
+    new AnnotationFixer(lens).run(newProgramClasses.values());
+    return lens;
+  }
+
+  public boolean verifyRun() {
+    fixupApplication();
+    return true;
+  }
+
+  private void fixupApplication() {
     for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
       newProgramClasses.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
     }
     appBuilder.replaceProgramClasses(new ArrayList<>(newProgramClasses.values()));
-    RepackagingLens lens = lensBuilder.build(appView);
-    new AnnotationFixer(lens).run(newProgramClasses.values());
-    return lens;
   }
 
   private DexProgramClass fixupClass(DexProgramClass clazz) {
@@ -93,6 +102,17 @@
         fixupMethods(
             clazz.getMethodCollection().virtualMethods(),
             clazz.getMethodCollection().numberOfVirtualMethods()));
+    // Transfer properties that are not passed to the constructor.
+    if (clazz.hasClassFileVersion()) {
+      newClass.setInitialClassFileVersion(clazz.getInitialClassFileVersion());
+    }
+    if (clazz.isDeprecated()) {
+      newClass.setDeprecated();
+    }
+    if (clazz.getKotlinInfo() != null) {
+      newClass.setKotlinInfo(clazz.getKotlinInfo());
+    }
+    // If the class type changed, record the move in the lens.
     if (newClass.getType() != clazz.getType()) {
       lensBuilder.recordMove(clazz.getType(), newClass.getType());
     }
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
index 0eac295..563ba1c 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFileDeprecatedAttribute.java
@@ -8,7 +8,9 @@
 
 import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.ClassFileConsumer.ForwardingConsumer;
 import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -116,6 +118,28 @@
     }
   }
 
+  @Test
+  public void testR8() throws Exception {
+    R8FullTestBuilder builder =
+        testForR8(parameters.getBackend())
+            .addProgramClasses(TestClass.class)
+            .addKeepClassAndMembersRules(TestClass.class)
+            .addKeepAllAttributes()
+            .setMinApi(parameters.getApiLevel());
+    if (parameters.isCfRuntime()) {
+      builder.setProgramConsumer(
+          new ForwardingConsumer(null) {
+            @Override
+            public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+              checkDeprecatedAttributes(data.getBuffer());
+            }
+          });
+    }
+    builder
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
   @Deprecated
   public static class TestClass {
     @Deprecated public Object object = new Object();