Merge "Remove rules for services for building r8 processed r8 jars"
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
index 07cc8b5..1c59f19 100644
--- a/infra/config/global/luci-notify.cfg
+++ b/infra/config/global/luci-notify.cfg
@@ -8,7 +8,7 @@
   notifications {
     on_change: false
     on_success: false
-    on_failure: false
+    on_failure: true
     on_new_failure: true
     email {
       # Temporary, to ensure that it works.
@@ -19,177 +19,177 @@
   }
   builders {
     name: "archive"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "archive_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-4.0.4"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-4.0.4_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-4.4.4"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-4.4.4_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-5.1.1"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-5.1.1_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-6.0.1"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-6.0.1_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-7.0.0"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-android-7.0.0_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-jctf"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux-jctf_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "d8-linux_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-4.0.4"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-4.0.4_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-4.4.4"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-4.4.4_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-5.1.1"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-5.1.1_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-6.0.1"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-6.0.1_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-7.0.0"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-android-7.0.0_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-internal"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-internal_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-jctf"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "linux-jctf_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "r8cf-linux-jctf"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "windows"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
   builders {
     name: "windows_release"
-    bucket: "ci"
+    bucket: "luci.r8.ci"
     repository: "https://r8.googlesource.com/r8"
   }
 }
diff --git a/src/main/java/com/android/tools/r8/PrintClassList.java b/src/main/java/com/android/tools/r8/PrintClassList.java
index ea43a00..40cdf2e 100644
--- a/src/main/java/com/android/tools/r8/PrintClassList.java
+++ b/src/main/java/com/android/tools/r8/PrintClassList.java
@@ -76,7 +76,7 @@
       // Detour via Signature to get the same formatting.
       FieldSignature signature = new FieldSignature(field.name.toSourceString(),
           field.type.toSourceString());
-      System.out.println(field.clazz.toSourceString() + " " + signature);
+      System.out.println(field.holder.toSourceString() + " " + signature);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index b12e26f..f2d2143 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -175,13 +175,13 @@
       addType(field.type);
       DexEncodedField baseField =
           isStatic
-              ? appInfo.lookupStaticTarget(field.clazz, field)
-              : appInfo.lookupInstanceTarget(field.clazz, field);
-      if (baseField != null && baseField.field.clazz != field.clazz) {
+              ? appInfo.lookupStaticTarget(field.holder, field)
+              : appInfo.lookupInstanceTarget(field.holder, field);
+      if (baseField != null && baseField.field.holder != field.holder) {
         field = baseField.field;
       }
-      addType(field.clazz);
-      Set<DexField> typeFields = fields.get(field.clazz);
+      addType(field.holder);
+      Set<DexField> typeFields = fields.get(field.holder);
       if (typeFields != null) {
         typeFields.add(field);
       }
diff --git a/src/main/java/com/android/tools/r8/ResourceShrinker.java b/src/main/java/com/android/tools/r8/ResourceShrinker.java
index 6267db1..21e4f10 100644
--- a/src/main/java/com/android/tools/r8/ResourceShrinker.java
+++ b/src/main/java/com/android/tools/r8/ResourceShrinker.java
@@ -233,11 +233,11 @@
 
     private void processAnnotations(DexProgramClass classDef) {
       Stream<DexAnnotation> instanceFieldAnnotations =
-          Arrays.stream(classDef.instanceFields())
+          classDef.instanceFields().stream()
               .filter(DexEncodedField::hasAnnotation)
               .flatMap(f -> Arrays.stream(f.annotations.annotations));
       Stream<DexAnnotation> staticFieldAnnotations =
-          Arrays.stream(classDef.staticFields())
+          classDef.staticFields().stream()
               .filter(DexEncodedField::hasAnnotation)
               .flatMap(f -> Arrays.stream(f.annotations.annotations));
       Stream<DexAnnotation> virtualMethodAnnotations =
@@ -411,7 +411,7 @@
         throw new AssertionError("Not a get static instruction");
       }
 
-      callback.referencedStaticField(field.clazz.getInternalName(), field.name.toString());
+      callback.referencedStaticField(field.holder.getInternalName(), field.name.toString());
     }
 
     private boolean isInvokeInstruction(Instruction instruction) {
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index f35021b..1cda441 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.5.10-dev";
+  public static final String LABEL = "1.5.11-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 1f62c88..97a9aaf 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -710,7 +710,7 @@
       builder.append(mapper.originalSignatureOf(field).toString());
       return;
     }
-    appendClass(field.getHolder());
+    appendClass(field.holder);
     builder.append('/').append(field.name);
   }
 
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
index 4bf9e04..d84f551 100644
--- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -239,8 +239,8 @@
         if (argumentIndex < 0) {
           argumentType =
               code.method.isInstanceInitializer()
-                  ? new ThisInstanceInfo(instruction.asArgument(), code.method.method.getHolder())
-                  : createInitializedType(code.method.method.getHolder());
+                  ? new ThisInstanceInfo(instruction.asArgument(), code.method.method.holder)
+                  : createInitializedType(code.method.method.holder);
         } else {
           argumentType =
               createInitializedType(code.method.method.proto.parameters.values[argumentIndex]);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 350c1c9..4f05d38 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -39,7 +39,7 @@
 
   @Override
   public void write(MethodVisitor visitor, NamingLens lens) {
-    String owner = lens.lookupInternalName(field.getHolder());
+    String owner = lens.lookupInternalName(field.holder);
     String name = lens.lookupName(declaringField).toString();
     String desc = lens.lookupDescriptor(field.type).toString();
     visitor.visitFieldInsn(opcode, owner, name, desc);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index c8749fe..233fc01 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -51,7 +51,7 @@
 
   @Override
   public void write(MethodVisitor visitor, NamingLens lens) {
-    String owner = lens.lookupInternalName(method.getHolder());
+    String owner = lens.lookupInternalName(method.holder);
     String name = lens.lookupName(method).toString();
     String desc = method.proto.toDescriptorString(lens);
     visitor.visitMethodInsn(opcode, owner, name, desc, itf);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 7b2c688..ab06751 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -49,7 +50,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
+import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -227,77 +228,58 @@
       SortAnnotations sortAnnotations = new SortAnnotations();
       application.classes().forEach((clazz) -> clazz.addDependencies(sortAnnotations));
 
-      // Collect the indexed items sets for all files and perform JumboString processing.
-      // This is required to ensure that shared code blocks have a single and consistent code
-      // item that is valid for all dex files.
-      // Use a linked hash map as the order matters when addDexProgramData is called below.
-      Map<VirtualFile, Future<ObjectToOffsetMapping>> offsetMappingFutures = new LinkedHashMap<>();
-      for (VirtualFile newFile : distribute(executorService)) {
-        if (!newFile.isEmpty()) {
-          offsetMappingFutures
-              .put(newFile, executorService.submit(() -> {
-                ObjectToOffsetMapping mapping = newFile.computeMapping(application);
-                rewriteCodeWithJumboStrings(mapping, newFile.classes(), application);
-                return mapping;
-              }));
-        }
-      }
-
-      // Wait for all spawned futures to terminate to ensure jumbo string writing is complete.
-      ThreadUtils.awaitFutures(offsetMappingFutures.values());
-
       // Generate the dex file contents.
       List<Future<Boolean>> dexDataFutures = new ArrayList<>();
-      try {
-        for (VirtualFile virtualFile : offsetMappingFutures.keySet()) {
-          assert !virtualFile.isEmpty();
-          final ObjectToOffsetMapping mapping = offsetMappingFutures.get(virtualFile).get();
-          dexDataFutures.add(
-              executorService.submit(
-                  () -> {
-                    ProgramConsumer consumer;
-                    ByteBufferProvider byteBufferProvider;
-                    if (programConsumer != null) {
-                      consumer = programConsumer;
-                      byteBufferProvider = programConsumer;
-                    } else if (virtualFile.getPrimaryClassDescriptor() != null) {
-                      consumer = options.getDexFilePerClassFileConsumer();
-                      byteBufferProvider = options.getDexFilePerClassFileConsumer();
-                    } else {
-                      consumer = options.getDexIndexedConsumer();
-                      byteBufferProvider = options.getDexIndexedConsumer();
-                    }
-                    ByteBufferResult result = writeDexFile(mapping, byteBufferProvider);
-                    ByteDataView data =
-                        new ByteDataView(
-                            result.buffer.array(), result.buffer.arrayOffset(), result.length);
-                    if (consumer instanceof DexFilePerClassFileConsumer) {
-                      ((DexFilePerClassFileConsumer) consumer)
-                          .accept(
-                              virtualFile.getPrimaryClassDescriptor(),
-                              data,
-                              virtualFile.getClassDescriptors(),
-                              options.reporter);
-                    } else {
-                      ((DexIndexedConsumer) consumer)
-                          .accept(
-                              virtualFile.getId(),
-                              data,
-                              virtualFile.getClassDescriptors(),
-                              options.reporter);
-                    }
-                    // Release use of the backing buffer now that accept has returned.
-                    data.invalidate();
-                    byteBufferProvider.releaseByteBuffer(result.buffer.asByteBuffer());
-                    return true;
-                  }));
+      Iterable<VirtualFile> virtualFiles = distribute(executorService);
+      for (VirtualFile virtualFile : virtualFiles) {
+        if (virtualFile.isEmpty()) {
+          continue;
         }
-      } catch (InterruptedException e) {
-        throw new RuntimeException("Interrupted while waiting for future.", e);
+        dexDataFutures.add(
+            executorService.submit(
+                () -> {
+                  ProgramConsumer consumer;
+                  ByteBufferProvider byteBufferProvider;
+                  if (programConsumer != null) {
+                    consumer = programConsumer;
+                    byteBufferProvider = programConsumer;
+                  } else if (virtualFile.getPrimaryClassDescriptor() != null) {
+                    consumer = options.getDexFilePerClassFileConsumer();
+                    byteBufferProvider = options.getDexFilePerClassFileConsumer();
+                  } else {
+                    consumer = options.getDexIndexedConsumer();
+                    byteBufferProvider = options.getDexIndexedConsumer();
+                  }
+                  ObjectToOffsetMapping objectMapping = virtualFile.computeMapping(application);
+                  MethodToCodeObjectMapping codeMapping =
+                      rewriteCodeWithJumboStrings(
+                          objectMapping, virtualFile.classes(), application);
+                  ByteBufferResult result =
+                      writeDexFile(objectMapping, codeMapping, byteBufferProvider);
+                  ByteDataView data =
+                      new ByteDataView(
+                          result.buffer.array(), result.buffer.arrayOffset(), result.length);
+                  if (consumer instanceof DexFilePerClassFileConsumer) {
+                    ((DexFilePerClassFileConsumer) consumer)
+                        .accept(
+                            virtualFile.getPrimaryClassDescriptor(),
+                            data,
+                            virtualFile.getClassDescriptors(),
+                            options.reporter);
+                  } else {
+                    ((DexIndexedConsumer) consumer)
+                        .accept(
+                            virtualFile.getId(),
+                            data,
+                            virtualFile.getClassDescriptors(),
+                            options.reporter);
+                  }
+                  // Release use of the backing buffer now that accept has returned.
+                  data.invalidate();
+                  byteBufferProvider.releaseByteBuffer(result.buffer.asByteBuffer());
+                  return true;
+                }));
       }
-
-      // Clear out the map, as it is no longer needed.
-      offsetMappingFutures.clear();
       // Wait for all files to be processed before moving on.
       ThreadUtils.awaitFutures(dexDataFutures);
       // Fail if there are pending errors, e.g., the program consumers may have reported errors.
@@ -482,36 +464,58 @@
   }
 
   /**
-   * Rewrites the code for all methods in the given file so that they use JumboString for at
-   * least the strings that require it in mapping.
-   * <p>
-   * If run multiple times on a class, the lowest index that is required to be a JumboString will
+   * Rewrites the code for all methods in the given file so that they use JumboString for at least
+   * the strings that require it in mapping.
+   *
+   * <p>If run multiple times on a class, the lowest index that is required to be a JumboString will
    * be used.
    */
-  private void rewriteCodeWithJumboStrings(ObjectToOffsetMapping mapping,
-      Collection<DexProgramClass> classes, DexApplication application) {
+  private MethodToCodeObjectMapping rewriteCodeWithJumboStrings(
+      ObjectToOffsetMapping mapping,
+      Collection<DexProgramClass> classes,
+      DexApplication application) {
     // Do not bail out early if forcing jumbo string processing.
     if (!options.testing.forceJumboStringProcessing) {
       // If there are no strings with jumbo indices at all this is a no-op.
       if (!mapping.hasJumboStrings()) {
-        return;
+        return MethodToCodeObjectMapping.fromMethodBacking();
       }
       // If the globally highest sorting string is not a jumbo string this is also a no-op.
       if (application.highestSortingString != null &&
           application.highestSortingString.slowCompareTo(mapping.getFirstJumboString()) < 0) {
-        return;
+        return MethodToCodeObjectMapping.fromMethodBacking();
       }
     }
-    // At least one method needs a jumbo string.
+    // At least one method needs a jumbo string in which case we construct a thread local mapping
+    // for all code objects and write the processed results into that map.
+    Map<DexEncodedMethod, DexCode> codeMapping = new IdentityHashMap<>();
     for (DexProgramClass clazz : classes) {
-      clazz.forEachMethod(method -> method.rewriteCodeWithJumboStrings(
-          mapping, application, options.testing.forceJumboStringProcessing));
+      boolean isSharedSynthetic = clazz.getSynthesizedFrom().size() > 1;
+      clazz.forEachMethod(
+          method -> {
+            DexCode code =
+                method.rewriteCodeWithJumboStrings(
+                    mapping,
+                    application.dexItemFactory,
+                    options.testing.forceJumboStringProcessing);
+            codeMapping.put(method, code);
+            if (!isSharedSynthetic) {
+              // If the class is not a shared class the mapping now has ownership of the methods
+              // code object. This ensures freeing of code resources once the map entry is cleared
+              // and also ensures that we don't end up using the incorrect code pointer again later!
+              method.removeCode();
+            }
+          });
     }
+    return MethodToCodeObjectMapping.fromMapBacking(codeMapping);
   }
 
   private ByteBufferResult writeDexFile(
-      ObjectToOffsetMapping mapping, ByteBufferProvider provider) {
-    FileWriter fileWriter = new FileWriter(provider, mapping, application, options, namingLens);
+      ObjectToOffsetMapping objectMapping,
+      MethodToCodeObjectMapping codeMapping,
+      ByteBufferProvider provider) {
+    FileWriter fileWriter =
+        new FileWriter(provider, objectMapping, codeMapping, application, options, namingLens);
     // Collect the non-fixed sections.
     fileWriter.collect();
     // Generate and write the bytes.
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 4197da5..c9b8ffc 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -58,7 +58,7 @@
 import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
 import java.security.MessageDigest;
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
@@ -86,6 +86,7 @@
   }
 
   private final ObjectToOffsetMapping mapping;
+  private final MethodToCodeObjectMapping codeMapping;
   private final DexApplication application;
   private final InternalOptions options;
   private final NamingLens namingLens;
@@ -95,19 +96,21 @@
   public FileWriter(
       ByteBufferProvider provider,
       ObjectToOffsetMapping mapping,
+      MethodToCodeObjectMapping codeMapping,
       DexApplication application,
       InternalOptions options,
       NamingLens namingLens) {
     this.mapping = mapping;
+    this.codeMapping = codeMapping;
     this.application = application;
     this.options = options;
     this.namingLens = namingLens;
     this.dest = new DexOutputBuffer(provider);
-    this.mixedSectionOffsets = new MixedSectionOffsets(options);
+    this.mixedSectionOffsets = new MixedSectionOffsets(options, codeMapping);
   }
 
-  public static void writeEncodedAnnotation(DexEncodedAnnotation annotation, DexOutputBuffer dest,
-      ObjectToOffsetMapping mapping) {
+  public static void writeEncodedAnnotation(
+      DexEncodedAnnotation annotation, DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
     if (Log.ENABLED) {
       Log.verbose(FileWriter.class, "Writing encoded annotation @ %08x", dest.position());
     }
@@ -154,8 +157,11 @@
     Layout layout = Layout.from(mapping);
     layout.setCodesOffset(layout.dataSectionOffset);
 
+    // Check code objects in the code-mapping are consistent with the collected code objects.
+    assert codeMapping.verifyCodeObjects(mixedSectionOffsets.getCodes());
+
     // Sort the codes first, as their order might impact size due to alignment constraints.
-    List<DexCode> codes = sortDexCodesByClassName(mixedSectionOffsets.getCodes(), application);
+    List<DexCode> codes = sortDexCodesByClassName();
 
     // Output the debug_info_items first, as they have no dependencies.
     dest.moveTo(layout.getCodesOffset() + sizeOfCodeItems(codes));
@@ -276,35 +282,40 @@
     }
   }
 
-  private List<DexCode> sortDexCodesByClassName(Collection<DexCode> codes,
-      DexApplication application) {
+  private List<DexCode> sortDexCodesByClassName() {
     Map<DexCode, String> codeToSignatureMap = new IdentityHashMap<>();
+    List<DexCode> codesSorted = new ArrayList<>();
     for (DexProgramClass clazz : mapping.getClasses()) {
-      clazz.forEachMethod(method ->
-          addSignaturesFromMethod(method, codeToSignatureMap, application.getProguardMap()));
+      clazz.forEachMethod(
+          method -> {
+            DexCode code = codeMapping.getCode(method);
+            assert code != null || method.shouldNotHaveCode();
+            if (code != null) {
+              codesSorted.add(code);
+              addSignaturesFromMethod(
+                  method, code, codeToSignatureMap, application.getProguardMap());
+            }
+          });
     }
-    DexCode[] codesArray = codes.toArray(new DexCode[codes.size()]);
-    Arrays.sort(codesArray, Comparator.comparing(codeToSignatureMap::get));
-    return Arrays.asList(codesArray);
+    codesSorted.sort(Comparator.comparing(codeToSignatureMap::get));
+    return codesSorted;
   }
 
-  private static void addSignaturesFromMethod(DexEncodedMethod method,
+  private static void addSignaturesFromMethod(
+      DexEncodedMethod method,
+      DexCode code,
       Map<DexCode, String> codeToSignatureMap,
       ClassNameMapper proguardMap) {
-    if (!method.hasCode()) {
-      assert method.shouldNotHaveCode();
+    Signature signature;
+    String originalClassName;
+    if (proguardMap != null) {
+      signature = proguardMap.originalSignatureOf(method.method);
+      originalClassName = proguardMap.originalNameOf(method.method.holder);
     } else {
-      Signature signature;
-      String originalClassName;
-      if (proguardMap != null) {
-        signature = proguardMap.originalSignatureOf(method.method);
-        originalClassName = proguardMap.originalNameOf(method.method.holder);
-      } else {
-        signature = MethodSignature.fromDexMethod(method.method);
-        originalClassName = method.method.holder.toSourceString();
-      }
-      codeToSignatureMap.put(method.getCode().asDexCode(), originalClassName + signature);
+      signature = MethodSignature.fromDexMethod(method.method);
+      originalClassName = method.method.holder.toSourceString();
     }
+    codeToSignatureMap.put(code, originalClassName + signature);
   }
 
   private <T extends IndexedDexItem> void writeFixedSectionItems(
@@ -394,7 +405,7 @@
   }
 
   private void writeFieldItem(DexField field) {
-    int classIdx = mapping.getOffsetFor(field.clazz);
+    int classIdx = mapping.getOffsetFor(field.holder);
     assert (classIdx & 0xFFFF) == classIdx;
     dest.putShort((short) classIdx);
     int typeIdx = mapping.getOffsetFor(field.type);
@@ -561,8 +572,8 @@
         item -> mixedSectionOffsets.getOffsetFor(item.parameterAnnotationsList));
   }
 
-  private void writeEncodedFields(DexEncodedField[] fields) {
-    assert PresortedComparable.isSorted(Arrays.asList(fields));
+  private void writeEncodedFields(List<DexEncodedField> fields) {
+    assert PresortedComparable.isSorted(fields);
     int currentOffset = 0;
     for (DexEncodedField field : fields) {
       int nextOffset = mapping.getOffsetFor(field.field);
@@ -573,7 +584,7 @@
     }
   }
 
-  private void writeEncodedMethods(List<DexEncodedMethod> methods, boolean clearBodies) {
+  private void writeEncodedMethods(List<DexEncodedMethod> methods, boolean isSharedSynthetic) {
     assert PresortedComparable.isSorted(methods);
     int currentOffset = 0;
     for (DexEncodedMethod method : methods) {
@@ -582,16 +593,15 @@
       dest.putUleb128(nextOffset - currentOffset);
       currentOffset = nextOffset;
       dest.putUleb128(method.accessFlags.getAsDexAccessFlags());
-      if (!method.hasCode()) {
+      DexCode code = codeMapping.getCode(method);
+      if (code == null) {
         assert method.shouldNotHaveCode();
         dest.putUleb128(0);
       } else {
-        dest.putUleb128(mixedSectionOffsets.getOffsetFor(method.getCode().asDexCode()));
+        dest.putUleb128(mixedSectionOffsets.getOffsetFor(code));
         // Writing the methods starts to take up memory so we are going to flush the
         // code objects since they are no longer necessary after this.
-        if (clearBodies) {
-          method.removeCode();
-        }
+        codeMapping.clearCode(method, isSharedSynthetic);
       }
     }
   }
@@ -599,16 +609,15 @@
   private void writeClassData(DexProgramClass clazz) {
     assert clazz.hasMethodsOrFields();
     mixedSectionOffsets.setOffsetFor(clazz, dest.position());
-    dest.putUleb128(clazz.staticFields().length);
-    dest.putUleb128(clazz.instanceFields().length);
+    dest.putUleb128(clazz.staticFields().size());
+    dest.putUleb128(clazz.instanceFields().size());
     dest.putUleb128(clazz.directMethods().size());
     dest.putUleb128(clazz.virtualMethods().size());
     writeEncodedFields(clazz.staticFields());
     writeEncodedFields(clazz.instanceFields());
-
     boolean isSharedSynthetic = clazz.getSynthesizedFrom().size() > 1;
-    writeEncodedMethods(clazz.directMethods(), !isSharedSynthetic);
-    writeEncodedMethods(clazz.virtualMethods(), !isSharedSynthetic);
+    writeEncodedMethods(clazz.directMethods(), isSharedSynthetic);
+    writeEncodedMethods(clazz.virtualMethods(), isSharedSynthetic);
   }
 
   private void addStaticFieldValues(DexProgramClass clazz) {
@@ -1001,6 +1010,8 @@
     private static final int NOT_SET = -1;
     private static final int NOT_KNOWN = -2;
 
+    private final MethodToCodeObjectMapping codeMapping;
+
     private final Reference2IntMap<DexCode> codes = createReference2IntMap();
     private final Object2IntMap<DexDebugInfo> debugInfos = createObject2IntMap();
     private final Object2IntMap<DexTypeList> typeLists = createObject2IntMap();
@@ -1030,8 +1041,9 @@
       return result;
     }
 
-    private MixedSectionOffsets(InternalOptions options) {
+    private MixedSectionOffsets(InternalOptions options, MethodToCodeObjectMapping codeMapping) {
       this.minApiLevel = options.minApiLevel;
+      this.codeMapping = codeMapping;
     }
 
     private <T> boolean add(Object2IntMap<T> map, T item) {
@@ -1071,6 +1083,11 @@
     }
 
     @Override
+    public void visit(DexEncodedMethod method) {
+      method.collectMixedSectionItemsWithCodeMapping(this, codeMapping);
+    }
+
+    @Override
     public boolean add(DexCode code) {
       return add(codes, code);
     }
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
index 1c18107..b2a3e09 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -107,7 +107,7 @@
     this.factory = factory;
   }
 
-  public void rewrite() {
+  public DexCode rewrite() {
     // Build maps from everything in the code that uses offsets or direct addresses to reference
     // instructions to the actual instruction referenced.
     recordTargets();
@@ -133,7 +133,7 @@
     // As we have rewritten the code, we now know that its highest string index that is not
     // a jumbo-string is firstJumboString (actually the previous string, but we do not have that).
     newCode.highestSortingString = firstJumboString;
-    method.setCode(newCode);
+    return newCode;
   }
 
   private void rewriteInstructionOffsets(List<Instruction> instructions) {
diff --git a/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
new file mode 100644
index 0000000..feeb64b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/MethodToCodeObjectMapping.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2019, 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.dex;
+
+import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import java.util.Collection;
+import java.util.Map;
+
+public abstract class MethodToCodeObjectMapping {
+
+  public abstract DexCode getCode(DexEncodedMethod method);
+
+  public abstract void clearCode(DexEncodedMethod method, boolean isSharedSynthetic);
+
+  public abstract boolean verifyCodeObjects(Collection<DexCode> codes);
+
+  public static MethodToCodeObjectMapping fromMethodBacking() {
+    return MethodBacking.INSTANCE;
+  }
+
+  public static MethodToCodeObjectMapping fromMapBacking(Map<DexEncodedMethod, DexCode> map) {
+    return new MapBacking(map);
+  }
+
+  private static class MethodBacking extends MethodToCodeObjectMapping {
+
+    private static final MethodBacking INSTANCE = new MethodBacking();
+
+    @Override
+    public DexCode getCode(DexEncodedMethod method) {
+      Code code = method.getCode();
+      assert code == null || code.isDexCode();
+      return code == null ? null : code.asDexCode();
+    }
+
+    @Override
+    public void clearCode(DexEncodedMethod method, boolean isSharedSynthetic) {
+      // When using methods directly any shared class needs to maintain its methods as read-only.
+      if (!isSharedSynthetic) {
+        method.removeCode();
+      }
+    }
+
+    @Override
+    public boolean verifyCodeObjects(Collection<DexCode> codes) {
+      return true;
+    }
+  }
+
+  private static class MapBacking extends MethodToCodeObjectMapping {
+
+    private final Map<DexEncodedMethod, DexCode> codes;
+
+    public MapBacking(Map<DexEncodedMethod, DexCode> codes) {
+      this.codes = codes;
+    }
+
+    @Override
+    public DexCode getCode(DexEncodedMethod method) {
+      return codes.get(method);
+    }
+
+    @Override
+    public void clearCode(DexEncodedMethod method, boolean isSharedSynthetic) {
+      // We can safely clear the thread local pointer to even shared methods.
+      codes.put(method, null);
+    }
+
+    @Override
+    public boolean verifyCodeObjects(Collection<DexCode> codes) {
+      assert this.codes.values().containsAll(codes);
+      return true;
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
index dee12fc..2cedf5c 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexDebugInfo;
 import com.android.tools.r8.graph.DexEncodedArray;
+import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexTypeList;
@@ -53,6 +54,16 @@
   public abstract boolean add(DexAnnotationSet dexAnnotationSet);
 
   /**
+   * Recurse on the given encoded method to add items to the collection.
+   *
+   * <p>Allows overriding the behavior when dex-file writing.
+   */
+  public void visit(DexEncodedMethod dexEncodedMethod) {
+    dexEncodedMethod.collectMixedSectionItemsWithCodeMapping(
+        this, MethodToCodeObjectMapping.fromMethodBacking());
+  }
+
+  /**
    * Adds the given code item to the collection.
    *
    * Does not add any dependencies.
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index a01f840..1754ad9 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -110,7 +110,7 @@
 
   @Override
   public DexEncodedMethod definitionFor(DexMethod method) {
-    DexType holderType = method.getHolder();
+    DexType holderType = method.holder;
     DexEncodedMethod cached = (DexEncodedMethod) getDefinitions(holderType).get(method);
     if (cached != null && cached.isObsolete()) {
       definitions.remove(holderType);
@@ -121,7 +121,7 @@
 
   @Override
   public DexEncodedField definitionFor(DexField field) {
-    return (DexEncodedField) getDefinitions(field.getHolder()).get(field);
+    return (DexEncodedField) getDefinitions(field.holder).get(field);
   }
 
   private Map<Descriptor<?,?>, KeyedDexItem<?>> getDefinitions(DexType type) {
@@ -441,7 +441,7 @@
    * #resolveFieldOn}.
    */
   public DexEncodedField resolveField(DexField field) {
-    return resolveFieldOn(field.clazz, field);
+    return resolveFieldOn(field.holder, field);
   }
 
   /**
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 eee4acd..6448896 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -108,70 +108,8 @@
   }
 
   public AppView<AppInfoWithLiveness> withLiveness() {
-    return new AppViewWithLiveness();
-  }
-
-  private class AppViewWithLiveness extends AppView<AppInfoWithLiveness> {
-
-    private AppViewWithLiveness() {
-      super(null, null, null);
-    }
-
-    @Override
-    public AppInfoWithLiveness appInfo() {
-      return AppView.this.appInfo().withLiveness();
-    }
-
-    @Override
-    public void setAppInfo(AppInfoWithLiveness appInfoWithLiveness) {
-      @SuppressWarnings("unchecked")
-      T appInfo = (T) appInfoWithLiveness;
-      AppView.this.setAppInfo(appInfo);
-    }
-
-    @Override
-    public AppServices appServices() {
-      return AppView.this.appServices();
-    }
-
-    @Override
-    public void setAppServices(AppServices appServices) {
-      AppView.this.setAppServices(appServices);
-    }
-
-    @Override
-    public DexItemFactory dexItemFactory() {
-      return AppView.this.dexItemFactory();
-    }
-
-    @Override
-    public boolean enableWholeProgramOptimizations() {
-      return AppView.this.enableWholeProgramOptimizations();
-    }
-
-    @Override
-    public GraphLense graphLense() {
-      return AppView.this.graphLense();
-    }
-
-    @Override
-    public void setGraphLense(GraphLense graphLense) {
-      AppView.this.setGraphLense(graphLense);
-    }
-
-    @Override
-    public InternalOptions options() {
-      return AppView.this.options();
-    }
-
-    @Override
-    public VerticallyMergedClasses verticallyMergedClasses() {
-      return AppView.this.verticallyMergedClasses();
-    }
-
-    @Override
-    public AppView<AppInfoWithLiveness> withLiveness() {
-      return this;
-    }
+    @SuppressWarnings("unchecked")
+    AppView<AppInfoWithLiveness> appViewWithLiveness = (AppView<AppInfoWithLiveness>) this;
+    return appViewWithLiveness;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/Descriptor.java b/src/main/java/com/android/tools/r8/graph/Descriptor.java
index 7691bfa..09bef34 100644
--- a/src/main/java/com/android/tools/r8/graph/Descriptor.java
+++ b/src/main/java/com/android/tools/r8/graph/Descriptor.java
@@ -8,8 +8,6 @@
 
   public abstract boolean match(T entry);
 
-  public abstract DexType getHolder();
-
   @Override
   public boolean isDescriptor() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index 1ef2f68..1f2d9f1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.OrderedMergingIterator;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.function.Function;
 
@@ -38,11 +37,10 @@
         parameterAnnotations.add(method);
       }
     }
-    assert isSorted(Arrays.asList(clazz.staticFields()));
-    assert isSorted(Arrays.asList(clazz.instanceFields()));
+    assert isSorted(clazz.staticFields());
+    assert isSorted(clazz.instanceFields());
     OrderedMergingIterator<DexEncodedField, DexField> fields =
-        new OrderedMergingIterator<>(
-            Arrays.asList(clazz.staticFields()), Arrays.asList(clazz.instanceFields()));
+        new OrderedMergingIterator<>(clazz.staticFields(), clazz.instanceFields());
     fieldAnnotations = new ArrayList<>();
     while (fields.hasNext()) {
       DexEncodedField field = fields.next();
@@ -68,10 +66,9 @@
     return fieldAnnotations;
   }
 
-
   /**
-   * DexAnnotationDirectory of a class can be canonicalized only if a clazz has annotations and
-   * does not contains annotations for his fields, methods or parameters. Indeed, if a field, method
+   * DexAnnotationDirectory of a class can be canonicalized only if a class has annotations and
+   * does not contains annotations for its fields, methods or parameters. Indeed, if a field, method
    * or parameter has annotations in this case, the DexAnnotationDirectory can not be shared since
    * it will contains information about field, method and parameters that are only related to only
    * one class.
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index a9f1ea4..382f4bb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -205,7 +205,7 @@
     }
 
     private void write(DexField field) throws IOException {
-      write(field.clazz);
+      write(field.holder);
       write(field.type);
       write(field.name);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index f303c74..0b104f5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -23,8 +23,11 @@
 
 public abstract class DexClass extends DexDefinition {
 
-  public interface MethodSetter {
+  public interface FieldSetter {
+    void setField(int index, DexEncodedField field);
+  }
 
+  public interface MethodSetter {
     void setMethod(int index, DexEncodedMethod method);
   }
 
@@ -171,8 +174,8 @@
     assert verifyNoDuplicateMethods();
   }
 
-  public void setDirectMethods(DexEncodedMethod[] values) {
-    directMethods = MoreObjects.firstNonNull(values, NO_METHODS);
+  public void setDirectMethods(DexEncodedMethod[] methods) {
+    directMethods = MoreObjects.firstNonNull(methods, NO_METHODS);
     assert verifyCorrectnessOfMethodHolders(directMethods());
     assert verifyNoDuplicateMethods();
   }
@@ -221,8 +224,8 @@
     assert verifyNoDuplicateMethods();
   }
 
-  public void setVirtualMethods(DexEncodedMethod[] values) {
-    virtualMethods = MoreObjects.firstNonNull(values, NO_METHODS);
+  public void setVirtualMethods(DexEncodedMethod[] methods) {
+    virtualMethods = MoreObjects.firstNonNull(methods, NO_METHODS);
     assert verifyCorrectnessOfMethodHolders(virtualMethods());
     assert verifyNoDuplicateMethods();
   }
@@ -340,12 +343,53 @@
     }
   }
 
-  public DexEncodedField[] staticFields() {
-    return staticFields;
+  public List<DexEncodedField> staticFields() {
+    assert staticFields != null;
+    if (InternalOptions.assertionsEnabled()) {
+      return Collections.unmodifiableList(Arrays.asList(staticFields));
+    }
+    return Arrays.asList(staticFields);
   }
 
-  public void setStaticFields(DexEncodedField[] values) {
-    staticFields = MoreObjects.firstNonNull(values, NO_FIELDS);
+  public void appendStaticField(DexEncodedField field) {
+    DexEncodedField[] newFields = new DexEncodedField[staticFields.length + 1];
+    System.arraycopy(staticFields, 0, newFields, 0, staticFields.length);
+    newFields[staticFields.length] = field;
+    staticFields = newFields;
+    assert verifyCorrectnessOfFieldHolder(field);
+    assert verifyNoDuplicateFields();
+  }
+
+  public void appendStaticFields(Collection<DexEncodedField> fields) {
+    DexEncodedField[] newFields = new DexEncodedField[staticFields.length + fields.size()];
+    System.arraycopy(staticFields, 0, newFields, 0, staticFields.length);
+    int i = staticFields.length;
+    for (DexEncodedField field : fields) {
+      newFields[i] = field;
+      i++;
+    }
+    staticFields = newFields;
+    assert verifyCorrectnessOfFieldHolders(fields);
+    assert verifyNoDuplicateFields();
+  }
+
+  public void removeStaticField(int index) {
+    DexEncodedField[] newFields = new DexEncodedField[staticFields.length - 1];
+    System.arraycopy(staticFields, 0, newFields, 0, index);
+    System.arraycopy(staticFields, index + 1, newFields, index, staticFields.length - index - 1);
+    staticFields = newFields;
+  }
+
+  public void setStaticField(int index, DexEncodedField field) {
+    staticFields[index] = field;
+    assert verifyCorrectnessOfFieldHolder(field);
+    assert verifyNoDuplicateFields();
+  }
+
+  public void setStaticFields(DexEncodedField[] fields) {
+    staticFields = MoreObjects.firstNonNull(fields, NO_FIELDS);
+    assert verifyCorrectnessOfFieldHolders(staticFields());
+    assert verifyNoDuplicateFields();
   }
 
   public boolean definesStaticField(DexField field) {
@@ -357,12 +401,80 @@
     return false;
   }
 
-  public DexEncodedField[] instanceFields() {
-    return instanceFields;
+  public List<DexEncodedField> instanceFields() {
+    assert instanceFields != null;
+    if (InternalOptions.assertionsEnabled()) {
+      return Collections.unmodifiableList(Arrays.asList(instanceFields));
+    }
+    return Arrays.asList(instanceFields);
   }
 
-  public void setInstanceFields(DexEncodedField[] values) {
-    instanceFields = MoreObjects.firstNonNull(values, NO_FIELDS);
+  public void appendInstanceField(DexEncodedField field) {
+    DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + 1];
+    System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length);
+    newFields[instanceFields.length] = field;
+    instanceFields = newFields;
+    assert verifyCorrectnessOfFieldHolder(field);
+    assert verifyNoDuplicateFields();
+  }
+
+  public void appendInstanceFields(Collection<DexEncodedField> fields) {
+    DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + fields.size()];
+    System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length);
+    int i = instanceFields.length;
+    for (DexEncodedField field : fields) {
+      newFields[i] = field;
+      i++;
+    }
+    instanceFields = newFields;
+    assert verifyCorrectnessOfFieldHolders(fields);
+    assert verifyNoDuplicateFields();
+  }
+
+  public void removeInstanceField(int index) {
+    DexEncodedField[] newFields = new DexEncodedField[instanceFields.length - 1];
+    System.arraycopy(instanceFields, 0, newFields, 0, index);
+    System.arraycopy(
+        instanceFields, index + 1, newFields, index, instanceFields.length - index - 1);
+    instanceFields = newFields;
+  }
+
+  public void setInstanceField(int index, DexEncodedField field) {
+    instanceFields[index] = field;
+    assert verifyCorrectnessOfFieldHolder(field);
+    assert verifyNoDuplicateFields();
+  }
+
+  public void setInstanceFields(DexEncodedField[] fields) {
+    instanceFields = MoreObjects.firstNonNull(fields, NO_FIELDS);
+    assert verifyCorrectnessOfFieldHolders(instanceFields());
+    assert verifyNoDuplicateFields();
+  }
+
+  private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
+    assert field.field.holder == type
+        : "Expected field `"
+            + field.field.toSourceString()
+            + "` to have holder `"
+            + type.toSourceString()
+            + "`";
+    return true;
+  }
+
+  private boolean verifyCorrectnessOfFieldHolders(Iterable<DexEncodedField> fields) {
+    for (DexEncodedField field : fields) {
+      assert verifyCorrectnessOfFieldHolder(field);
+    }
+    return true;
+  }
+
+  private boolean verifyNoDuplicateFields() {
+    Set<DexField> unique = Sets.newIdentityHashSet();
+    for (DexEncodedField field : fields()) {
+      boolean changed = unique.add(field.field);
+      assert changed : "Duplicate field `" + field.field.toSourceString() + "`";
+    }
+    return true;
   }
 
   public DexEncodedField[] allFieldsSorted() {
@@ -380,14 +492,14 @@
    * Find static field in this class matching field
    */
   public DexEncodedField lookupStaticField(DexField field) {
-    return lookupTarget(staticFields(), field);
+    return lookupTarget(staticFields, field);
   }
 
   /**
    * Find instance field in this class matching field.
    */
   public DexEncodedField lookupInstanceField(DexField field) {
-    return lookupTarget(instanceFields(), field);
+    return lookupTarget(instanceFields, field);
   }
 
   /**
@@ -598,7 +710,7 @@
   }
 
   public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
-    return Arrays.stream(staticFields())
+    return staticFields().stream()
         .anyMatch(field -> field.getStaticValue().mayHaveSideEffects());
   }
 
@@ -669,6 +781,8 @@
   public boolean isValid() {
     assert !isInterface()
         || Arrays.stream(virtualMethods).noneMatch(method -> method.accessFlags.isFinal());
+    assert verifyCorrectnessOfFieldHolders(fields());
+    assert verifyNoDuplicateFields();
     assert verifyCorrectnessOfMethodHolders(methods());
     assert verifyNoDuplicateMethods();
     return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index eca2384..49611c6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -73,7 +73,7 @@
     int argumentRegister = code.registerSize - code.incomingRegisterSize;
     if (!method.accessFlags.isStatic()) {
       DexString name = factory.thisName;
-      DexType type = method.method.getHolder();
+      DexType type = method.method.holder;
       startArgument(argumentRegister, name, type);
       argumentRegister += ValueType.fromDexType(type).requiredRegisters();
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 58fe234..9c7b10a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -31,8 +31,8 @@
   }
 
   public boolean isProgramField(AppInfo appInfo) {
-    if (field.clazz.isClassType()) {
-      DexClass clazz = appInfo.definitionFor(field.clazz);
+    if (field.holder.isClassType()) {
+      DexClass clazz = appInfo.definitionFor(field.holder);
       return clazz != null && clazz.isProgramClass();
     }
     return false;
@@ -130,7 +130,7 @@
       return null;
     }
     if (accessFlags.isStatic()) {
-      DexClass clazz = appInfo.definitionFor(field.clazz);
+      DexClass clazz = appInfo.definitionFor(field.holder);
       assert clazz != null : "Class for the field must be present";
       return getStaticValue().asConstInstruction(clazz.hasClassInitializer(), dest, options);
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 9fa16f1..057090e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.JumboStringRewriter;
+import com.android.tools.r8.dex.MethodToCodeObjectMapping;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
@@ -255,7 +256,7 @@
   public boolean isInliningCandidate(
       DexEncodedMethod container, Reason inliningReason, AppInfoWithSubtyping appInfo) {
     checkIfObsolete();
-    return isInliningCandidate(container.method.getHolder(), inliningReason, appInfo);
+    return isInliningCandidate(container.method.holder, inliningReason, appInfo);
   }
 
   public boolean isInliningCandidate(
@@ -279,11 +280,11 @@
       case PROCESSED_INLINING_CANDIDATE_ANY:
         return true;
       case PROCESSED_INLINING_CANDIDATE_SUBCLASS:
-        return containerType.isSubtypeOf(method.getHolder(), appInfo);
+        return containerType.isSubtypeOf(method.holder, appInfo);
       case PROCESSED_INLINING_CANDIDATE_SAME_PACKAGE:
-        return containerType.isSamePackage(method.getHolder());
+        return containerType.isSamePackage(method.holder);
       case PROCESSED_INLINING_CANDIDATE_SAME_CLASS:
-        return containerType == method.getHolder();
+        return containerType == method.holder;
       default:
         return false;
     }
@@ -366,6 +367,12 @@
 
   @Override
   void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    mixedItems.visit(this);
+  }
+
+  public void collectMixedSectionItemsWithCodeMapping(
+      MixedSectionCollection mixedItems, MethodToCodeObjectMapping mapping) {
+    DexCode code = mapping.getCode(this);
     if (code != null) {
       code.collectMixedSectionItems(mixedItems);
     }
@@ -678,19 +685,13 @@
     return method;
   }
 
-  /**
-   * Rewrites the code in this method to have JumboString bytecode if required by mapping.
-   * <p>
-   * Synchronized such that it can be called concurrently for different mappings. As a side-effect,
-   * this will also update the highestSortingString to the index of the strings up to which the code
-   * was rewritten to avoid rewriting again unless needed.
-   */
-  public synchronized void rewriteCodeWithJumboStrings(
-      ObjectToOffsetMapping mapping, DexApplication application, boolean force) {
+  /** Rewrites the code in this method to have JumboString bytecode if required by mapping. */
+  public DexCode rewriteCodeWithJumboStrings(
+      ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force) {
     checkIfObsolete();
     assert code == null || code.isDexCode();
     if (code == null) {
-      return;
+      return null;
     }
     DexCode code = this.code.asDexCode();
     DexString firstJumboString = null;
@@ -706,10 +707,10 @@
       }
     }
     if (firstJumboString != null) {
-      JumboStringRewriter rewriter =
-          new JumboStringRewriter(this, firstJumboString, application.dexItemFactory);
-      rewriter.rewrite();
+      JumboStringRewriter rewriter = new JumboStringRewriter(this, firstJumboString, factory);
+      return rewriter.rewrite();
     }
+    return code;
   }
 
   public String codeToString() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 550a91f..1f106b7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -10,12 +10,12 @@
 public class DexField extends Descriptor<DexEncodedField, DexField> implements
     PresortedComparable<DexField> {
 
-  public final DexType clazz;
+  public final DexType holder;
   public final DexType type;
   public final DexString name;
 
-  DexField(DexType clazz, DexType type, DexString name, boolean skipNameValidationForTesting) {
-    this.clazz = clazz;
+  DexField(DexType holder, DexType type, DexString name, boolean skipNameValidationForTesting) {
+    this.holder = holder;
     this.type = type;
     this.name = name;
     if (!skipNameValidationForTesting && !name.isValidFieldName()) {
@@ -26,7 +26,7 @@
 
   @Override
   public int computeHashCode() {
-    return clazz.hashCode()
+    return holder.hashCode()
         + type.hashCode() * 7
         + name.hashCode() * 31;
   }
@@ -35,7 +35,7 @@
   public boolean computeEquals(Object other) {
     if (other instanceof DexField) {
       DexField o = (DexField) other;
-      return clazz.equals(o.clazz)
+      return holder.equals(o.holder)
           && type.equals(o.type)
           && name.equals(o.name);
     }
@@ -44,14 +44,14 @@
 
   @Override
   public String toString() {
-    return "Field " + type + " " + clazz + "." + name;
+    return "Field " + type + " " + holder + "." + name;
   }
 
   @Override
   public void collectIndexedItems(IndexedItemCollection indexedItems,
       DexMethod method, int instructionOffset) {
     if (indexedItems.addField(this)) {
-      clazz.collectIndexedItems(indexedItems, method, instructionOffset);
+      holder.collectIndexedItems(indexedItems, method, instructionOffset);
       type.collectIndexedItems(indexedItems, method, instructionOffset);
       indexedItems.getRenamedName(this).collectIndexedItems(
           indexedItems, method, instructionOffset);
@@ -80,7 +80,7 @@
 
   @Override
   public int slowCompareTo(DexField other) {
-    int result = clazz.slowCompareTo(other.clazz);
+    int result = holder.slowCompareTo(other.holder);
     if (result != 0) {
       return result;
     }
@@ -93,7 +93,7 @@
 
   @Override
   public int slowCompareTo(DexField other, NamingLens namingLens) {
-    int result = clazz.slowCompareTo(other.clazz, namingLens);
+    int result = holder.slowCompareTo(other.holder, namingLens);
     if (result != 0) {
       return result;
     }
@@ -106,7 +106,7 @@
 
   @Override
   public int layeredCompareTo(DexField other, NamingLens namingLens) {
-    int result = clazz.compareTo(other.clazz);
+    int result = holder.compareTo(other.holder);
     if (result != 0) {
       return result;
     }
@@ -122,22 +122,17 @@
     return entry.field.name == name && entry.field.type == type;
   }
 
-  @Override
-  public DexType getHolder() {
-    return clazz;
-  }
-
   public String qualifiedName() {
-    return clazz + "." + name;
+    return holder + "." + name;
   }
 
   @Override
   public String toSmaliString() {
-    return clazz.toSmaliString() + "->" + name + ":" + type.toSmaliString();
+    return holder.toSmaliString() + "->" + name + ":" + type.toSmaliString();
   }
 
   @Override
   public String toSourceString() {
-    return type.toSourceString() + " " + clazz.toSourceString() + "." + name.toSourceString();
+    return type.toSourceString() + " " + holder.toSourceString() + "." + name.toSourceString();
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index bd86c12..964735a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -152,11 +152,6 @@
     return entry.method.name == name && entry.method.proto == proto;
   }
 
-  @Override
-  public DexType getHolder() {
-    return holder;
-  }
-
   public String qualifiedName() {
     return holder + "." + name;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index 3af0a41..8d7b98a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -374,10 +374,10 @@
     } else {
       assert isFieldHandle();
       DexField field = asField();
-      owner = lens.lookupInternalName(field.clazz);
+      owner = lens.lookupInternalName(field.holder);
       name = lens.lookupName(field).toString();
       desc = lens.lookupDescriptor(field.type).toString();
-      itf = field.clazz.isInterface();
+      itf = field.holder.isInterface();
     }
     return new Handle(getAsmTag(), owner, name, desc, itf);
   }
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 57b08fb..4312bfd 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -225,7 +225,7 @@
   }
 
   public boolean hasMethodsOrFields() {
-    int numberOfFields = staticFields().length + instanceFields().length;
+    int numberOfFields = staticFields().size() + instanceFields().size();
     int numberOfMethods = directMethods().size() + virtualMethods().size();
     return numberOfFields + numberOfMethods > 0;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java b/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
index 57bc3c4..0fe8c04 100644
--- a/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
@@ -35,7 +35,7 @@
   public void write(ClassWriter writer, NamingLens lens) {
     if (enclosingMethod != null) {
       writer.visitOuterClass(
-          lens.lookupInternalName(enclosingMethod.getHolder()),
+          lens.lookupInternalName(enclosingMethod.holder),
           lens.lookupName(enclosingMethod).toString(),
           enclosingMethod.proto.toDescriptorString(lens));
     } else {
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 732f35b..33d145f 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -192,7 +192,7 @@
     assert node.localVariables.isEmpty() || keepLocals(encodedMethod, appView.options());
     JarSourceCode source =
         new JarSourceCode(
-            method.getHolder(),
+            method.holder,
             node,
             application,
             appView.graphLense().getOriginalMethodSignature(encodedMethod.method),
@@ -205,7 +205,7 @@
   public void registerCodeReferences(UseRegistry registry) {
     triggerDelayedParsingIfNeccessary();
     node.instructions.accept(
-        new JarRegisterEffectsVisitor(method.getHolder(), registry, application));
+        new JarRegisterEffectsVisitor(method.holder, registry, application));
     for (TryCatchBlockNode tryCatchBlockNode : node.tryCatchBlocks) {
       // Exception type can be null for "catch all" used for try/finally.
       if (tryCatchBlockNode.type != null) {
@@ -223,7 +223,7 @@
 
   public ConstraintWithTarget computeInliningConstraint(
       DexEncodedMethod encodedMethod,
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       GraphLense graphLense,
       DexType invocationContext) {
     InliningConstraintVisitor visitor =
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index ccf790b..1d0b4fb 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -78,7 +78,7 @@
         break;
       case INVOKE_CONSTRUCTOR:
         DexMethod method = methodHandle.asMethod();
-        registerNewInstance(method.getHolder());
+        registerNewInstance(method.holder);
         registerInvokeDirect(method);
         break;
       case INVOKE_INTERFACE:
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
index 120a099..757dcac 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ClassInitializationAnalysis.java
@@ -59,7 +59,7 @@
         }
       };
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final IRCode code;
   private final DexItemFactory dexItemFactory;
 
@@ -72,7 +72,7 @@
     this.dexItemFactory = null;
   }
 
-  public ClassInitializationAnalysis(AppView<? extends AppInfoWithLiveness> appView, IRCode code) {
+  public ClassInitializationAnalysis(AppView<AppInfoWithLiveness> appView, IRCode code) {
     this.appView = appView;
     this.code = code;
     this.dexItemFactory = appView.dexItemFactory();
@@ -276,7 +276,7 @@
           return false;
         }
       }
-      return isTypeInitializedBy(type, instruction.getField().clazz, appView, mode);
+      return isTypeInitializedBy(type, instruction.getField().holder, appView, mode);
     }
 
     public static boolean forInvokeDirect(
@@ -407,7 +407,7 @@
         // Class initialization may fail with ExceptionInInitializerError.
         return false;
       }
-      return isTypeInitializedBy(type, instruction.getField().clazz, appView, mode);
+      return isTypeInitializedBy(type, instruction.getField().holder, appView, mode);
     }
 
     private static boolean isTypeInitializedBy(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index e5ea8b7..719f954 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -171,7 +171,7 @@
 
   public static DexType getRefinedReceiverType(
       DexDefinitionSupplier definitions, InvokeMethodWithReceiver invoke) {
-    DexType receiverType = invoke.getInvokedMethod().getHolder();
+    DexType receiverType = invoke.getInvokedMethod().holder;
     TypeLatticeElement lattice = invoke.getReceiver().getTypeLattice();
     if (lattice.isClassType()) {
       DexType refinedType = lattice.asClassTypeLatticeElement().getClassType();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 3634edf..c78cd20 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -118,8 +118,8 @@
     }
     if (!isMemberVisibleFromOriginalContext(
         appInfo,
-        code.method.method.getHolder(),
-        resolvedField.field.clazz,
+        code.method.method.holder,
+        resolvedField.field.holder,
         resolvedField.accessFlags)) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index 6358853..15623e0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -111,13 +111,13 @@
     }
     if (!isMemberVisibleFromOriginalContext(
         appInfo,
-        code.method.method.getHolder(),
-        resolvedField.field.clazz,
+        code.method.method.holder,
+        resolvedField.field.holder,
         resolvedField.accessFlags)) {
       return false;
     }
     DexType context = code.method.method.holder;
-    return !getField().clazz.classInitializationMayHaveSideEffects(
+    return !getField().holder.classInitializationMayHaveSideEffects(
         appInfo,
         // Types that are a super type of `context` are guaranteed to be initialized already.
         type -> context.isSubtypeOf(type, appInfo));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index ab1e319..f8465d0 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -150,7 +150,7 @@
 
   public static CallGraph build(
       DexApplication application,
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       InternalOptions options,
       Timing timing) {
     CallGraph graph = new CallGraph(options);
@@ -488,7 +488,7 @@
     private final Node caller;
     private final CallGraph graph;
 
-    InvokeExtractor(AppView<? extends AppInfoWithLiveness> appView, Node caller, CallGraph graph) {
+    InvokeExtractor(AppView<AppInfoWithLiveness> appView, Node caller, CallGraph graph) {
       super(appView.dexItemFactory());
       this.appInfo = appView.appInfo();
       this.graphLense = appView.graphLense();
@@ -518,7 +518,7 @@
 
     private void addPossibleTarget(DexEncodedMethod possibleTarget) {
       DexClass possibleTargetClass =
-          appInfo.definitionFor(possibleTarget.method.getHolder());
+          appInfo.definitionFor(possibleTarget.method.holder);
       if (possibleTargetClass != null && !possibleTargetClass.isLibraryClass()) {
         addTarget(possibleTarget);
       }
@@ -541,7 +541,7 @@
       DexEncodedMethod definition = appInfo.lookup(type, method, source.method.holder);
       if (definition != null) {
         assert !source.accessFlags.isBridge() || definition != caller.method;
-        DexClass clazz = appInfo.definitionFor(definition.method.getHolder());
+        DexClass clazz = appInfo.definitionFor(definition.method.holder);
         assert clazz != null;
         if (clazz.isProgramClass()) {
           // For static invokes, the class could be initialized.
@@ -566,10 +566,10 @@
 
     private void processFieldAccess(DexField field) {
       // Any field access implicitly calls the class initializer.
-      if (field.clazz.isClassType()) {
+      if (field.holder.isClassType()) {
         DexEncodedField encodedField = appInfo.resolveField(field);
         if (encodedField != null && encodedField.isStatic()) {
-          addClassInitializerTarget(field.getHolder());
+          addClassInitializerTarget(field.holder);
         }
       }
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 0d7f6e1..a9ecdf7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -858,7 +858,7 @@
     boolean receiverCouldBeNull = context != null && context != method;
     Nullability nullability = receiverCouldBeNull ? maybeNull() : definitelyNotNull();
     TypeLatticeElement receiver =
-        TypeLatticeElement.fromDexType(method.method.getHolder(), nullability, appView);
+        TypeLatticeElement.fromDexType(method.method.holder, nullability, appView);
     Value value = writeRegister(register, receiver, ThrowingInfo.NO_THROW, local);
     addInstruction(new Argument(value));
     value.markAsThis();
@@ -1361,7 +1361,7 @@
             "MethodHandle.invoke and MethodHandle.invokeExact",
             null /* sourceString */);
       } else if (!appView.options().canUseInvokePolymorphicOnVarHandle()
-          && ((DexMethod) item).getHolder() == appView.dexItemFactory().varHandleType) {
+          && ((DexMethod) item).holder == appView.dexItemFactory().varHandleType) {
         throw new ApiLevelException(
             AndroidApiLevel.P,
             "Call to polymorphic signature of VarHandle",
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 85d9a2f..a66e958 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -188,9 +188,14 @@
             ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
             : null;
     this.stringOptimizer = new StringOptimizer(appView);
+    this.nonNullTracker =
+        options.enableNonNullTracking
+            ? new NonNullTracker(
+                appView, libraryMethodsReturningNonNull(appView.dexItemFactory()))
+            : null;
     if (appView.enableWholeProgramOptimizations()) {
       assert appView.appInfo().hasLiveness();
-      AppView<? extends AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
       assert rootSet != null;
       this.classInliner =
@@ -199,9 +204,6 @@
               : null;
       this.classStaticizer =
           options.enableClassStaticizer ? new ClassStaticizer(appViewWithLiveness, this) : null;
-      this.nonNullTracker =
-          new NonNullTracker(
-              appViewWithLiveness, libraryMethodsReturningNonNull(appView.dexItemFactory()));
       this.inliner = new Inliner(appViewWithLiveness, mainDexClasses);
       this.outliner = new Outliner(appViewWithLiveness, this);
       this.memberValuePropagation =
@@ -222,7 +224,6 @@
     } else {
       this.classInliner = null;
       this.classStaticizer = null;
-      this.nonNullTracker = null;
       this.inliner = null;
       this.outliner = null;
       this.memberValuePropagation = null;
@@ -946,7 +947,7 @@
 
     if (devirtualizer != null) {
       assert code.verifyTypes(appView);
-      devirtualizer.devirtualizeInvokeInterface(code, method.method.getHolder());
+      devirtualizer.devirtualizeInvokeInterface(code, method.method.holder);
     }
     if (uninstantiatedTypeOptimization != null) {
       uninstantiatedTypeOptimization.rewrite(method, code);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index e8cf039..7b58034 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -145,7 +145,7 @@
         } else if (current.isInvokeMethod()) {
           InvokeMethod invoke = current.asInvokeMethod();
           DexMethod invokedMethod = invoke.getInvokedMethod();
-          DexType invokedHolder = invokedMethod.getHolder();
+          DexType invokedHolder = invokedMethod.holder;
           if (invokedHolder.isArrayType()) {
             DexType baseType = invokedHolder.toBaseType(appInfo.dexItemFactory);
             DexType mappedBaseType = graphLense.lookupType(baseType);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index fb5fac8..3976ad7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -508,7 +508,7 @@
   }
 
   private Origin getMethodOrigin(DexMethod method) {
-    DexType holder = method.getHolder();
+    DexType holder = method.holder;
     if (isCompanionClassType(holder)) {
       holder = getInterfaceClassType(holder);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java
index 7c6c985..a92d536 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ArgumentRemovalUtils.java
@@ -12,8 +12,7 @@
 
   // Returns true if this method is pinned from the perspective of optimizations that attempt to
   // remove method arguments.
-  public static boolean isPinned(
-      DexEncodedMethod method, AppView<? extends AppInfoWithLiveness> appView) {
+  public static boolean isPinned(DexEncodedMethod method, AppView<AppInfoWithLiveness> appView) {
     return appView.appInfo().isPinned(method.method)
         || appView.appInfo().bootstrapMethods.contains(method.method)
         || appView.appInfo().brokenSuperInvokes.contains(method.method)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index ff903f3..36f54a6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -204,7 +204,7 @@
     // that the field is no longer written.
     if (appView.enableWholeProgramOptimizations() && converter.isInWave()) {
       if (appView.appInfo().hasLiveness()) {
-        AppView<? extends AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
+        AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
         AppInfoWithLiveness appInfoWithLiveness = appViewWithLiveness.appInfo();
 
         // First collect all the candidate fields that are *potentially* no longer being written to.
@@ -330,7 +330,7 @@
           if (instruction.isStaticGet()) {
             StaticGet get = instruction.asStaticGet();
             DexEncodedField field = appView.appInfo().resolveField(get.getField());
-            if (field != null && field.field.clazz == clazz.type) {
+            if (field != null && field.field.holder == clazz.type) {
               isReadBefore.add(field.field);
             } else if (instruction.instructionMayHaveSideEffects(appView, clazz.type)) {
               // Reading another field is only OK if the read does not have side-effects.
@@ -338,7 +338,7 @@
             }
           } else if (instruction.isStaticPut()) {
             StaticPut put = instruction.asStaticPut();
-            if (put.getField().clazz != clazz.type) {
+            if (put.getField().holder != clazz.type) {
               // Can cause clinit on another class which can read uninitialized static fields
               // of this class.
               return finalFieldPut.values();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 0015e17..240ad73 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1078,7 +1078,7 @@
           }
         }
         DexField field = insn.asFieldInstruction().getField();
-        if (field.clazz == clazz.type && clazz.lookupInstanceField(field) != null) {
+        if (field.holder == clazz.type && clazz.lookupInstanceField(field) != null) {
           // Require only accessing instance fields of the *current* class.
           continue;
         }
@@ -1440,7 +1440,7 @@
     Wrapper<DexMethod> methodWrap = wrapper.wrap(invokedMethod);
     if (methodWrap.equals(throwParamIsNullException)
         || (methodWrap.equals(checkParameterIsNotNull) && instr.inValues().get(0).equals(value))) {
-      if (invokedMethod.getHolder().getPackageDescriptor().startsWith(Kotlin.NAME)) {
+      if (invokedMethod.holder.getPackageDescriptor().startsWith(Kotlin.NAME)) {
         return true;
       }
     }
@@ -1592,7 +1592,7 @@
       return invoke.getInvokedMethod().proto.parameters.values[argumentIndex];
     }
     if (argumentIndex == 0) {
-      return invoke.getInvokedMethod().getHolder();
+      return invoke.getInvokedMethod().holder;
     }
     return invoke.getInvokedMethod().proto.parameters.values[argumentIndex - 1];
   }
@@ -1638,7 +1638,7 @@
             }
           } else if (appInfoWithLiveness != null) {
             DexEncodedMethod target =
-                invoke.lookupSingleTarget(appInfoWithLiveness, code.method.method.getHolder());
+                invoke.lookupSingleTarget(appInfoWithLiveness, code.method.method.holder);
             if (target != null) {
               DexMethod invokedMethod = target.method;
               // Check if the invoked method is known to return one of its arguments.
@@ -1956,7 +1956,7 @@
     }
     ConstraintWithTarget classVisibility =
         ConstraintWithTarget.deriveConstraint(
-            context.method.getHolder(), baseType, clazz.accessFlags, appInfo);
+            context.method.holder, baseType, clazz.accessFlags, appInfo);
     return classVisibility == ConstraintWithTarget.NEVER;
   }
 
@@ -3179,7 +3179,7 @@
         }
 
         DexEncodedMethod singleTarget = insn.asInvokeMethod().lookupSingleTarget(
-            appInfoWithLiveness, code.method.method.getHolder());
+            appInfoWithLiveness, code.method.method.holder);
         if (singleTarget == null || !singleTarget.getOptimizationInfo().neverReturnsNormally()) {
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 3f1b066..aa90c7f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -33,7 +33,7 @@
 
 public final class DefaultInliningOracle implements InliningOracle, InliningStrategy {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final Inliner inliner;
   private final DexEncodedMethod method;
   private final IRCode code;
@@ -44,7 +44,7 @@
   private int instructionAllowance;
 
   DefaultInliningOracle(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       Inliner inliner,
       DexEncodedMethod method,
       IRCode code,
@@ -75,7 +75,7 @@
         invoke.lookupSingleTarget(inliner.appView.appInfo(), invocationContext);
     if ((candidate == null)
         || (candidate.getCode() == null)
-        || inliner.appView.definitionFor(candidate.method.getHolder()).isLibraryClass()) {
+        || inliner.appView.definitionFor(candidate.method.holder).isLibraryClass()) {
       if (info != null) {
         info.exclude(invoke, "No inlinee");
       }
@@ -196,7 +196,7 @@
       return false;
     }
 
-    DexClass holder = inliner.appView.definitionFor(candidate.method.getHolder());
+    DexClass holder = inliner.appView.definitionFor(candidate.method.holder);
 
     if (holder.isInterface()) {
       // Art978_virtual_interfaceTest correctly expects an IncompatibleClassChangeError exception at
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index d0f3599..8fc16fe 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -35,9 +35,9 @@
  */
 public class Devirtualizer {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
 
-  public Devirtualizer(AppView<? extends AppInfoWithLiveness> appView) {
+  public Devirtualizer(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
@@ -112,7 +112,7 @@
         if (target == null) {
           continue;
         }
-        DexType holderType = target.method.getHolder();
+        DexType holderType = target.method.holder;
         DexClass holderClass = appView.definitionFor(holderType);
         // Make sure we are not landing on another interface, e.g., interface's default method.
         if (holderClass == null || holderClass.isInterface()) {
@@ -140,7 +140,7 @@
         // CodeRewriter#removeTrivialCheckCastAndInstanceOfInstructions}.
         // a <- check-cast A i  // Otherwise ART verification error.
         // (out <-) invoke-virtual a, ... A#foo
-        if (holderType != invoke.getInvokedMethod().getHolder()) {
+        if (holderType != invoke.getInvokedMethod().holder) {
           Value receiver = invoke.getReceiver();
           TypeLatticeElement receiverTypeLattice = receiver.getTypeLattice();
           TypeLatticeElement castTypeLattice =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
index aa3b405..01ef3ab 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/EnumOrdinalMapCollector.java
@@ -29,11 +29,11 @@
  */
 public class EnumOrdinalMapCollector {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
 
   private final Map<DexType, Reference2IntMap<DexField>> ordinalsMaps = new IdentityHashMap<>();
 
-  public EnumOrdinalMapCollector(AppView<? extends AppInfoWithLiveness> appView) {
+  public EnumOrdinalMapCollector(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 9a50518..55876af 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -49,7 +49,7 @@
 
 public class Inliner {
 
-  protected final AppView<? extends AppInfoWithLiveness> appView;
+  protected final AppView<AppInfoWithLiveness> appView;
   final MainDexClasses mainDexClasses;
 
   // State for inlining methods which are known to be called twice.
@@ -60,9 +60,7 @@
 
   private final Set<DexMethod> blackList = Sets.newIdentityHashSet();
 
-  public Inliner(
-      AppView<? extends AppInfoWithLiveness> appView,
-      MainDexClasses mainDexClasses) {
+  public Inliner(AppView<AppInfoWithLiveness> appView, MainDexClasses mainDexClasses) {
     this.appView = appView;
     this.mainDexClasses = mainDexClasses;
     fillInBlackList(appView.appInfo());
@@ -522,7 +520,7 @@
           return false;
         }
         DexField field = instruction.asInstancePut().getField();
-        DexEncodedField target = appView.appInfo().lookupInstanceTarget(field.getHolder(), field);
+        DexEncodedField target = appView.appInfo().lookupInstanceTarget(field.holder, field);
         if (target != null && target.accessFlags.isFinal()) {
           return false;
         }
@@ -673,10 +671,10 @@
       if (assumedReceiverType == null) {
         // In case we don't know exact type of the receiver we use declared
         // method holder as a fallback.
-        assumedReceiverType = invoke.getInvokedMethod().getHolder();
+        assumedReceiverType = invoke.getInvokedMethod().holder;
       }
-      if (assumedReceiverType != target.method.getHolder()) {
-        return target.method.getHolder();
+      if (assumedReceiverType != target.method.holder) {
+        return target.method.holder;
       }
     }
     return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index afb2e0f..efec070 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -23,7 +23,7 @@
 // Computes the inlining constraint for a given instruction.
 public class InliningConstraints {
 
-  private AppView<? extends AppInfoWithLiveness> appView;
+  private AppView<AppInfoWithLiveness> appView;
 
   private boolean allowStaticInterfaceMethodCalls = true;
 
@@ -39,8 +39,7 @@
   // type A to B, to create a temporary view of what the world would look like after class merging.
   private GraphLense graphLense;
 
-  public InliningConstraints(
-      AppView<? extends AppInfoWithLiveness> appView, GraphLense graphLense) {
+  public InliningConstraints(AppView<AppInfoWithLiveness> appView, GraphLense graphLense) {
     assert graphLense.isContextFreeForMethods();
     assert appView.graphLense() != graphLense || graphLense.isIdentityLense();
     this.appView = appView;
@@ -110,7 +109,7 @@
   public ConstraintWithTarget forInstanceGet(DexField field, DexType invocationContext) {
     DexField lookup = graphLense.lookupField(field);
     return forFieldInstruction(
-        lookup, appView.appInfo().lookupInstanceTarget(lookup.clazz, lookup), invocationContext);
+        lookup, appView.appInfo().lookupInstanceTarget(lookup.holder, lookup), invocationContext);
   }
 
   public ConstraintWithTarget forInstanceOf(DexType type, DexType invocationContext) {
@@ -120,7 +119,7 @@
   public ConstraintWithTarget forInstancePut(DexField field, DexType invocationContext) {
     DexField lookup = graphLense.lookupField(field);
     return forFieldInstruction(
-        lookup, appView.appInfo().lookupInstanceTarget(lookup.clazz, lookup), invocationContext);
+        lookup, appView.appInfo().lookupInstanceTarget(lookup.holder, lookup), invocationContext);
   }
 
   public ConstraintWithTarget forInvoke(DexMethod method, Type type, DexType invocationContext) {
@@ -237,13 +236,13 @@
   public ConstraintWithTarget forStaticGet(DexField field, DexType invocationContext) {
     DexField lookup = graphLense.lookupField(field);
     return forFieldInstruction(
-        lookup, appView.appInfo().lookupStaticTarget(lookup.clazz, lookup), invocationContext);
+        lookup, appView.appInfo().lookupStaticTarget(lookup.holder, lookup), invocationContext);
   }
 
   public ConstraintWithTarget forStaticPut(DexField field, DexType invocationContext) {
     DexField lookup = graphLense.lookupField(field);
     return forFieldInstruction(
-        lookup, appView.appInfo().lookupStaticTarget(lookup.clazz, lookup), invocationContext);
+        lookup, appView.appInfo().lookupStaticTarget(lookup.holder, lookup), invocationContext);
   }
 
   public ConstraintWithTarget forStore() {
@@ -269,7 +268,7 @@
   private ConstraintWithTarget forFieldInstruction(
       DexField field, DexEncodedField target, DexType invocationContext) {
     // Resolve the field if possible and decide whether the instruction can inlined.
-    DexType fieldHolder = graphLense.lookupType(field.clazz);
+    DexType fieldHolder = graphLense.lookupType(field.holder);
     DexClass fieldClass = appView.definitionFor(fieldHolder);
     if (target != null && fieldClass != null) {
       ConstraintWithTarget fieldConstraintWithTarget =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 0d5a895..36e9f25 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -42,7 +42,7 @@
 
 public class MemberValuePropagation {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
 
   private enum RuleType {
     NONE,
@@ -61,7 +61,7 @@
     }
   }
 
-  public MemberValuePropagation(AppView<? extends AppInfoWithLiveness> appView) {
+  public MemberValuePropagation(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
@@ -95,7 +95,7 @@
       DexField field = rule.getReturnValue().getField();
       assert typeLattice
           == TypeLatticeElement.fromDexType(field.type, Nullability.maybeNull(), appView.appInfo());
-      DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field.clazz, field);
+      DexEncodedField staticField = appView.appInfo().lookupStaticTarget(field.holder, field);
       if (staticField != null) {
         Value value = code.createValue(typeLattice, instruction.getLocalInfo());
         replacement =
@@ -104,7 +104,7 @@
           code.method.getMutableOptimizationInfo().markUseIdentifierNameString();
         }
       } else {
-        throw new CompilationError(field.clazz.toSourceString() + "." + field.name.toString()
+        throw new CompilationError(field.holder.toSourceString() + "." + field.name.toString()
             + " used in assumevalues rule does not exist.");
       }
     }
@@ -185,7 +185,7 @@
       InstructionListIterator iterator,
       InvokeMethod current) {
     DexMethod invokedMethod = current.getInvokedMethod();
-    DexType invokedHolder = invokedMethod.getHolder();
+    DexType invokedHolder = invokedMethod.holder;
     if (!invokedHolder.isClassType()) {
       return;
     }
@@ -261,7 +261,7 @@
     DexField field = current.getField();
 
     // TODO(b/123857022): Should be able to use definitionFor().
-    DexEncodedField target = appView.appInfo().lookupStaticTarget(field.getHolder(), field);
+    DexEncodedField target = appView.appInfo().lookupStaticTarget(field.holder, field);
     if (target == null || appView.appInfo().neverPropagateValue.contains(target.field)) {
       return;
     }
@@ -295,10 +295,10 @@
       // be updated outside the class constructor, e.g. via reflections), it is safe
       // to assume that the static-get instruction reads the value it initialized value
       // in class initializer and is never null.
-      DexClass holderDefinition = appView.definitionFor(field.getHolder());
+      DexClass holderDefinition = appView.definitionFor(field.holder);
       if (holderDefinition != null
           && holderDefinition.accessFlags.isFinal()
-          && !field.getHolder().initializationOfParentTypesMayHaveSideEffects(appView.appInfo())) {
+          && !field.holder.initializationOfParentTypesMayHaveSideEffects(appView.appInfo())) {
         Value outValue = current.dest();
         DexEncodedMethod classInitializer = holderDefinition.getClassInitializer();
         if (classInitializer != null && !isProcessedConcurrently.test(classInitializer)) {
@@ -324,8 +324,8 @@
     // TODO(b/123857022): Should be possible to use definitionFor().
     DexEncodedField target =
         current.isInstancePut()
-            ? appView.appInfo().lookupInstanceTarget(field.getHolder(), field)
-            : appView.appInfo().lookupStaticTarget(field.getHolder(), field);
+            ? appView.appInfo().lookupInstanceTarget(field.holder, field)
+            : appView.appInfo().lookupStaticTarget(field.holder, field);
     if (target == null || appView.appInfo().neverPropagateValue.contains(target.field)) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index 2a1092e..ec338ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
@@ -23,7 +24,6 @@
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.OptimizationFeedback;
-import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Sets;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -41,11 +41,11 @@
 
 public class NonNullTracker {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<? extends AppInfo> appView;
   private final Set<DexMethod> libraryMethodsReturningNonNull;
 
   public NonNullTracker(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<? extends AppInfo> appView,
       Set<DexMethod> libraryMethodsReturningNonNull) {
     this.appView = appView;
     this.libraryMethodsReturningNonNull = libraryMethodsReturningNonNull;
@@ -120,11 +120,14 @@
             knownToBeNonNullValues.add(knownToBeNonNullValue);
           }
         }
-        if (current.isInvokeMethod() && !current.isInvokePolymorphic()) {
+        if (current.isInvokeMethod()
+            && !current.isInvokePolymorphic()
+            && appView.appInfo().hasLiveness()) {
           DexEncodedMethod singleTarget =
               current
                   .asInvokeMethod()
-                  .lookupSingleTarget(appView.appInfo(), code.method.method.getHolder());
+                  .lookupSingleTarget(
+                      appView.appInfo().withLiveness(), code.method.method.holder);
           if (singleTarget != null
               && singleTarget.getOptimizationInfo().getNonNullParamOnNormalExits() != null) {
             BitSet facts = singleTarget.getOptimizationInfo().getNonNullParamOnNormalExits();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index a7ec086..f5fca72 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -114,7 +114,7 @@
 
   static final int MAX_IN_SIZE = 5;  // Avoid using ranged calls for outlined code.
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final InliningConstraints inliningConstraints;
 
   private abstract static class OutlineInstruction {
@@ -902,7 +902,7 @@
     private DexType argumentTypeFromInvoke(InvokeMethod invoke, int index) {
       assert invoke.isInvokeMethodWithReceiver() || invoke.isInvokePolymorphic();
       if (index == 0) {
-        return invoke.getInvokedMethod().getHolder();
+        return invoke.getInvokedMethod().holder;
       }
       DexProto methodProto;
       if (invoke.isInvokePolymorphic()) {
@@ -1199,7 +1199,7 @@
     }
   }
 
-  public Outliner(AppView<? extends AppInfoWithLiveness> appView, IRConverter converter) {
+  public Outliner(AppView<AppInfoWithLiveness> appView, IRConverter converter) {
     this.appView = appView;
     this.inliningConstraints = new InliningConstraints(appView, GraphLense.getIdentityLense());
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
index e298873..5c55d31 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -79,7 +79,7 @@
 
   private boolean couldBeVolatile(DexField field) {
     if (!appView.enableWholeProgramOptimizations()
-        && field.getHolder() != method.method.getHolder()) {
+        && field.holder != method.method.holder) {
       return true;
     }
     DexEncodedField definition = appView.definitionFor(field);
@@ -187,7 +187,7 @@
       FieldAndObject fieldAndObject = new FieldAndObject(field, object);
       activeInstanceFields.remove(fieldAndObject);
     } else if (instruction.isStaticPut()) {
-      if (field.clazz != code.method.method.holder) {
+      if (field.holder != code.method.method.holder) {
         // Accessing a static field on a different object could cause <clinit> to run which
         // could modify any static field on any other object.
         activeStaticFields.clear();
@@ -195,7 +195,7 @@
         activeStaticFields.remove(field);
       }
     } else if (instruction.isStaticGet()) {
-      if (field.clazz != code.method.method.holder) {
+      if (field.holder != code.method.method.holder) {
         // Accessing a static field on a different object could cause <clinit> to run which
         // could modify any static field on any other object.
         activeStaticFields.clear();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
index bd0fdea..e71def5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ReflectionOptimizer.java
@@ -76,7 +76,7 @@
   }
 
   // Rewrite getClass() call to const-class if the type of the given instance is effectively final.
-  public static void rewriteGetClass(AppView<? extends AppInfoWithLiveness> appView, IRCode code) {
+  public static void rewriteGetClass(AppView<AppInfoWithLiveness> appView, IRCode code) {
     InstructionIterator it = code.instructionIterator();
     DexItemFactory dexItemFactory = appView.dexItemFactory();
     while (it.hasNext()) {
@@ -125,7 +125,7 @@
           || (!in.isPhi() && in.definition.isCreatingInstanceOrArray())) {
         // Make sure the target (base) type is visible.
         ConstraintWithTarget constraints =
-            ConstraintWithTarget.classIsVisible(code.method.method.getHolder(), baseType, appView);
+            ConstraintWithTarget.classIsVisible(code.method.method.holder, baseType, appView);
         if (constraints == ConstraintWithTarget.NEVER) {
           continue;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
index a20539c..869e159 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchMapCollector.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import java.util.Arrays;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -60,13 +59,13 @@
  */
 public class SwitchMapCollector {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final DexString switchMapPrefix;
   private final DexType intArrayType;
 
   private final Map<DexField, Int2ReferenceMap<DexField>> switchMaps = new IdentityHashMap<>();
 
-  public SwitchMapCollector(AppView<? extends AppInfoWithLiveness> appView) {
+  public SwitchMapCollector(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
     switchMapPrefix = appView.dexItemFactory().createString("$SwitchMap$");
     intArrayType = appView.dexItemFactory().createType("[I");
@@ -87,7 +86,7 @@
     if (!clazz.accessFlags.isSynthetic() && !clazz.hasClassInitializer()) {
       return;
     }
-    List<DexEncodedField> switchMapFields = Arrays.stream(clazz.staticFields())
+    List<DexEncodedField> switchMapFields = clazz.staticFields().stream()
         .filter(this::maybeIsSwitchMap).collect(Collectors.toList());
     if (!switchMapFields.isEmpty()) {
       IRCode initializer = clazz.getClassInitializer().buildIR(appView, clazz.origin);
@@ -125,7 +124,7 @@
             return;
           }
           DexField enumField = enumGet.asStaticGet().getField();
-          if (!appView.definitionFor(enumField.getHolder()).accessFlags.isEnum()) {
+          if (!appView.definitionFor(enumField.holder).accessFlags.isEnum()) {
             return;
           }
           if (switchMap.put(integerIndex, enumField) != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SwitchUtils.java b/src/main/java/com/android/tools/r8/ir/optimize/SwitchUtils.java
index 7331b00..8313b09 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SwitchUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SwitchUtils.java
@@ -58,7 +58,7 @@
    * and {@link SwitchMapCollector} for details.
    */
   public static EnumSwitchInfo analyzeSwitchOverEnum(
-      Instruction switchInsn, AppView<? extends AppInfoWithLiveness> appView) {
+      Instruction switchInsn, AppView<AppInfoWithLiveness> appView) {
     AppInfoWithLiveness appInfo = appView.appInfo();
     Instruction input = switchInsn.inValues().get(0).definition;
     if (input == null || !input.isArrayGet()) {
@@ -92,7 +92,7 @@
     }
     // Due to member rebinding, only the fields are certain to provide the actual enums
     // class.
-    DexType enumType = indexMap.values().iterator().next().getHolder();
+    DexType enumType = indexMap.values().iterator().next().holder;
     Reference2IntMap<DexField> ordinalsMap = appInfo.getOrdinalsMapFor(enumType);
     if (ordinalsMap == null) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index 4daeb9e..c550118 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -105,7 +105,7 @@
     }
   }
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final DexItemFactory dexItemFactory;
   private final InternalOptions options;
 
@@ -114,7 +114,7 @@
   private int numberOfInvokesWithNullReceiver = 0;
 
   public UninstantiatedTypeOptimization(
-      AppView<? extends AppInfoWithLiveness> appView, InternalOptions options) {
+      AppView<AppInfoWithLiveness> appView, InternalOptions options) {
     this.appView = appView;
     this.dexItemFactory = appView.dexItemFactory();
     this.options = options;
@@ -459,7 +459,7 @@
 
       // We also need to be sure that this field instruction cannot trigger static class
       // initialization.
-      if (field.field.clazz != code.method.method.holder) {
+      if (field.field.holder != code.method.method.holder) {
         DexClass enclosingClass = appView.definitionFor(code.method.method.holder);
         if (enclosingClass == null
             || enclosingClass.classInitializationMayHaveSideEffects(appView.appInfo())) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
index 96bce47..ad5f3be 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -40,7 +40,7 @@
 
 public class UnusedArgumentsCollector {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
 
   private final BiMap<DexMethod, DexMethod> methodMapping = HashBiMap.create();
   private final Map<DexMethod, RemovedArgumentsInfo> removedArguments = new IdentityHashMap<>();
@@ -81,7 +81,7 @@
     }
   }
 
-  public UnusedArgumentsCollector(AppView<? extends AppInfoWithLiveness> appView) {
+  public UnusedArgumentsCollector(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index da18d38..10508db 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -27,12 +27,11 @@
 
 public final class ClassInliner {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final LambdaRewriter lambdaRewriter;
   private final ConcurrentHashMap<DexClass, Boolean> knownClasses = new ConcurrentHashMap<>();
 
-  public ClassInliner(
-      AppView<? extends AppInfoWithLiveness> appView, LambdaRewriter lambdaRewriter) {
+  public ClassInliner(AppView<AppInfoWithLiveness> appView, LambdaRewriter lambdaRewriter) {
     this.appView = appView;
     this.lambdaRewriter = lambdaRewriter;
   }
@@ -115,7 +114,7 @@
   //   }
   //
   public final void processMethodCode(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       CodeRewriter codeRewriter,
       StringOptimizer stringOptimizer,
       DexEncodedMethod method,
@@ -201,7 +200,7 @@
     }
   }
 
-  private boolean isClassEligible(AppView<? extends AppInfoWithLiveness> appView, DexClass clazz) {
+  private boolean isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
     Boolean eligible = knownClasses.get(clazz);
     if (eligible == null) {
       Boolean computed = computeClassEligible(appView, clazz);
@@ -216,8 +215,7 @@
   //   - is not an abstract class or interface
   //   - does not declare finalizer
   //   - does not trigger any static initializers except for its own
-  private boolean computeClassEligible(
-      AppView<? extends AppInfoWithLiveness> appView, DexClass clazz) {
+  private boolean computeClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
     if (clazz == null
         || clazz.isLibraryClass()
         || clazz.accessFlags.isAbstract()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index f36a132..79e4f2d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -58,7 +58,7 @@
   private static final ImmutableSet<If.Type> ALLOWED_ZERO_TEST_TYPES =
       ImmutableSet.of(If.Type.EQ, If.Type.NE);
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final LambdaRewriter lambdaRewriter;
   private final Inliner inliner;
   private final Predicate<DexClass> isClassEligible;
@@ -81,7 +81,7 @@
   private int estimatedCombinedSizeForInlining = 0;
 
   InlineCandidateProcessor(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       LambdaRewriter lambdaRewriter,
       Inliner inliner,
       Predicate<DexClass> isClassEligible,
@@ -207,7 +207,7 @@
     //      of class inlining
     //
 
-    if (eligibleClassDefinition.instanceFields().length > 0) {
+    if (eligibleClassDefinition.instanceFields().size() > 0) {
       return false;
     }
     if (eligibleClassDefinition.type.hasSubtypes()) {
@@ -253,7 +253,7 @@
         if (user.isInstanceGet()
             || (user.isInstancePut() && user.asInstancePut().value() != eligibleInstance)) {
           DexField field = user.asFieldInstruction().getField();
-          if (field.clazz == eligibleClass
+          if (field.holder == eligibleClass
               && eligibleClassDefinition.lookupInstanceField(field) != null) {
             // Since class inliner currently only supports classes directly extending
             // java.lang.Object, we don't need to worry about fields defined in superclasses.
@@ -511,7 +511,7 @@
                 + "` after field reads removed: "
                 + user);
       }
-      if (user.asInstancePut().getField().clazz != eligibleClass) {
+      if (user.asInstancePut().getField().holder != eligibleClass) {
         throw new Unreachable(
             "Unexpected field write left in method `"
                 + method.method.toSourceString()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
index ede37b1..5a39e71 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CaptureSignature.java
@@ -16,6 +16,7 @@
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Comparator;
+import java.util.List;
 import java.util.function.IntFunction;
 
 // While mapping fields representing lambda captures we rearrange fields to make sure
@@ -106,8 +107,8 @@
   }
 
   // Compute capture signature based on lambda class capture fields.
-  public static String getCaptureSignature(DexEncodedField[] fields) {
-    return getCaptureSignature(fields.length, i -> fields[i].field.type);
+  public static String getCaptureSignature(List<DexEncodedField> fields) {
+    return getCaptureSignature(fields.size(), i -> fields.get(i).field.type);
   }
 
   // Compute capture signature based on type list.
@@ -118,7 +119,7 @@
   // Having a list of fields of lambda captured values, maps one of them into
   // an index of the appropriate field in normalized capture signature.
   public static int mapFieldIntoCaptureIndex(
-      String capture, DexEncodedField[] lambdaFields, DexField fieldToMap) {
+      String capture, List<DexEncodedField> lambdaFields, DexField fieldToMap) {
     char fieldKind = fieldToMap.type.toShorty();
     int numberOfSameCaptureKind = 0;
     int result = -1;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
index 854ac19..edee200 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/CodeProcessor.java
@@ -281,9 +281,9 @@
 
   private void handle(InstanceGet instanceGet) {
     DexField field = instanceGet.getField();
-    Strategy strategy = strategyProvider.apply(field.clazz);
+    Strategy strategy = strategyProvider.apply(field.holder);
     if (strategy.isValidInstanceFieldRead(this, field)) {
-      if (field.clazz != this.method.method.holder) {
+      if (field.holder != this.method.method.holder) {
         // Only rewrite references to lambda classes if we are outside the class.
         process(strategy, instanceGet);
       }
@@ -298,9 +298,9 @@
 
   private void handle(InstancePut instancePut) {
     DexField field = instancePut.getField();
-    Strategy strategy = strategyProvider.apply(field.clazz);
+    Strategy strategy = strategyProvider.apply(field.holder);
     if (strategy.isValidInstanceFieldWrite(this, field)) {
-      if (field.clazz != this.method.method.holder) {
+      if (field.holder != this.method.method.holder) {
         // Only rewrite references to lambda classes if we are outside the class.
         process(strategy, instancePut);
       }
@@ -315,29 +315,29 @@
 
   private void handle(StaticGet staticGet) {
     DexField field = staticGet.getField();
-    Strategy strategy = strategyProvider.apply(field.clazz);
+    Strategy strategy = strategyProvider.apply(field.holder);
     if (strategy.isValidStaticFieldRead(this, field)) {
-      if (field.clazz != this.method.method.holder) {
+      if (field.holder != this.method.method.holder) {
         // Only rewrite references to lambda classes if we are outside the class.
         process(strategy, staticGet);
       }
     } else {
       lambdaChecker.accept(field.type);
-      lambdaChecker.accept(field.clazz);
+      lambdaChecker.accept(field.holder);
     }
   }
 
   private void handle(StaticPut staticPut) {
     DexField field = staticPut.getField();
-    Strategy strategy = strategyProvider.apply(field.clazz);
+    Strategy strategy = strategyProvider.apply(field.holder);
     if (strategy.isValidStaticFieldWrite(this, field)) {
-      if (field.clazz != this.method.method.holder) {
+      if (field.holder != this.method.method.holder) {
         // Only rewrite references to lambda classes if we are outside the class.
         process(strategy, staticPut);
       }
     } else {
       lambdaChecker.accept(field.type);
-      lambdaChecker.accept(field.clazz);
+      lambdaChecker.accept(field.holder);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
index b0af387..6b5625c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaGroup.java
@@ -110,16 +110,16 @@
     return lambdas.get(lambda).id;
   }
 
-  protected final DexEncodedField[] lambdaCaptureFields(DexType lambda) {
+  protected final List<DexEncodedField> lambdaCaptureFields(DexType lambda) {
     assert lambdas.containsKey(lambda);
     return lambdas.get(lambda).clazz.instanceFields();
   }
 
   protected final DexEncodedField lambdaSingletonField(DexType lambda) {
     assert lambdas.containsKey(lambda);
-    DexEncodedField[] fields = lambdas.get(lambda).clazz.staticFields();
-    assert fields.length < 2;
-    return fields.length == 0 ? null : fields[0];
+    List<DexEncodedField> fields = lambdas.get(lambda).clazz.staticFields();
+    assert fields.size() < 2;
+    return fields.size() == 0 ? null : fields.get(0);
   }
 
   // Contains less than 2 elements?
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 66ae73a..20f0b6a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -146,7 +146,7 @@
   // We do this before methods are being processed to guarantee stable order of
   // lambdas inside each group.
   public final void collectGroupCandidates(
-      DexApplication app, AppView<? extends AppInfoWithLiveness> appView) {
+      DexApplication app, AppView<AppInfoWithLiveness> appView) {
     // Collect lambda groups.
     app.classes().stream()
         .filter(cls -> !appView.appInfo().isPinned(cls.type))
@@ -154,7 +154,8 @@
             cls ->
                 cls.hasKotlinInfo()
                     && cls.getKotlinInfo().isSyntheticClass()
-                    && cls.getKotlinInfo().asSyntheticClass().isLambda())
+                    && cls.getKotlinInfo().asSyntheticClass().isLambda()
+                    && KotlinLambdaGroupIdFactory.hasValidAnnotations(kotlin, cls))
         .sorted((a, b) -> a.type.slowCompareTo(b.type)) // Ensure stable ordering.
         .forEachOrdered(
             lambda -> {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
index 05c71e6..7422bc0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaTypeVisitor.java
@@ -87,8 +87,8 @@
 
   void accept(DexField field, DexType holderToIgnore) {
     accept(field.type);
-    if (holderToIgnore != field.clazz) {
-      accept(field.clazz);
+    if (holderToIgnore != field.holder) {
+      accept(field.holder);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
index 2bdc441..db9f04e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroup.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.collect.Lists;
+import java.util.List;
 import java.util.function.IntFunction;
 
 // Represents a j-style lambda group created to combine several lambda classes
@@ -152,8 +153,8 @@
     }
 
     @Override
-    int getInstanceInitializerSize(DexEncodedField[] captures) {
-      return captures.length + 2;
+    int getInstanceInitializerSize(List<DexEncodedField> captures) {
+      return captures.size() + 2;
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
index ee0b800..587e172 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroup.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.google.common.collect.Lists;
+import java.util.List;
 import java.util.function.IntFunction;
 
 // Represents a k-style lambda group created to combine several lambda classes
@@ -158,8 +159,8 @@
     }
 
     @Override
-    int getInstanceInitializerSize(DexEncodedField[] captures) {
-      return captures.length + 3;
+    int getInstanceInitializerSize(List<DexEncodedField> captures) {
+      return captures.size() + 3;
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
index 2385f35..16430eb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaClassValidator.java
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.optimize.lambda.LambdaGroup.LambdaStructureError;
 import com.android.tools.r8.kotlin.Kotlin;
 import com.android.tools.r8.utils.ThrowingConsumer;
+import java.util.List;
 
 // Encapsulates the logic of deep-checking of the lambda class assumptions.
 //
@@ -118,14 +119,14 @@
     }
   }
 
-  abstract int getInstanceInitializerSize(DexEncodedField[] captures);
+  abstract int getInstanceInitializerSize(List<DexEncodedField> captures);
 
   abstract int validateInstanceInitializerEpilogue(
       com.android.tools.r8.code.Instruction[] instructions, int index) throws LambdaStructureError;
 
   private void validateInstanceInitializer(DexClass lambda, Code code)
       throws LambdaStructureError {
-    DexEncodedField[] captures = lambda.instanceFields();
+    List<DexEncodedField> captures = lambda.instanceFields();
     com.android.tools.r8.code.Instruction[] instructions = code.asDexCode().instructions;
     int index = 0;
 
@@ -142,66 +143,66 @@
     assert index == instructions.length;
   }
 
-  private int validateInstanceInitializerParameterMapping(DexEncodedField[] captures,
+  private int validateInstanceInitializerParameterMapping(List<DexEncodedField> captures,
       Instruction[] instructions, int index) throws LambdaStructureError {
     int wideFieldsSeen = 0;
     for (DexEncodedField field : captures) {
       switch (field.field.type.toShorty()) {
         case 'Z':
-          if (!(instructions[index] instanceof IputBoolean) ||
-              (instructions[index].getField() != field.field) ||
-              (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+          if (!(instructions[index] instanceof IputBoolean)
+              || (instructions[index].getField() != field.field)
+              || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
             throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
           }
           break;
 
         case 'B':
-          if (!(instructions[index] instanceof IputByte) ||
-              (instructions[index].getField() != field.field) ||
-              (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+          if (!(instructions[index] instanceof IputByte)
+              || (instructions[index].getField() != field.field)
+              || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
             throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
           }
           break;
 
         case 'S':
-          if (!(instructions[index] instanceof IputShort) ||
-              (instructions[index].getField() != field.field) ||
-              (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+          if (!(instructions[index] instanceof IputShort)
+              || (instructions[index].getField() != field.field)
+              || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
             throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
           }
           break;
 
         case 'C':
-          if (!(instructions[index] instanceof IputChar) ||
-              (instructions[index].getField() != field.field) ||
-              (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+          if (!(instructions[index] instanceof IputChar)
+              || (instructions[index].getField() != field.field)
+              || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
             throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
           }
           break;
 
         case 'I':
         case 'F':
-          if (!(instructions[index] instanceof Iput) ||
-              (instructions[index].getField() != field.field) ||
-              (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+          if (!(instructions[index] instanceof Iput)
+              || (instructions[index].getField() != field.field)
+              || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
             throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
           }
           break;
 
         case 'J':
         case 'D':
-          if (!(instructions[index] instanceof IputWide) ||
-              (instructions[index].getField() != field.field) ||
-              (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+          if (!(instructions[index] instanceof IputWide)
+              || (instructions[index].getField() != field.field)
+              || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
             throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
           }
           wideFieldsSeen++;
           break;
 
         case 'L':
-          if (!(instructions[index] instanceof IputObject) ||
-              (instructions[index].getField() != field.field) ||
-              (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
+          if (!(instructions[index] instanceof IputObject)
+              || (instructions[index].getField() != field.field)
+              || (((Format22c) instructions[index]).A != (index + 1 + wideFieldsSeen))) {
             throw structureError(LAMBDA_INIT_CODE_VERIFICATION_FAILED);
           }
           break;
@@ -221,8 +222,8 @@
     if (instructions.length != 4) {
       throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
     }
-    if (!(instructions[0] instanceof com.android.tools.r8.code.NewInstance) ||
-        ((com.android.tools.r8.code.NewInstance) instructions[0]).getType() != lambda.type) {
+    if (!(instructions[0] instanceof com.android.tools.r8.code.NewInstance)
+        || ((com.android.tools.r8.code.NewInstance) instructions[0]).getType() != lambda.type) {
       throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
     }
     if (!(instructions[1] instanceof com.android.tools.r8.code.InvokeDirect
@@ -230,8 +231,8 @@
         || !isLambdaInitializerMethod(lambda, instructions[1].getMethod())) {
       throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
     }
-    if (!(instructions[2] instanceof SputObject) ||
-        !isLambdaSingletonField(lambda, instructions[2].getField())) {
+    if (!(instructions[2] instanceof SputObject)
+        || !isLambdaSingletonField(lambda, instructions[2].getField())) {
       throw structureError(LAMBDA_CLINIT_CODE_VERIFICATION_FAILED);
     }
     if (!(instructions[3] instanceof ReturnVoid)) {
@@ -240,12 +241,15 @@
   }
 
   private boolean isLambdaSingletonField(DexClass lambda, DexField field) {
-    return field.type == lambda.type && field.clazz == lambda.type &&
-        field.name == kotlin.functional.kotlinStyleLambdaInstanceName;
+    return field.type == lambda.type
+        && field.holder == lambda.type
+        && field.name == kotlin.functional.kotlinStyleLambdaInstanceName;
   }
 
   private boolean isLambdaInitializerMethod(DexClass holder, DexMethod method) {
-    return method.holder == holder.type && method.name == kotlin.factory.constructorMethodName &&
-        method.proto.parameters.isEmpty() && method.proto.returnType == kotlin.factory.voidType;
+    return method.holder == holder.type
+        && method.name == kotlin.factory.constructorMethodName
+        && method.proto.parameters.isEmpty()
+        && method.proto.returnType == kotlin.factory.voidType;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
index 144487b..48967c3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupCodeStrategy.java
@@ -48,7 +48,7 @@
 
   @Override
   public boolean isValidStaticFieldWrite(CodeProcessor context, DexField field) {
-    DexType lambda = field.clazz;
+    DexType lambda = field.holder;
     assert group.containsLambda(lambda);
     // Only support writes to singleton static field named 'INSTANCE' from lambda
     // static class initializer.
@@ -60,7 +60,7 @@
 
   @Override
   public boolean isValidStaticFieldRead(CodeProcessor context, DexField field) {
-    DexType lambda = field.clazz;
+    DexType lambda = field.holder;
     assert group.containsLambda(lambda);
     // Support all reads of singleton static field named 'INSTANCE'.
     return field.name == context.kotlin.functional.kotlinStyleLambdaInstanceName &&
@@ -69,7 +69,7 @@
 
   @Override
   public boolean isValidInstanceFieldWrite(CodeProcessor context, DexField field) {
-    DexType lambda = field.clazz;
+    DexType lambda = field.holder;
     DexMethod method = context.method.method;
     assert group.containsLambda(lambda);
     // Support writes to capture instance fields inside lambda constructor only.
@@ -78,7 +78,7 @@
 
   @Override
   public boolean isValidInstanceFieldRead(CodeProcessor context, DexField field) {
-    assert group.containsLambda(field.clazz);
+    assert group.containsLambda(field.holder);
     // Support all reads from capture instance fields.
     return true;
   }
@@ -157,7 +157,7 @@
         new InstanceGet(
             createValueForType(context, fieldType),
             instanceGet.object(),
-            mapCaptureField(context.factory, field.clazz, field));
+            mapCaptureField(context.factory, field.holder, field));
     context.instructions().replaceCurrentInstruction(newInstanceGet);
 
     if (fieldType.isPrimitiveType() || fieldType == context.factory.objectType) {
@@ -270,6 +270,6 @@
 
   // Map lambda class initializer into lambda group class initializer.
   private DexField mapSingletonInstanceField(DexItemFactory factory, DexField field) {
-    return group.getSingletonInstanceField(factory, group.lambdaId(field.clazz));
+    return group.getSingletonInstanceField(factory, group.lambdaId(field.holder));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 4b8290e..7f7643b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -87,6 +87,21 @@
     return null;
   }
 
+  public static boolean hasValidAnnotations(Kotlin kotlin, DexClass lambda) {
+    if (!lambda.annotations.isEmpty()) {
+      for (DexAnnotation annotation : lambda.annotations.annotations) {
+        if (DexAnnotation.isSignatureAnnotation(annotation, kotlin.factory)) {
+          continue;
+        }
+        if (annotation.annotation.type == kotlin.metadata.kotlinMetadataType) {
+          continue;
+        }
+        return false;
+      }
+    }
+    return true;
+  }
+
   String validateAnnotations(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
     String signature = null;
     if (!lambda.annotations.isEmpty()) {
@@ -103,38 +118,40 @@
           continue;
         }
 
+        assert !hasValidAnnotations(kotlin, lambda);
         throw new LambdaStructureError(
             "unexpected annotation: " + annotation.annotation.type.toSourceString());
       }
     }
+    assert hasValidAnnotations(kotlin, lambda);
     return signature;
   }
 
   void validateStaticFields(Kotlin kotlin, DexClass lambda) throws LambdaStructureError {
-    DexEncodedField[] staticFields = lambda.staticFields();
-    if (staticFields.length == 1) {
-      DexEncodedField field = staticFields[0];
+    List<DexEncodedField> staticFields = lambda.staticFields();
+    if (staticFields.size() == 1) {
+      DexEncodedField field = staticFields.get(0);
       if (field.field.name != kotlin.functional.kotlinStyleLambdaInstanceName ||
           field.field.type != lambda.type || !field.accessFlags.isPublic() ||
           !field.accessFlags.isFinal() || !field.accessFlags.isStatic()) {
         throw new LambdaStructureError("unexpected static field " + field.toSourceString());
       }
       // No state if the lambda is a singleton.
-      if (lambda.instanceFields().length > 0) {
+      if (lambda.instanceFields().size() > 0) {
         throw new LambdaStructureError("has instance fields along with INSTANCE");
       }
       checkAccessFlags("static field access flags", field.accessFlags, SINGLETON_FIELD_FLAGS);
       checkFieldAnnotations(field);
 
-    } else if (staticFields.length > 1) {
+    } else if (staticFields.size() > 1) {
       throw new LambdaStructureError(
-          "only one static field max expected, found " + staticFields.length);
+          "only one static field max expected, found " + staticFields.size());
     }
   }
 
   String validateInstanceFields(DexClass lambda, boolean accessRelaxed)
       throws LambdaStructureError {
-    DexEncodedField[] instanceFields = lambda.instanceFields();
+    List<DexEncodedField> instanceFields = lambda.instanceFields();
     for (DexEncodedField field : instanceFields) {
       checkAccessFlags("capture field access flags", field.accessFlags,
           accessRelaxed ? CAPTURE_FIELD_FLAGS_RELAXED : CAPTURE_FIELD_FLAGS);
@@ -147,7 +164,7 @@
     for (DexEncodedMethod method : lambda.directMethods()) {
       if (method.isClassInitializer()) {
         // We expect to see class initializer only if there is a singleton field.
-        if (lambda.staticFields().length != 1) {
+        if (lambda.staticFields().size() != 1) {
           throw new LambdaStructureError("has static initializer, but no singleton field");
         }
         checkAccessFlags(
@@ -163,15 +180,15 @@
         // Lambda class is expected to have one constructor
         // with parameters matching capture signature.
         DexType[] parameters = method.method.proto.parameters.values;
-        DexEncodedField[] instanceFields = lambda.instanceFields();
-        if (parameters.length != instanceFields.length) {
+        List<DexEncodedField> instanceFields = lambda.instanceFields();
+        if (parameters.length != instanceFields.size()) {
           throw new LambdaStructureError("constructor parameters don't match captured values.");
         }
         for (int i = 0; i < parameters.length; i++) {
           // Kotlin compiler sometimes reshuffles the parameters so that their order
           // in the constructor don't match order of capture fields. We could add
           // support for it, but it happens quite rarely so don't bother for now.
-          if (parameters[i] != instanceFields[i].field.type) {
+          if (parameters[i] != instanceFields.get(i).field.type) {
             throw new LambdaStructureError(
                 "constructor parameters don't match captured values.", false);
           }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
index 9473341..0265c5b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -46,7 +46,7 @@
 
 public final class ClassStaticizer {
 
-  final AppView<? extends AppInfoWithLiveness> appView;
+  final AppView<AppInfoWithLiveness> appView;
   final DexItemFactory factory;
   final IRConverter converter;
 
@@ -85,7 +85,7 @@
     }
 
     DexType hostType() {
-      return singletonField.field.clazz;
+      return singletonField.field.holder;
     }
 
     DexClass hostClass() {
@@ -103,7 +103,7 @@
   // The map storing all the potential candidates for staticizing.
   final ConcurrentHashMap<DexType, CandidateInfo> candidates = new ConcurrentHashMap<>();
 
-  public ClassStaticizer(AppView<? extends AppInfoWithLiveness> appView, IRConverter converter) {
+  public ClassStaticizer(AppView<AppInfoWithLiveness> appView, IRConverter converter) {
     this.appView = appView;
     this.factory = appView.dexItemFactory();
     this.converter = converter;
@@ -144,18 +144,18 @@
       }
 
       // High-level limitations on what classes we consider eligible.
-      if (cls.isInterface() || // Must not be an interface or an abstract class.
-          cls.accessFlags.isAbstract() ||
+      if (cls.isInterface() // Must not be an interface or an abstract class.
+          || cls.accessFlags.isAbstract()
           // Don't support candidates with instance fields
-          cls.instanceFields().length > 0 ||
+          || cls.instanceFields().size() > 0
           // Only support classes directly extending java.lang.Object
-          cls.superType != factory.objectType ||
+          || cls.superType != factory.objectType
           // Instead of requiring the class being final,
           // just ensure it does not have subtypes
-          cls.type.hasSubtypes() ||
+          || cls.type.hasSubtypes()
           // Staticizing classes implementing interfaces is more
           // difficult, so don't support it until we really need it.
-          !cls.interfaces.isEmpty()) {
+          || !cls.interfaces.isEmpty()) {
         notEligible.add(cls.type);
       }
     });
@@ -312,7 +312,7 @@
 
       if (instruction.isInstanceGet() || instruction.isInstancePut()) {
         DexField fieldReferenced = instruction.asFieldInstruction().getField();
-        CandidateInfo candidateInfo = candidates.get(fieldReferenced.clazz);
+        CandidateInfo candidateInfo = candidates.get(fieldReferenced.holder);
         if (candidateInfo != null) {
           // Reads/writes to instance field of the candidate class are not supported.
           candidateInfo.invalidate();
@@ -324,7 +324,7 @@
 
   private boolean isAllowedInHostClassInitializer(
       DexType host, Instruction insn, IRCode code) {
-    return (insn.isStaticPut() && insn.asStaticPut().getField().clazz == host) ||
+    return (insn.isStaticPut() && insn.asStaticPut().getField().holder == host) ||
         insn.isConstNumber() ||
         insn.isConstString() ||
         (insn.isGoto() && insn.asGoto().isTrivialGotoToTheNextBlock(code)) ||
@@ -455,7 +455,7 @@
     // Allow single assignment to a singleton field.
     StaticPut staticPut = instruction.asStaticPut();
     DexEncodedField fieldAccessed =
-        appView.appInfo().lookupStaticTarget(staticPut.getField().clazz, staticPut.getField());
+        appView.appInfo().lookupStaticTarget(staticPut.getField().holder, staticPut.getField());
     return fieldAccessed == info.singletonField;
   }
 
@@ -470,7 +470,7 @@
       return null;
     }
 
-    assert candidateInfo.singletonField == appView.appInfo().lookupStaticTarget(field.clazz, field)
+    assert candidateInfo.singletonField == appView.appInfo().lookupStaticTarget(field.holder, field)
         : "Added reference after collectCandidates(...)?";
 
     Value singletonValue = staticGet.dest();
@@ -574,7 +574,7 @@
     }
 
     private boolean registerField(DexField field) {
-      registerTypeReference(field.clazz);
+      registerTypeReference(field.holder);
       registerTypeReference(field.type);
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
index 6331d5f..ec23f05 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -50,11 +50,10 @@
 import java.util.concurrent.Future;
 import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 final class StaticizingProcessor {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final ClassStaticizer classStaticizer;
   private final ExecutorService executorService;
 
@@ -65,7 +64,7 @@
   private final Map<DexType, DexType> candidateToHostMapping = new IdentityHashMap<>();
 
   StaticizingProcessor(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       ClassStaticizer classStaticizer,
       ExecutorService executorService) {
     this.appView = appView;
@@ -125,7 +124,7 @@
       // CHECK: instance initializer used to create an instance is trivial.
       // NOTE: Along with requirement that candidate does not have instance
       // fields this should guarantee that the constructor is empty.
-      assert candidateClass.instanceFields().length == 0;
+      assert candidateClass.instanceFields().size() == 0;
       assert constructorUsed.isProcessed();
       TrivialInitializer trivialInitializer =
           constructorUsed.getOptimizationInfo().getTrivialInitializerInfo();
@@ -231,7 +230,7 @@
         //       extend java.lang.Object guarantees that the constructor is actually
         //       empty and does not need to be inlined.
         assert candidateInfo.candidate.superType == factory().objectType;
-        assert candidateInfo.candidate.instanceFields().length == 0;
+        assert candidateInfo.candidate.instanceFields().size() == 0;
 
         Value singletonValue = instruction.outValue();
         assert singletonValue != null;
@@ -319,7 +318,7 @@
   // From staticizer's viewpoint, `sp` is trivial in the sense that it is composed of values that
   // refer to the same singleton field. If so, we can safely relax the assertion; remove uses of
   // field reads; remove quasi-trivial phis; and then remove original field reads.
-  private boolean testAndcollectPhisComposedOfSameFieldRead(
+  private boolean testAndCollectPhisComposedOfSameFieldRead(
       Set<Phi> phisToCheck, DexField field, Set<Phi> trivialPhis) {
     for (Phi phi : phisToCheck) {
       Set<Phi> chainedPhis = Sets.newIdentityHashSet();
@@ -336,7 +335,7 @@
         }
       }
       if (!chainedPhis.isEmpty()) {
-        if (!testAndcollectPhisComposedOfSameFieldRead(chainedPhis, field, trivialPhis)) {
+        if (!testAndCollectPhisComposedOfSameFieldRead(chainedPhis, field, trivialPhis)) {
           return false;
         }
       }
@@ -353,7 +352,7 @@
     // However, it may be not true if re-processing introduces phis after optimizing common suffix.
     Set<Phi> trivialPhis = Sets.newIdentityHashSet();
     boolean hasTrivialPhis =
-        testAndcollectPhisComposedOfSameFieldRead(dest.uniquePhiUsers(), field, trivialPhis);
+        testAndCollectPhisComposedOfSameFieldRead(dest.uniquePhiUsers(), field, trivialPhis);
     assert dest.numberOfPhiUsers() == 0 || hasTrivialPhis;
     Set<Instruction> users = new HashSet<>(dest.uniqueUsers());
     // If that is the case, method calls we want to fix up include users of those phis.
@@ -456,13 +455,13 @@
   }
 
   private DexField mapFieldIfMoved(DexField field) {
-    DexType hostType = candidateToHostMapping.get(field.clazz);
+    DexType hostType = candidateToHostMapping.get(field.holder);
     if (hostType != null) {
       field = factory().createField(hostType, field.type, field.name);
     }
     hostType = candidateToHostMapping.get(field.type);
     if (hostType != null) {
-      field = factory().createField(field.clazz, hostType, field.name);
+      field = factory().createField(field.holder, hostType, field.name);
     }
     return field;
   }
@@ -511,12 +510,13 @@
 
   private boolean classMembersConflict(DexClass a, DexClass b) {
     assert Streams.stream(a.methods()).allMatch(DexEncodedMethod::isStatic);
-    assert a.instanceFields().length == 0;
-    return Stream.of(a.staticFields()).anyMatch(fld -> b.lookupField(fld.field) != null) ||
-        Streams.stream(a.methods()).anyMatch(method -> b.lookupMethod(method.method) != null);
+    assert a.instanceFields().size() == 0;
+    return a.staticFields().stream().anyMatch(fld -> b.lookupField(fld.field) != null)
+        || Streams.stream(a.methods()).anyMatch(method -> b.lookupMethod(method.method) != null);
   }
 
-  private void moveMembersIntoHost(Set<DexEncodedMethod> staticizedMethods,
+  private void moveMembersIntoHost(
+      Set<DexEncodedMethod> staticizedMethods,
       DexProgramClass candidateClass,
       DexType hostType, DexClass hostClass,
       BiMap<DexMethod, DexMethod> methodMapping,
@@ -524,26 +524,36 @@
     candidateToHostMapping.put(candidateClass.type, hostType);
 
     // Process static fields.
-    // Append fields first.
-    if (candidateClass.staticFields().length > 0) {
-      DexEncodedField[] oldFields = hostClass.staticFields();
-      DexEncodedField[] extraFields = candidateClass.staticFields();
-      DexEncodedField[] newFields = new DexEncodedField[oldFields.length + extraFields.length];
-      System.arraycopy(oldFields, 0, newFields, 0, oldFields.length);
-      System.arraycopy(extraFields, 0, newFields, oldFields.length, extraFields.length);
-      hostClass.setStaticFields(newFields);
-    }
-
-    // Fixup field types.
-    DexEncodedField[] staticFields = hostClass.staticFields();
-    for (int i = 0; i < staticFields.length; i++) {
-      DexEncodedField field = staticFields[i];
+    int numOfHostStaticFields = hostClass.staticFields().size();
+    DexEncodedField[] newFields =
+        candidateClass.staticFields().size() > 0
+            ? new DexEncodedField[numOfHostStaticFields + candidateClass.staticFields().size()]
+            : new DexEncodedField[numOfHostStaticFields];
+    List<DexEncodedField> oldFields = hostClass.staticFields();
+    for (int i = 0; i < oldFields.size(); i++) {
+      DexEncodedField field = oldFields.get(i);
       DexField newField = mapCandidateField(field.field, candidateClass.type, hostType);
       if (newField != field.field) {
-        staticFields[i] = field.toTypeSubstitutedField(newField);
+        newFields[i] = field.toTypeSubstitutedField(newField);
         fieldMapping.put(field.field, newField);
+      } else {
+        newFields[i] = field;
       }
     }
+    if (candidateClass.staticFields().size() > 0) {
+      List<DexEncodedField> extraFields = candidateClass.staticFields();
+      for (int i = 0; i < extraFields.size(); i++) {
+        DexEncodedField field = extraFields.get(i);
+        DexField newField = mapCandidateField(field.field, candidateClass.type, hostType);
+        if (newField != field.field) {
+          newFields[numOfHostStaticFields + i] = field.toTypeSubstitutedField(newField);
+          fieldMapping.put(field.field, newField);
+        } else {
+          newFields[numOfHostStaticFields + i] = field;
+        }
+      }
+    }
+    hostClass.setStaticFields(newFields);
 
     // Process static methods.
     List<DexEncodedMethod> extraMethods = candidateClass.directMethods();
@@ -567,9 +577,9 @@
   }
 
   private DexField mapCandidateField(DexField field, DexType candidateType, DexType hostType) {
-    return field.clazz != candidateType && field.type != candidateType ? field
+    return field.holder != candidateType && field.type != candidateType ? field
         : factory().createField(
-            field.clazz == candidateType ? hostType : field.clazz,
+            field.holder == candidateType ? hostType : field.holder,
             field.type == candidateType ? hostType : field.type,
             field.name);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
index 942265e..35ddb6f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringOptimizer.java
@@ -358,7 +358,7 @@
       }
       if (instr.isInvokeMethod()) {
         DexMethod invokedMethod = instr.asInvokeMethod().getInvokedMethod();
-        DexClass holder = appInfo.definitionFor(invokedMethod.getHolder());
+        DexClass holder = appInfo.definitionFor(invokedMethod.holder);
         // For most cases, library call is not interesting, e.g.,
         // System.out.println(...), String.valueOf(...), etc.
         // If it's too broad, we can introduce black-list.
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index d3cda4a..1fda335 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -37,7 +37,7 @@
 public class InliningConstraintVisitor extends MethodVisitor {
 
   private final JarApplicationReader application;
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final GraphLense graphLense;
   private final InliningConstraints inliningConstraints;
   private final DexEncodedMethod method;
@@ -47,7 +47,7 @@
 
   public InliningConstraintVisitor(
       JarApplicationReader application,
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       GraphLense graphLense,
       DexEncodedMethod method,
       DexType invocationContext) {
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 1e7094d..00ec506 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -217,7 +217,7 @@
 
   public String originalNameOf(IndexedDexItem item) {
     if (item instanceof DexField) {
-      return lookupName(getRenamedFieldSignature((DexField) item), ((DexField) item).clazz);
+      return lookupName(getRenamedFieldSignature((DexField) item), ((DexField) item).holder);
     } else if (item instanceof DexMethod) {
       return lookupName(getRenamedMethodSignature((DexMethod) item), ((DexMethod) item).holder);
     } else if (item instanceof DexType) {
@@ -255,7 +255,7 @@
   }
 
   public FieldSignature originalSignatureOf(DexField field) {
-    String decoded = descriptorToJavaType(field.clazz.descriptor.toString());
+    String decoded = descriptorToJavaType(field.holder.descriptor.toString());
     FieldSignature memberSignature = getRenamedFieldSignature(field);
     ClassNaming classNaming = getClassNaming(decoded);
     if (classNaming == null) {
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 c073bb5..92c07d1 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -39,7 +39,7 @@
 
 class ClassNameMinifier {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
   private final ClassNamingStrategy classNamingStrategy;
   private final PackageNamingStrategy packageNamingStrategy;
@@ -62,7 +62,7 @@
   private final Namespace topLevelState;
 
   ClassNameMinifier(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       RootSet rootSet,
       ClassNamingStrategy classNamingStrategy,
       PackageNamingStrategy packageNamingStrategy,
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index e5f9218..96bba7a 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -20,9 +20,7 @@
 class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
 
   FieldNameMinifier(
-      AppView<? extends AppInfoWithLiveness> appView,
-      RootSet rootSet,
-      MemberNamingStrategy strategy) {
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
     super(appView, rootSet, strategy);
   }
 
@@ -134,7 +132,7 @@
       return;
     }
     // Now, `field` is reference. Find its definition and check if it's renamed.
-    DexClass holder = appInfo.definitionFor(field.clazz);
+    DexClass holder = appInfo.definitionFor(field.holder);
     // We don't care pruned types or library classes.
     if (holder == null || holder.isLibraryClass()) {
       return;
@@ -146,7 +144,7 @@
       return;
     }
     assert definition.field != field;
-    assert definition.field.clazz != field.clazz;
+    assert definition.field.holder != field.holder;
     // If the definition is renamed,
     if (renaming.containsKey(definition.field)) {
       // Assign the same, renamed name as the definition to the reference.
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 9479ab3..8468724 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -46,10 +46,10 @@
 
 public class IdentifierNameStringMarker {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final Object2BooleanMap<DexReference> identifierNameStrings;
 
-  public IdentifierNameStringMarker(AppView<? extends AppInfoWithLiveness> appView) {
+  public IdentifierNameStringMarker(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
     // Note that this info is only available at AppInfoWithLiveness.
     this.identifierNameStrings = appView.appInfo().identifierNameStrings;
@@ -84,7 +84,7 @@
       return;
     }
     ThrowingInfo throwingInfo = ThrowingInfo.defaultForConstString(appView.options());
-    DexType originHolder = code.method.method.getHolder();
+    DexType originHolder = code.method.method.holder;
     ListIterator<BasicBlock> blocks = code.listIterator();
     while (blocks.hasNext()) {
       BasicBlock block = blocks.next();
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
index 42e3f76..b6b50b9 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -22,7 +22,7 @@
 
 abstract class MemberNameMinifier<MemberType, StateType extends CachedHashValueDexItem> {
 
-  protected final AppView<? extends AppInfoWithLiveness> appView;
+  protected final AppView<AppInfoWithLiveness> appView;
   protected final AppInfoWithLiveness appInfo;
   protected final RootSet rootSet;
   protected final InternalOptions options;
@@ -41,9 +41,7 @@
   private final BiMap<DexType, NamingState<StateType, ?>> states = HashBiMap.create();
 
   MemberNameMinifier(
-      AppView<? extends AppInfoWithLiveness> appView,
-      RootSet rootSet,
-      MemberNamingStrategy strategy) {
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 50b22b0..e9932e1 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -94,9 +94,7 @@
   private final MemberNamingStrategy strategy;
 
   MethodNameMinifier(
-      AppView<? extends AppInfoWithLiveness> appView,
-      RootSet rootSet,
-      MemberNamingStrategy strategy) {
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
     super(appView, rootSet, strategy);
     this.strategy = strategy;
     equivalence =
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
index e3d09b6..0fdeebc 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedNameMapPrinter.java
@@ -72,7 +72,7 @@
     }
   }
 
-  private void writeFields(DexEncodedField[] fields, StringBuilder out) {
+  private void writeFields(List<DexEncodedField> fields, StringBuilder out) {
     for (DexEncodedField encodedField : fields) {
       DexField field = encodedField.field;
       DexString renamed = namingLens.lookupName(field);
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 f72efe1..18a23ba 100644
--- a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -150,11 +150,11 @@
     DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
     DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
     DexClass staticTargetHolder =
-        staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
+        staticTarget != null ? appInfo.definitionFor(staticTarget.method.holder) : null;
     DexClass directTargetHolder =
-        directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
+        directTarget != null ? appInfo.definitionFor(directTarget.method.holder) : null;
     DexClass virtualTargetHolder =
-        virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
+        virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.holder) : null;
     return (directTarget == null && staticTarget == null && virtualTarget == null)
         || (virtualTarget != null && virtualTarget.method == item)
         || (directTarget != null && directTarget.method == item)
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 96b174e..0b7bf45 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -28,16 +28,14 @@
 
 public class Minifier {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
   private final RootSet rootSet;
   private final Set<DexCallSite> desugaredCallSites;
   private final InternalOptions options;
 
   public Minifier(
-      AppView<? extends AppInfoWithLiveness> appView,
-      RootSet rootSet,
-      Set<DexCallSite> desugaredCallSites) {
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, Set<DexCallSite> desugaredCallSites) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
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 aecae1e..8488f09 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingLens.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingLens.java
@@ -79,7 +79,7 @@
 
   public final DexField lookupField(DexField field, DexItemFactory dexItemFactory) {
     return dexItemFactory.createField(
-        lookupType(field.clazz, dexItemFactory),
+        lookupType(field.holder, dexItemFactory),
         lookupType(field.type, dexItemFactory),
         lookupName(field));
   }
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 bd05b42..84c21f6 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -37,7 +37,7 @@
 
 public class ProguardMapMinifier {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
   private final RootSet rootSet;
   private final SeedMapper seedMapper;
@@ -45,7 +45,7 @@
   private final DexItemFactory factory;
 
   public ProguardMapMinifier(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       RootSet rootSet,
       SeedMapper seedMapper,
       Set<DexCallSite> desugaredCallSites) {
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index 1f9570c..20f4bc2 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -28,17 +28,17 @@
 
 public class GenericSignatureRewriter {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
   private final Map<DexType, DexString> renaming;
   private final Reporter reporter;
 
-  public GenericSignatureRewriter(AppView<? extends AppInfoWithLiveness> appView) {
+  public GenericSignatureRewriter(AppView<AppInfoWithLiveness> appView) {
     this(appView, Maps.newIdentityHashMap());
   }
 
   public GenericSignatureRewriter(
-      AppView<? extends AppInfoWithLiveness> appView, Map<DexType, DexString> renaming) {
+      AppView<AppInfoWithLiveness> appView, Map<DexType, DexString> renaming) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
     this.renaming = renaming;
diff --git a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
index 229471e..93c02ac 100644
--- a/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/ClassAndMemberPublicizer.java
@@ -23,16 +23,14 @@
 public final class ClassAndMemberPublicizer {
 
   private final DexApplication application;
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final RootSet rootSet;
   private final MethodPoolCollection methodPoolCollection;
 
   private final PublicizedLenseBuilder lenseBuilder = PublicizerLense.createBuilder();
 
   private ClassAndMemberPublicizer(
-      DexApplication application,
-      AppView<? extends AppInfoWithLiveness> appView,
-      RootSet rootSet) {
+      DexApplication application, AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
     this.application = application;
     this.appView = appView;
     this.methodPoolCollection = new MethodPoolCollection(application);
@@ -49,7 +47,7 @@
       ExecutorService executorService,
       Timing timing,
       DexApplication application,
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       RootSet rootSet)
       throws ExecutionException {
     return new ClassAndMemberPublicizer(application, appView, rootSet).run(executorService, timing);
@@ -125,7 +123,7 @@
       accessFlags.promoteToPublic();
       // Although the current method became public, it surely has the single virtual target.
       encodedMethod.method.setSingleVirtualMethodCache(
-          encodedMethod.method.getHolder(), encodedMethod);
+          encodedMethod.method.holder, encodedMethod);
       return true;
     }
 
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index cd3f27a..650d7ca 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -36,7 +36,7 @@
 
   private final MemberRebindingLense.Builder builder;
 
-  public MemberRebindingAnalysis(AppView<? extends AppInfoWithLiveness> appView) {
+  public MemberRebindingAnalysis(AppView<AppInfoWithLiveness> appView) {
     assert appView.graphLense().isContextFreeForMethods();
     this.appInfo = appView.appInfo();
     this.lense = appView.graphLense();
@@ -47,7 +47,7 @@
   }
 
   private DexMethod validTargetFor(DexMethod target, DexMethod original) {
-    DexClass clazz = appInfo.definitionFor(target.getHolder());
+    DexClass clazz = appInfo.definitionFor(target.holder);
     assert clazz != null;
     if (searchInLibrary || !clazz.isLibraryClass()) {
       return target;
@@ -55,25 +55,25 @@
     DexType newHolder;
     if (clazz.isInterface()) {
       newHolder =
-          firstLibraryClassForInterfaceTarget(target, original.getHolder(), DexClass::lookupMethod);
+          firstLibraryClassForInterfaceTarget(target, original.holder, DexClass::lookupMethod);
     } else {
-      newHolder = firstLibraryClass(target.getHolder(), original.getHolder());
+      newHolder = firstLibraryClass(target.holder, original.holder);
     }
     return appInfo.dexItemFactory.createMethod(newHolder, original.proto, original.name);
   }
 
   private DexField validTargetFor(DexField target, DexField original,
       BiFunction<DexClass, DexField, DexEncodedField> lookup) {
-    DexClass clazz = appInfo.definitionFor(target.getHolder());
+    DexClass clazz = appInfo.definitionFor(target.holder);
     assert clazz != null;
     if (searchInLibrary || !clazz.isLibraryClass()) {
       return target;
     }
     DexType newHolder;
     if (clazz.isInterface()) {
-      newHolder = firstLibraryClassForInterfaceTarget(target, original.getHolder(), lookup);
+      newHolder = firstLibraryClassForInterfaceTarget(target, original.holder, lookup);
     } else {
-      newHolder = firstLibraryClass(target.getHolder(), original.getHolder());
+      newHolder = firstLibraryClass(target.holder, original.holder);
     }
     return appInfo.dexItemFactory.createField(newHolder, original.type, original.name);
   }
@@ -113,15 +113,15 @@
   }
 
   private DexEncodedMethod classLookup(DexMethod method) {
-    return appInfo.resolveMethodOnClass(method.getHolder(), method).asResultOfResolve();
+    return appInfo.resolveMethodOnClass(method.holder, method).asResultOfResolve();
   }
 
   private DexEncodedMethod interfaceLookup(DexMethod method) {
-    return appInfo.resolveMethodOnInterface(method.getHolder(), method).asResultOfResolve();
+    return appInfo.resolveMethodOnInterface(method.holder, method).asResultOfResolve();
   }
 
   private DexEncodedMethod anyLookup(DexMethod method) {
-    return appInfo.resolveMethod(method.getHolder(), method).asResultOfResolve();
+    return appInfo.resolveMethod(method.holder, method).asResultOfResolve();
   }
 
   private void computeMethodRebinding(
@@ -130,7 +130,7 @@
       Type invokeType) {
     for (DexMethod method : methodsWithContexts.keySet()) {
       // We can safely ignore array types, as the corresponding methods are defined in a library.
-      if (!method.getHolder().isClassType()) {
+      if (!method.holder.isClassType()) {
         continue;
       }
       DexClass originalClass = appInfo.definitionFor(method.holder);
@@ -159,7 +159,7 @@
           final DexEncodedMethod finalTarget = target;
           Set<DexEncodedMethod> contexts = methodsWithContexts.get(method);
           if (contexts.stream().anyMatch(context ->
-              mayNeedBridgeForVisibility(context.method.getHolder(), finalTarget))) {
+              mayNeedBridgeForVisibility(context.method.holder, finalTarget))) {
             target =
                 insertBridgeForVisibilityIfNeeded(
                     method, target, originalClass, targetClass, lookupTarget);
@@ -216,7 +216,7 @@
   }
 
   private boolean mayNeedBridgeForVisibility(DexType context, DexEncodedMethod method) {
-    DexType holderType = method.method.getHolder();
+    DexType holderType = method.method.holder;
     DexClass holder = appInfo.definitionFor(holderType);
     if (holder == null) {
       return false;
@@ -289,7 +289,7 @@
       BiFunction<DexType, DexField, DexEncodedField> lookup,
       BiFunction<DexClass, DexField, DexEncodedField> lookupTargetOnClass) {
     for (DexField field : fieldsWithContexts.keySet()) {
-      DexEncodedField target = lookup.apply(field.getHolder(), field);
+      DexEncodedField target = lookup.apply(field.holder, field);
       // Rebind to the lowest library class or program class. Do not rebind accesses to fields that
       // are not visible from the access context.
       Set<DexEncodedMethod> contexts = fieldsWithContexts.get(field);
@@ -300,8 +300,8 @@
                   context ->
                       isMemberVisibleFromOriginalContext(
                           appInfo,
-                          context.method.getHolder(),
-                          target.field.clazz,
+                          context.method.holder,
+                          target.field.holder,
                           target.accessFlags))) {
         builder.map(field,
             lense.lookupField(validTargetFor(target.field, field, lookupTargetOnClass)));
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
index f2e6c6d..8b6c7fa 100644
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -17,9 +17,9 @@
 
 public class VisibilityBridgeRemover {
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
 
-  public VisibilityBridgeRemover(AppView<? extends AppInfoWithLiveness> appView) {
+  public VisibilityBridgeRemover(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
@@ -73,7 +73,7 @@
       if (kind == InvokeKind.SUPER) {
         // This is a visibility forward, so check for the direct target.
         DexEncodedMethod targetMethod =
-            appView.appInfo().resolveMethod(target.getHolder(), target).asSingleTarget();
+            appView.appInfo().resolveMethod(target.holder, target).asSingleTarget();
         if (targetMethod != null && targetMethod.accessFlags.isPublic()) {
           if (Log.ENABLED) {
             Log.info(
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index b6aa517..d164d91 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -429,22 +429,33 @@
 
   private <S extends DexItem, T extends Descriptor<S, T>> boolean registerItemWithTarget(
       Map<DexType, Set<T>> seen, T item) {
-    DexType holder = item.getHolder().toBaseType(appInfo.dexItemFactory);
+    assert item.isDexField() || item.isDexMethod();
+    DexType itemHolder =
+        item.isDexField()
+            ? item.asDexField().holder
+            : item.asDexMethod().holder;
+    DexType holder = itemHolder.toBaseType(appInfo.dexItemFactory);
     if (!holder.isClassType()) {
       return false;
     }
     markTypeAsLive(holder);
-    return seen.computeIfAbsent(item.getHolder(), (ignore) -> Sets.newIdentityHashSet()).add(item);
+    return seen.computeIfAbsent(itemHolder, (ignore) -> Sets.newIdentityHashSet()).add(item);
   }
 
   private <S extends DexItem, T extends Descriptor<S, T>> boolean registerItemWithTargetAndContext(
       Map<DexType, Set<TargetWithContext<T>>> seen, T item, DexEncodedMethod context) {
-    DexType holder = item.getHolder().toBaseType(appInfo.dexItemFactory);
+    assert item.isDexField() || item.isDexMethod();
+    DexType itemHolder =
+        item.isDexField()
+            ? item.asDexField().holder
+            : item.asDexMethod().holder;
+    DexType holder = itemHolder.toBaseType(appInfo.dexItemFactory);
     if (!holder.isClassType()) {
       return false;
     }
     markTypeAsLive(holder);
-    return seen.computeIfAbsent(item.getHolder(), (ignore) -> new HashSet<>())
+    return seen
+        .computeIfAbsent(itemHolder, (ignore) -> new HashSet<>())
         .add(new TargetWithContext<>(item, context));
   }
 
@@ -616,7 +627,7 @@
       DexEncodedField encodedField = appInfo.resolveField(field);
       if (encodedField != null && encodedField.isProgramField(appInfo)) {
         boolean isWrittenOutsideEnclosingStaticInitializer =
-            currentMethod.method.holder != encodedField.field.clazz
+            currentMethod.method.holder != encodedField.field.holder
                 || !currentMethod.isClassInitializer();
         if (isWrittenOutsideEnclosingStaticInitializer) {
           staticFieldsWrittenOutsideEnclosingStaticInitializer.add(encodedField.field);
@@ -798,11 +809,11 @@
   }
 
   private DexMethod getInvokeSuperTarget(DexMethod method, DexEncodedMethod currentMethod) {
-    DexClass methodHolderClass = appInfo.definitionFor(method.getHolder());
+    DexClass methodHolderClass = appInfo.definitionFor(method.holder);
     if (methodHolderClass != null && methodHolderClass.isInterface()) {
       return method;
     }
-    DexClass holderClass = appInfo.definitionFor(currentMethod.method.getHolder());
+    DexClass holderClass = appInfo.definitionFor(currentMethod.method.holder);
     if (holderClass == null || holderClass.superType == null || holderClass.isInterface()) {
       // We do not know better or this call is made from an interface.
       return method;
@@ -1157,7 +1168,7 @@
       DexField field, KeepReason reason, DexEncodedField encodedField) {
     // Mark the type live here, so that the class exists at runtime. Note that this also marks all
     // supertypes as live, so even if the field is actually on a supertype, its class will be live.
-    markTypeAsLive(field.clazz);
+    markTypeAsLive(field.holder);
     markTypeAsLive(field.type);
 
     // Find the actual field.
@@ -1188,7 +1199,7 @@
 
   private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
     assert field != null;
-    markTypeAsLive(field.field.clazz);
+    markTypeAsLive(field.field.holder);
     markTypeAsLive(field.field.type);
     if (Log.ENABLED) {
       Log.verbose(getClass(), "Adding instance field `%s` to live set.", field.field);
@@ -1292,13 +1303,13 @@
     if (encodedField.accessFlags.isStatic()) {
       markStaticFieldAsLive(encodedField.field, reason);
     } else {
-      if (isInstantiatedOrHasInstantiatedSubtype(encodedField.field.clazz)) {
+      if (isInstantiatedOrHasInstantiatedSubtype(encodedField.field.holder)) {
         // We have at least one live subtype, so mark it as live.
         markInstanceFieldAsLive(encodedField, reason);
       } else {
         // Add the field to the reachable set if the type later becomes instantiated.
         reachableInstanceFields
-            .computeIfAbsent(encodedField.field.clazz, ignore -> newSetWithoutReasonReporter())
+            .computeIfAbsent(encodedField.field.holder, ignore -> newSetWithoutReasonReporter())
             .add(encodedField, reason);
       }
     }
@@ -1623,7 +1634,7 @@
 
   private void markFieldAsKept(DexEncodedField target, KeepReason reason) {
     // If this field no longer has a corresponding class, then we have shaken it away before.
-    if (appInfo.definitionFor(target.field.clazz) == null) {
+    if (appInfo.definitionFor(target.field.holder) == null) {
       return;
     }
     if (target.accessFlags.isStatic()) {
@@ -1714,12 +1725,12 @@
   }
 
   private DexField tryLookupInstanceField(DexField field) {
-    DexEncodedField target = appInfo.lookupInstanceTarget(field.clazz, field);
+    DexEncodedField target = appInfo.lookupInstanceTarget(field.holder, field);
     return target == null ? null : target.field;
   }
 
   private DexField tryLookupStaticField(DexField field) {
-    DexEncodedField target = appInfo.lookupStaticTarget(field.clazz, field);
+    DexEncodedField target = appInfo.lookupStaticTarget(field.holder, field);
     return target == null ? null : target.field;
   }
 
@@ -1749,7 +1760,7 @@
   }
 
   private void markMethodAsKeptWithCompatRule(DexEncodedMethod method) {
-    DexClass holderClass = appInfo.definitionFor(method.method.getHolder());
+    DexClass holderClass = appInfo.definitionFor(method.method.holder);
     ProguardKeepRule rule =
         ProguardConfigurationUtils.buildMethodKeepRule(holderClass, method);
     proguardCompatibilityWorkList.add(
@@ -1809,7 +1820,7 @@
             !encodedField.accessFlags.isStatic()
                 && appInfo.dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod);
         if (keepClass) {
-          DexClass holderClass = appInfo.definitionFor(encodedField.field.getHolder());
+          DexClass holderClass = appInfo.definitionFor(encodedField.field.holder);
           markInstantiated(holderClass.type, KeepReason.reflectiveUseIn(method));
         }
         markFieldAsKept(encodedField, KeepReason.reflectiveUseIn(method));
@@ -2443,9 +2454,11 @@
         DexType typeToCheck;
         if (item.isDexType()) {
           typeToCheck = item.asDexType();
+        } else if (item.isDexMethod()) {
+          typeToCheck = item.asDexMethod().holder;
         } else {
-          assert item.isDescriptor();
-          typeToCheck = item.asDescriptor().getHolder();
+          assert item.isDexField();
+          typeToCheck = item.asDexField().holder;
         }
         assert !typeSet.contains(typeToCheck);
       }
@@ -2487,7 +2500,7 @@
           // TODO(b/121354886): Pinned fields should be in `fieldsRead`.
           || isPinned(field)
           // Fields in the class that is synthesized by D8/R8 would be used soon.
-          || field.getHolder().isD8R8SynthesizedClassType()
+          || field.holder.isD8R8SynthesizedClassType()
           // For library classes we don't know whether a field is read.
           || isLibraryField(field);
     }
@@ -2497,7 +2510,7 @@
           // TODO(b/121354886): Pinned fields should be in `fieldsWritten`.
           || isPinned(field)
           // Fields in the class that is synthesized by D8/R8 would be used soon.
-          || field.clazz.isD8R8SynthesizedClassType()
+          || field.holder.isD8R8SynthesizedClassType()
           // For library classes we don't know whether a field is rewritten.
           || isLibraryField(field);
     }
@@ -2508,7 +2521,7 @@
     }
 
     private boolean isLibraryField(DexField field) {
-      DexClass holder = definitionFor(field.clazz);
+      DexClass holder = definitionFor(field.holder);
       return holder == null || holder.isLibraryClass();
     }
 
@@ -2620,7 +2633,7 @@
     }
 
     public DexEncodedMethod lookup(Type type, DexMethod target, DexType invocationContext) {
-      DexType holder = target.getHolder();
+      DexType holder = target.holder;
       if (!holder.isClassType()) {
         return null;
       }
@@ -2934,7 +2947,7 @@
 
     @Override
     public boolean addField(DexField field) {
-      DexClass holder = appInfo.definitionFor(field.clazz);
+      DexClass holder = appInfo.definitionFor(field.holder);
       if (holder == null) {
         return false;
       }
@@ -3119,11 +3132,11 @@
     return fieldNodes.computeIfAbsent(
         context,
         f -> {
-          DexClass holderDefinition = appInfo.definitionFor(context.getHolder());
+          DexClass holderDefinition = appInfo.definitionFor(context.holder);
           return new FieldGraphNode(
               holderDefinition != null && holderDefinition.isLibraryClass(),
               Reference.field(
-                  Reference.classFromDescriptor(f.getHolder().toDescriptorString()),
+                  Reference.classFromDescriptor(f.holder.toDescriptorString()),
                   f.name.toString(),
                   Reference.typeFromDescriptor(f.type.toDescriptorString())));
         });
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
index 71b3d53..07aaecd 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -125,7 +125,7 @@
     }
 
     protected boolean registerInvoke(DexMethod method) {
-      consumer.accept(method.getHolder());
+      consumer.accept(method.holder);
       traceMethodDirectDependencies(method, consumer);
       return true;
     }
@@ -151,7 +151,7 @@
     }
 
     protected boolean registerFieldAccess(DexField field) {
-      consumer.accept(field.getHolder());
+      consumer.accept(field.holder);
       consumer.accept(field.type);
       return true;
     }
@@ -179,14 +179,14 @@
 
     @Override
     public boolean addField(DexField field) {
-      consumer.accept(field.getHolder());
+      consumer.accept(field.holder);
       consumer.accept(field.type);
       return false;
     }
 
     @Override
     public boolean addMethod(DexMethod method) {
-      consumer.accept(method.getHolder());
+      consumer.accept(method.holder);
       addProto(method.proto);
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 9f7d021..7e473a7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -177,7 +177,7 @@
     reporter.failIfPendingErrors();
   }
 
-  private static enum IdentifierType {
+  private enum IdentifierType {
     CLASS_NAME,
     ANY
   }
@@ -292,7 +292,11 @@
           configurationBuilder.setPackagePrefix(parsePackageNameOrEmptyString());
           expectClosingQuote(quote);
         } else {
-          configurationBuilder.setPackagePrefix("");
+          if (hasNextChar('-')) {
+            configurationBuilder.setPackagePrefix("");
+          } else {
+            configurationBuilder.setPackagePrefix(parsePackageNameOrEmptyString());
+          }
         }
       } else if (acceptString("flattenpackagehierarchy")) {
         if (configurationBuilder.getPackageObfuscationMode() == PackageObfuscationMode.REPACKAGE) {
@@ -308,7 +312,11 @@
             configurationBuilder.setFlattenPackagePrefix(parsePackageNameOrEmptyString());
             expectClosingQuote(quote);
           } else {
-            configurationBuilder.setFlattenPackagePrefix("");
+            if (hasNextChar('-')) {
+              configurationBuilder.setFlattenPackagePrefix("");
+            } else {
+              configurationBuilder.setFlattenPackagePrefix(parsePackageNameOrEmptyString());
+            }
           }
         }
       } else if (acceptString("overloadaggressively")) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 7dbae8a..39e5e9a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -57,7 +57,7 @@
   public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) {
     // TODO(b/122295241): These generated rules should be linked into the graph, eg, the method
     // using identified reflection should be the source keeping the target alive.
-    assert clazz.type == method.method.getHolder();
+    assert clazz.type == method.method.holder;
     ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
     builder.setOrigin(proguardCompatOrigin);
     builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
index a1aad20..c8545c7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -122,7 +122,7 @@
       }
     } else {
       assert isField();
-      result.append(field.clazz.toSourceString() + '.' + field.name);
+      result.append(field.holder.toSourceString() + '.' + field.name);
     }
     return result.toString();
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 208fb9c..e1dd681 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Descriptor;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
@@ -402,14 +401,14 @@
           targetClass.fields(
               f ->
                   liveFields.contains(f)
-                      && appView.graphLense().getOriginalFieldSignature(f.field).getHolder()
+                      && appView.graphLense().getOriginalFieldSignature(f.field).holder
                           == sourceClass.type));
       Iterables.addAll(
           filteredMembers,
           targetClass.methods(
               m ->
                   (liveMethods.contains(m) || targetedMethods.contains(m))
-                      && appView.graphLense().getOriginalMethodSignature(m.method).getHolder()
+                      && appView.graphLense().getOriginalMethodSignature(m.method).holder
                           == sourceClass.type));
 
       // If the number of member rules to hold is more than live members, we can't make it.
@@ -575,9 +574,9 @@
         }
       } else if (seed.isDexField()) {
         DexField field = seed.asDexField();
-        if (include.test(field.clazz)) {
+        if (include.test(field.holder)) {
           out.println(
-              field.clazz.toSourceString()
+              field.holder.toSourceString()
                   + ": "
                   + field.type.toSourceString()
                   + " "
@@ -906,7 +905,8 @@
     if (context instanceof ProguardKeepRule) {
       if (item.isDexEncodedMethod()) {
         DexEncodedMethod encodedMethod = item.asDexEncodedMethod();
-        if (encodedMethod.method.isLambdaDeserializeMethod(appView.dexItemFactory())) {
+        if (options.isGeneratingDex()
+            && encodedMethod.method.isLambdaDeserializeMethod(appView.dexItemFactory())) {
           // Don't keep lambda deserialization methods.
           return;
         }
@@ -915,7 +915,7 @@
         if (options.isInterfaceMethodDesugaringEnabled()
             && encodedMethod.hasCode()
             && (encodedMethod.isPrivateMethod() || encodedMethod.isStaticMember())) {
-          DexClass holder = appView.definitionFor(encodedMethod.method.getHolder());
+          DexClass holder = appView.definitionFor(encodedMethod.method.holder);
           if (holder != null && holder.isInterface()) {
             if (rule.isSpecific()) {
               options.reporter.warning(
@@ -1197,7 +1197,7 @@
           DexField field = reference.asDexField();
           DexEncodedField encodedField = appInfo.definitionFor(field);
           if (encodedField != null
-              && (encodedField.isStatic() || isKeptDirectlyOrIndirectly(field.clazz, appInfo))) {
+              && (encodedField.isStatic() || isKeptDirectlyOrIndirectly(field.holder, appInfo))) {
             // TODO(b/121354886): Enable asserts for reads and writes.
             /*assert appInfo.fieldsRead.contains(field)
                 : "Expected kept field `" + field.toSourceString() + "` to be read";
@@ -1270,9 +1270,12 @@
           requiredReferencesPerType.putIfAbsent(type, Sets.newIdentityHashSet());
         } else {
           assert reference.isDexField() || reference.isDexMethod();
-          Descriptor<?, ?> descriptor = reference.asDescriptor();
+          DexType holder =
+              reference.isDexField()
+                  ? reference.asDexField().holder
+                  : reference.asDexMethod().holder;
           requiredReferencesPerType
-              .computeIfAbsent(descriptor.getHolder(), key -> Sets.newIdentityHashSet())
+              .computeIfAbsent(holder, key -> Sets.newIdentityHashSet())
               .add(reference);
         }
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
index 42b7e21..1895a07 100644
--- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -31,7 +31,6 @@
 import com.google.common.collect.Multiset.Entry;
 import com.google.common.collect.Streams;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -192,7 +191,7 @@
     }
   }
 
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final MainDexClasses mainDexClasses;
 
   /** The equivalence that should be used for the member buckets in {@link Representative}. */
@@ -207,7 +206,7 @@
   private int numberOfMergedClasses = 0;
 
   public StaticClassMerger(
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       InternalOptions options,
       MainDexClasses mainDexClasses) {
     this.appView = appView;
@@ -259,14 +258,14 @@
     if (appView.appInfo().neverMerge.contains(clazz.type)) {
       return MergeGroup.DONT_MERGE;
     }
-    if (clazz.staticFields().length + clazz.directMethods().size() + clazz.virtualMethods().size()
+    if (clazz.staticFields().size() + clazz.directMethods().size() + clazz.virtualMethods().size()
         == 0) {
       return MergeGroup.DONT_MERGE;
     }
-    if (clazz.instanceFields().length > 0) {
+    if (clazz.instanceFields().size() > 0) {
       return MergeGroup.DONT_MERGE;
     }
-    if (Arrays.stream(clazz.staticFields())
+    if (clazz.staticFields().stream()
         .anyMatch(field -> appView.appInfo().isPinned(field.field))) {
       return MergeGroup.DONT_MERGE;
     }
@@ -284,7 +283,7 @@
                     // TODO(christofferqa): Remove the invariant that the graph lense should not
                     // modify any methods from the sets alwaysInline and noSideEffects.
                     || appView.appInfo().alwaysInline.contains(method.method)
-                    || appView.appInfo().noSideEffects.keySet().contains(method))) {
+                    || appView.appInfo().noSideEffects.keySet().contains(method.method))) {
       return MergeGroup.DONT_MERGE;
     }
     if (clazz.classInitializationMayHaveSideEffects(appView.appInfo())) {
@@ -311,6 +310,7 @@
     // Disallow interfaces from being representatives, since interface methods require desugaring.
     return !clazz.isInterface();
   }
+
   private boolean merge(DexProgramClass clazz, MergeGroup group) {
     assert satisfiesMergeCriteria(clazz) == group;
     assert group != MergeGroup.DONT_MERGE;
@@ -451,15 +451,15 @@
         .allMatch(method -> method.accessFlags.isPrivate() || method.accessFlags.isPublic())) {
       return false;
     }
-    if (!Arrays.stream(clazz.staticFields())
-        .allMatch(method -> method.accessFlags.isPrivate() || method.accessFlags.isPublic())) {
+    if (!clazz.staticFields().stream()
+        .allMatch(field -> field.accessFlags.isPrivate() || field.accessFlags.isPublic())) {
       return false;
     }
 
     // Note that a class is only considered a candidate if it has no instance fields and all of its
     // virtual methods are private. Therefore, we don't need to consider check if there are any
     // package-private or protected instance fields or virtual methods here.
-    assert Arrays.stream(clazz.instanceFields()).count() == 0;
+    assert clazz.instanceFields().size() == 0;
     assert clazz.virtualMethods().stream().allMatch(method -> method.accessFlags.isPrivate());
 
     // Check that no methods access package-private or protected members.
@@ -485,8 +485,8 @@
     }
 
     assert targetClass.accessFlags.isAtLeastAsVisibleAs(sourceClass.accessFlags);
-    assert sourceClass.instanceFields().length == 0;
-    assert targetClass.instanceFields().length == 0;
+    assert sourceClass.instanceFields().size() == 0;
+    assert targetClass.instanceFields().size() == 0;
 
     numberOfMergedClasses++;
 
@@ -534,27 +534,31 @@
   }
 
   private DexEncodedField[] mergeFields(
-      DexEncodedField[] sourceFields, DexEncodedField[] targetFields, DexProgramClass targetClass) {
-    DexEncodedField[] result = new DexEncodedField[sourceFields.length + targetFields.length];
+      List<DexEncodedField> sourceFields,
+      List<DexEncodedField> targetFields,
+      DexProgramClass targetClass) {
+    DexEncodedField[] result = new DexEncodedField[sourceFields.size() + targetFields.size()];
 
     // Move all target fields to result.
-    System.arraycopy(targetFields, 0, result, 0, targetFields.length);
+    int index = 0;
+    for (DexEncodedField targetField : targetFields) {
+      result[index++] = targetField;
+    }
 
     // Move source fields to result one by one, renaming them if needed.
     FieldSignatureEquivalence equivalence = FieldSignatureEquivalence.get();
     Set<Wrapper<DexField>> existingFields =
-        Arrays.stream(targetFields)
+        targetFields.stream()
             .map(targetField -> equivalence.wrap(targetField.field))
             .collect(Collectors.toSet());
 
     Predicate<DexField> availableFieldSignatures =
         field -> !existingFields.contains(equivalence.wrap(field));
 
-    int i = targetFields.length;
     for (DexEncodedField sourceField : sourceFields) {
       DexEncodedField sourceFieldAfterMove =
           renameFieldIfNeeded(sourceField, targetClass, availableFieldSignatures);
-      result[i++] = sourceFieldAfterMove;
+      result[index++] = sourceFieldAfterMove;
 
       DexField originalField =
           fieldMapping.inverse().getOrDefault(sourceField.field, sourceField.field);
@@ -563,6 +567,7 @@
       existingFields.add(equivalence.wrap(sourceFieldAfterMove.field));
     }
 
+    assert index == result.length;
     return result;
   }
 
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 786d026..743e48d 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -21,7 +21,6 @@
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.Sets;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -109,8 +108,14 @@
         if (reachableVirtualMethods != null) {
           clazz.setVirtualMethods(reachableVirtualMethods);
         }
-        clazz.setInstanceFields(reachableFields(clazz.instanceFields()));
-        clazz.setStaticFields(reachableFields(clazz.staticFields()));
+        DexEncodedField[] reachableInstanceFields = reachableFields(clazz.instanceFields());
+        if (reachableInstanceFields != null) {
+          clazz.setInstanceFields(reachableInstanceFields);
+        }
+        DexEncodedField[] reachableStaticFields = reachableFields(clazz.staticFields());
+        if (reachableStaticFields != null) {
+          clazz.setStaticFields(reachableStaticFields);
+        }
         clazz.removeInnerClasses(this::isAttributeReferencingPrunedType);
         clazz.removeEnclosingMethod(this::isAttributeReferencingPrunedItem);
         usagePrinter.visited();
@@ -145,7 +150,7 @@
             // Hence, removal of the current InnerClassAttribute too.
             return true;
           }
-          context = enclosingMethod.getHolder();
+          context = enclosingMethod.holder;
         }
       }
     }
@@ -221,36 +226,37 @@
         usagePrinter.printUnusedMethod(method);
       }
     }
-    return reachableMethods.toArray(new DexEncodedMethod[reachableMethods.size()]);
+    return reachableMethods.isEmpty()
+        ? DexEncodedMethod.EMPTY_ARRAY
+        : reachableMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
   }
 
-  private DexEncodedField[] reachableFields(DexEncodedField[] fields) {
+  private DexEncodedField[] reachableFields(List<DexEncodedField> fields) {
     Predicate<DexField> isReachableOrReferencedField =
         field ->
             appInfo.liveFields.contains(field)
                 || appInfo.isFieldRead(field)
                 || appInfo.isFieldWritten(field);
-    int firstUnreachable =
-        firstUnreachableIndex(Arrays.asList(fields), isReachableOrReferencedField);
+    int firstUnreachable = firstUnreachableIndex(fields, isReachableOrReferencedField);
     // Return the original array if all fields are used.
     if (firstUnreachable == -1) {
-      return fields;
+      return null;
     }
     if (Log.ENABLED) {
-      Log.debug(getClass(), "Removing field: " + fields[firstUnreachable]);
+      Log.debug(getClass(), "Removing field %s.", fields.get(firstUnreachable));
     }
-    usagePrinter.printUnusedField(fields[firstUnreachable]);
-    ArrayList<DexEncodedField> reachableOrReferencedFields = new ArrayList<>(fields.length);
+    usagePrinter.printUnusedField(fields.get(firstUnreachable));
+    ArrayList<DexEncodedField> reachableOrReferencedFields = new ArrayList<>(fields.size());
     for (int i = 0; i < firstUnreachable; i++) {
-      reachableOrReferencedFields.add(fields[i]);
+      reachableOrReferencedFields.add(fields.get(i));
     }
-    for (int i = firstUnreachable + 1; i < fields.length; i++) {
-      DexEncodedField field = fields[i];
+    for (int i = firstUnreachable + 1; i < fields.size(); i++) {
+      DexEncodedField field = fields.get(i);
       if (isReachableOrReferencedField.test(field.field)) {
         reachableOrReferencedFields.add(field);
       } else {
         if (Log.ENABLED) {
-          Log.debug(getClass(), "Removing field: " + field);
+          Log.debug(getClass(), "Removing field %s.", field.field);
         }
         usagePrinter.printUnusedField(field);
       }
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 8f8b8c0..6aa1136 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClass.FieldSetter;
 import com.android.tools.r8.graph.DexClass.MethodSetter;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -198,7 +199,7 @@
 
   private final DexApplication application;
   private final AppInfoWithLiveness appInfo;
-  private final AppView<? extends AppInfoWithLiveness> appView;
+  private final AppView<AppInfoWithLiveness> appView;
   private final ExecutorService executorService;
   private final GraphLense graphLense;
   private final MethodPoolCollection methodPoolCollection;
@@ -227,7 +228,7 @@
 
   public VerticalClassMerger(
       DexApplication application,
-      AppView<? extends AppInfoWithLiveness> appView,
+      AppView<AppInfoWithLiveness> appView,
       ExecutorService executorService,
       Timing timing,
       MainDexClasses mainDexClasses) {
@@ -312,7 +313,7 @@
       } else if (item.isDexField()) {
         // Pin the holder and the type of the field.
         DexField field = item.asDexField();
-        markTypeAsPinned(field.clazz, reason);
+        markTypeAsPinned(field.holder, reason);
         markTypeAsPinned(field.type, reason);
       } else {
         assert item.isDexMethod();
@@ -1255,11 +1256,11 @@
     }
 
     private DexEncodedField[] mergeFields(
-        DexEncodedField[] sourceFields,
-        DexEncodedField[] targetFields,
+        Collection<DexEncodedField> sourceFields,
+        Collection<DexEncodedField> targetFields,
         Predicate<DexField> availableFieldSignatures,
         Set<DexString> existingFieldNames) {
-      DexEncodedField[] result = new DexEncodedField[sourceFields.length + targetFields.length];
+      DexEncodedField[] result = new DexEncodedField[sourceFields.size() + targetFields.size()];
       // Add fields from source
       int i = 0;
       for (DexEncodedField field : sourceFields) {
@@ -1270,21 +1271,10 @@
         i++;
       }
       // Add fields from target.
-      System.arraycopy(targetFields, 0, result, i, targetFields.length);
-      return result;
-    }
-
-    private DexEncodedMethod[] mergeMethods(
-        Collection<DexEncodedMethod> sourceMethods, List<DexEncodedMethod> targetMethods) {
-      DexEncodedMethod[] result = new DexEncodedMethod[sourceMethods.size() + targetMethods.size()];
-      // Add methods from source.
-      int i = 0;
-      for (DexEncodedMethod method : sourceMethods) {
-        result[i] = method;
+      for (DexEncodedField field : targetFields) {
+        result[i] = field;
         i++;
       }
-      // Add methods from target.
-      System.arraycopy(targetMethods, 0, result, i, targetMethods.size());
       return result;
     }
 
@@ -1372,7 +1362,7 @@
     private DexEncodedField renameFieldIfNeeded(
         DexEncodedField field, Predicate<DexField> availableFieldSignatures) {
       DexString oldName = field.field.name;
-      DexType oldHolder = field.field.clazz;
+      DexType oldHolder = field.field.holder;
 
       DexField newSignature =
           application.dexItemFactory.createField(target.type, field.field.type, oldName);
@@ -1429,8 +1419,8 @@
       for (DexProgramClass clazz : appInfo.classes()) {
         fixupMethods(clazz.directMethods(), clazz::setDirectMethod);
         fixupMethods(clazz.virtualMethods(), clazz::setVirtualMethod);
-        clazz.setStaticFields(substituteTypesIn(clazz.staticFields()));
-        clazz.setInstanceFields(substituteTypesIn(clazz.instanceFields()));
+        fixupFields(clazz.staticFields(), clazz::setStaticField);
+        fixupFields(clazz.instanceFields(), clazz::setInstanceField);
       }
       for (SynthesizedBridgeCode synthesizedBridge : synthesizedBridges) {
         synthesizedBridge.updateMethodSignatures(this::fixupMethod);
@@ -1457,22 +1447,21 @@
       }
     }
 
-    private DexEncodedField[] substituteTypesIn(DexEncodedField[] fields) {
+    private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
       if (fields == null) {
-        return null;
+        return;
       }
-      for (int i = 0; i < fields.length; i++) {
-        DexEncodedField encodedField = fields[i];
+      for (int i = 0; i < fields.size(); i++) {
+        DexEncodedField encodedField = fields.get(i);
         DexField field = encodedField.field;
         DexType newType = fixupType(field.type);
-        DexType newHolder = fixupType(field.clazz);
+        DexType newHolder = fixupType(field.holder);
         DexField newField = application.dexItemFactory.createField(newHolder, newType, field.name);
         if (newField != encodedField.field) {
           lense.move(encodedField.field, newField);
-          fields[i] = encodedField.toTypeSubstitutedField(newField);
+          setter.setField(i, encodedField.toTypeSubstitutedField(newField));
         }
       }
-      return fields;
     }
 
     private DexMethod fixupMethod(DexMethod method) {
@@ -1752,9 +1741,9 @@
     private boolean checkFieldReference(DexField field) {
       if (!foundIllegalAccess) {
         DexType baseType =
-            appView.graphLense().lookupType(field.clazz.toBaseType(appView.dexItemFactory()));
+            appView.graphLense().lookupType(field.holder.toBaseType(appView.dexItemFactory()));
         if (baseType.isClassType() && baseType.isSamePackage(source.type)) {
-          checkTypeReference(field.clazz);
+          checkTypeReference(field.holder);
           checkTypeReference(field.type);
 
           DexEncodedField definition = appView.definitionFor(field);
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index c60ba34..277bf8a 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -356,9 +356,9 @@
           DexField dexField = dexEncodedField.field;
           DexField originalField = graphLense.getOriginalFieldSignature(dexField);
           DexString renamedName = namingLens.lookupName(dexField);
-          if (renamedName != originalField.name || originalField.clazz != clazz.type) {
+          if (renamedName != originalField.name || originalField.holder != clazz.type) {
             FieldSignature originalSignature =
-                FieldSignature.fromDexField(originalField, originalField.clazz != clazz.type);
+                FieldSignature.fromDexField(originalField, originalField.holder != clazz.type);
             MemberNaming memberNaming = new MemberNaming(originalSignature, renamedName.toString());
             onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
           }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 3ccdc81..2d08ac1 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1023,7 +1023,7 @@
     return Streams.stream(method.iterateInstructions(instructionSubject -> {
       if (instructionSubject.isInvoke()) {
         DexMethod invokedMethod = instructionSubject.getMethod();
-        return invokedMethod.getHolder().toString().contains(className)
+        return invokedMethod.holder.toString().contains(className)
             && invokedMethod.name.toString().contains(methodName);
       }
       return false;
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 5fccb4a..6eea205 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -9,6 +9,7 @@
 import java.nio.file.Path;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.concurrent.ExecutionException;
 
 public abstract class TestBuilder<RR extends TestRunResult, T extends TestBuilder<RR, T>> {
 
@@ -34,18 +35,20 @@
   }
 
   @Deprecated
-  public abstract RR run(String mainClass) throws IOException, CompilationFailedException;
+  public abstract RR run(String mainClass)
+      throws CompilationFailedException, ExecutionException, IOException;
 
   public abstract RR run(TestRuntime runtime, String mainClass)
-      throws IOException, CompilationFailedException;
+      throws CompilationFailedException, ExecutionException, IOException;
 
   @Deprecated
-  public RR run(Class mainClass) throws IOException, CompilationFailedException {
+  public RR run(Class<?> mainClass)
+      throws CompilationFailedException, ExecutionException, IOException {
     return run(mainClass.getTypeName());
   }
 
-  public RR run(TestRuntime runtime, Class mainClass)
-      throws IOException, CompilationFailedException {
+  public RR run(TestRuntime runtime, Class<?> mainClass)
+      throws CompilationFailedException, ExecutionException, IOException {
     return run(runtime, mainClass.getTypeName());
   }
 
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 7fcc59a..1c4d7e1 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -4,6 +4,8 @@
 package com.android.tools.r8;
 
 import static com.android.tools.r8.TestBase.Backend.DEX;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
 
 import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.TestBase.Backend;
@@ -15,6 +17,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -48,33 +51,38 @@
   protected abstract RR createRunResult(ProcessResult result);
 
   @Deprecated
-  public RR run(Class<?> mainClass) throws IOException {
+  public RR run(Class<?> mainClass) throws ExecutionException, IOException {
     return run(mainClass.getTypeName());
   }
 
   @Deprecated
-  public RR run(String mainClass) throws IOException {
+  public RR run(String mainClass) throws ExecutionException, IOException {
+    ClassSubject mainClassSubject = inspector().clazz(mainClass);
+    assertThat(mainClassSubject, isPresent());
     switch (getBackend()) {
       case DEX:
-        return runArt(null, additionalRunClassPath, mainClass);
+        return runArt(null, additionalRunClassPath, mainClassSubject.getFinalName());
       case CF:
-        return runJava(null, additionalRunClassPath, mainClass);
+        return runJava(null, additionalRunClassPath, mainClassSubject.getFinalName());
       default:
         throw new Unreachable();
     }
   }
 
-  public RR run(TestRuntime runtime, Class<?> mainClass) throws IOException {
+  public RR run(TestRuntime runtime, Class<?> mainClass) throws ExecutionException, IOException {
     return run(runtime, mainClass.getTypeName());
   }
 
-  public RR run(TestRuntime runtime, String mainClass) throws IOException {
+  public RR run(TestRuntime runtime, String mainClass) throws ExecutionException, IOException {
     assert getBackend() == runtime.getBackend();
+    ClassSubject mainClassSubject = inspector().clazz(mainClass);
+    assertThat(mainClassSubject, isPresent());
     if (runtime.isDex()) {
-      return runArt(runtime.asDex().getVm(), additionalRunClassPath, mainClass);
+      return runArt(
+          runtime.asDex().getVm(), additionalRunClassPath, mainClassSubject.getFinalName());
     }
     assert runtime.isCf();
-    return runJava(runtime, additionalRunClassPath, mainClass);
+    return runJava(runtime, additionalRunClassPath, mainClassSubject.getFinalName());
   }
 
   public CR addRunClasspathFiles(Path... classpath) {
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 8dbc309..c648668 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -14,6 +14,7 @@
 import java.io.PrintStream;
 import java.nio.file.Path;
 import java.util.Collection;
+import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
@@ -85,13 +86,14 @@
   }
 
   @Override
-  public RR run(String mainClass) throws IOException, CompilationFailedException {
+  public RR run(String mainClass)
+      throws CompilationFailedException, ExecutionException, IOException {
     return compile().run(mainClass);
   }
 
   @Override
   public RR run(TestRuntime runtime, String mainClass)
-      throws IOException, CompilationFailedException {
+      throws CompilationFailedException, ExecutionException, IOException {
     return compile().run(runtime, mainClass);
   }
 
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java
index e2f4239..f22b23e 100644
--- a/src/test/java/com/android/tools/r8/TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -83,6 +83,12 @@
     return self();
   }
 
+  public RR assertSuccessWithOutputThatMatches(Matcher<String> matcher) {
+    assertSuccess();
+    assertThat(errorMessage("Run stdout incorrect.", matcher.toString()), result.stdout, matcher);
+    return self();
+  }
+
   public CodeInspector inspector() throws IOException, ExecutionException {
     // Inspection post run implies success. If inspection of an invalid program is needed it should
     // be done on the compilation result or on the input.
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 511f328..5e3e100 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -18,7 +18,7 @@
         B extends BaseCompilerCommand.Builder<C, B>,
         CR extends TestCompileResult<CR, RR>,
         RR extends TestRunResult,
-        T extends TestCompilerBuilder<C, B, CR, RR, T>>
+        T extends TestShrinkerBuilder<C, B, CR, RR, T>>
     extends TestCompilerBuilder<C, B, CR, RR, T> {
 
   protected boolean enableMinification = true;
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 37d3914..3420ad9 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -146,7 +146,7 @@
     DexCode code = fooInCls2.getMethod().getCode().asDexCode();
     checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
     InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
-    assertEquals(absSubject.getDexClass().type, invoke.getMethod().getHolder());
+    assertEquals(absSubject.getDexClass().type, invoke.getMethod().holder);
 
     MethodSubject fooInCls1 =
         cls1Subject.method("void", "foo", ImmutableList.of("java.lang.String"));
@@ -154,7 +154,7 @@
     code = fooInCls1.getMethod().getCode().asDexCode();
     checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
     invoke = (InvokeVirtual) code.instructions[0];
-    assertEquals(absSubject.getDexClass().type, invoke.getMethod().getHolder());
+    assertEquals(absSubject.getDexClass().type, invoke.getMethod().holder);
   }
 
   /**
@@ -252,7 +252,7 @@
     DexCode code = barInCls2.getMethod().getCode().asDexCode();
     checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
     InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
-    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().getHolder());
+    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
 
     MethodSubject fooInCls1 =
         cls1Subject.method("void", "foo", ImmutableList.of("java.lang.Integer"));
@@ -260,7 +260,7 @@
     code = fooInCls1.getMethod().getCode().asDexCode();
     checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
     invoke = (InvokeVirtual) code.instructions[0];
-    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().getHolder());
+    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
   }
 
   /**
@@ -346,7 +346,7 @@
     DexCode code = barInSub.getMethod().getCode().asDexCode();
     checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
     InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
-    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().getHolder());
+    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
   }
 
   /*
@@ -420,7 +420,7 @@
     DexCode code = barInSub.getMethod().getCode().asDexCode();
     checkInstructions(code, ImmutableList.of(InvokeVirtual.class, ReturnVoid.class));
     InvokeVirtual invoke = (InvokeVirtual) code.instructions[0];
-    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().getHolder());
+    assertEquals(baseSubject.getDexClass().type, invoke.getMethod().holder);
   }
 
   private AndroidApp runAndVerifyOnJvmAndArt(
diff --git a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTest.java b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTest.java
new file mode 100644
index 0000000..4dc81f4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTest.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2019, 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.cf;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+class KeepDeserializeLambdaMethodTest {
+  static final String LAMBDA_MESSAGE = "[I'm the lambda.]";
+
+  static void invokeLambda(Object o)
+      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    Method runMethod = o.getClass().getMethod("run");
+    runMethod.invoke(o);
+  }
+}
+
+class KeepDeserializeLambdaMethodTestDex extends KeepDeserializeLambdaMethodTest {
+  public static void main(String[] args) throws Exception {
+    Serializable myLambda =
+        (Runnable & Serializable)
+            () -> System.out.println(KeepDeserializeLambdaMethodTest.LAMBDA_MESSAGE);
+    invokeLambda(myLambda);
+  }
+}
+
+class KeepDeserializeLambdaMethodTestCf extends KeepDeserializeLambdaMethodTest {
+
+  public static void main(String[] args) throws Exception {
+    Serializable myLambda = (Runnable & Serializable) () -> System.out.println(LAMBDA_MESSAGE);
+
+    byte[] bytes;
+    {
+      ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+      ObjectOutputStream out = new ObjectOutputStream(byteStream);
+      out.writeObject(myLambda);
+      out.close();
+      bytes = byteStream.toByteArray();
+    }
+    Object o;
+    {
+      ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
+      ObjectInputStream in = new ObjectInputStream(byteStream);
+      o = in.readObject();
+      in.close();
+    }
+    String name = o.getClass().getName();
+    if (!name.contains("KeepDeserializeLambdaMethodTestCf")) {
+      throw new RuntimeException("Unexpected class name " + name);
+    }
+    invokeLambda(o);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
new file mode 100644
index 0000000..c2dc7b7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/KeepDeserializeLambdaMethodTestRunner.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2019, 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.cf;
+
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeepDeserializeLambdaMethodTestRunner extends TestBase {
+
+  private static final Class TEST_CLASS_CF =
+      com.android.tools.r8.cf.KeepDeserializeLambdaMethodTestCf.class;
+  private static final Class TEST_CLASS_DEX =
+      com.android.tools.r8.cf.KeepDeserializeLambdaMethodTestDex.class;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection params() {
+    return getTestParameters().withCfRuntimes().withDexRuntime(Version.last()).build();
+  }
+
+  private final TestParameters parameters;
+
+  public KeepDeserializeLambdaMethodTestRunner(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testNoTreeShakingCf() throws Exception {
+    test(false);
+  }
+
+  @Test
+  public void testKeepRuleCf() throws Exception {
+    test(true);
+  }
+
+  private void test(boolean keepRule)
+      throws IOException, CompilationFailedException, ExecutionException {
+    Class testClass = parameters.isCfRuntime() ? TEST_CLASS_CF : TEST_CLASS_DEX;
+    R8TestBuilder builder =
+        testForR8Compat(parameters.getBackend())
+            .addProgramClasses(
+                com.android.tools.r8.cf.KeepDeserializeLambdaMethodTest.class, testClass)
+            .apply(parameters::setMinApiForRuntime)
+            .addKeepMainRule(testClass)
+            .noMinification();
+    if (keepRule) {
+      builder.addKeepRules(
+          "-keepclassmembers class * {",
+          "private static synthetic java.lang.Object "
+              + "$deserializeLambda$(java.lang.invoke.SerializedLambda);",
+          "}");
+    } else {
+      builder.noTreeShaking();
+    }
+    R8TestRunResult result =
+        builder
+            .run(parameters.getRuntime(), testClass)
+            .assertSuccessWithOutputThatMatches(
+                containsString(KeepDeserializeLambdaMethodTest.LAMBDA_MESSAGE))
+            .inspect(
+                inspector -> {
+                  MethodSubject method =
+                      inspector.clazz(testClass).uniqueMethodWithName("$deserializeLambda$");
+                  assertEquals(parameters.isCfRuntime(), method.isPresent());
+                });
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index e11b957..95e1e76 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -159,7 +159,6 @@
         null);
     MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
     DexEncodedMethod method = new DexEncodedMethod(null, flags, null, null, code);
-    new JumboStringRewriter(method, string, factory).rewrite();
-    return method.getCode().asDexCode();
+    return new JumboStringRewriter(method, string, factory).rewrite();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index df2d769..298ffe1 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -48,7 +48,6 @@
 import java.util.concurrent.ExecutorService;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class SharedClassWritingTest {
@@ -111,7 +110,6 @@
         synthesizedFrom);
   }
 
-  @Ignore("b/128281550")
   @Test
   public void manyFilesWithSharedSynthesizedClass() throws ExecutionException, IOException {
 
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 0363c3e..263a4c0 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -155,8 +155,8 @@
     DexMethod methodYOnTest =
         getMethod(inspector, "Test", "int", "y", ImmutableList.of()).method;
 
-    DexType classTestSuper = methodXOnTestSuper.getHolder();
-    DexType classTest = methodYOnTest.getHolder();
+    DexType classTestSuper = methodXOnTestSuper.holder;
+    DexType classTest = methodYOnTest.holder;
     DexProto methodXProto = methodXOnTestSuper.proto;
     DexString methodXName = methodXOnTestSuper.name;
     DexMethod methodXOnTest =
@@ -205,7 +205,7 @@
         .createField(factory.createType("LInterface;"), factory.intType, "aField");
 
     assertEquals(aFieldOnInterface,
-        appInfo.lookupStaticTarget(aFieldOnSubClass.getHolder(), aFieldOnSubClass).field);
+        appInfo.lookupStaticTarget(aFieldOnSubClass.holder, aFieldOnSubClass).field);
 
     assertEquals("42", runArt(application));
 
diff --git a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
index 9c9e785..e92e0f3 100644
--- a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
+++ b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
@@ -94,7 +94,7 @@
     assertThat(clazz, isPresent());
 
     // Redundant fields have been removed.
-    assertEquals(1, clazz.getDexClass().instanceFields().length);
-    assertEquals(1, clazz.getDexClass().staticFields().length);
+    assertEquals(1, clazz.getDexClass().instanceFields().size());
+    assertEquals(1, clazz.getDexClass().staticFields().size());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/EscapeAnalysisForNameReflectionTest.java b/src/test/java/com/android/tools/r8/ir/analysis/EscapeAnalysisForNameReflectionTest.java
index 53fbe31..caa474d 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/EscapeAnalysisForNameReflectionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/EscapeAnalysisForNameReflectionTest.java
@@ -173,7 +173,7 @@
     return getMatchingInstruction(code, instruction -> {
       if (instruction.isInvokeVirtual()) {
         DexMethod invokedMethod = instruction.asInvokeVirtual().getInvokedMethod();
-        return invokedMethod.getHolder().toDescriptorString().equals("Ljava/lang/Class;")
+        return invokedMethod.holder.toDescriptorString().equals("Ljava/lang/Class;")
             && invokedMethod.name.toString().equals("getSimpleName");
       }
       return false;
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
index c5ac021..54399f9 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/type/NullabilityTest.java
@@ -11,6 +11,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexType;
@@ -32,7 +33,6 @@
 import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
 import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -47,9 +47,9 @@
       Class<?> mainClass,
       MethodSignature signature,
       boolean npeCaught,
-      BiConsumer<AppInfoWithLiveness, IRCode> inspector)
+      BiConsumer<AppInfo, IRCode> inspector)
       throws Exception {
-    AppView<? extends AppInfoWithLiveness> appView = build(mainClass);
+    AppView<? extends AppInfo> appView = build(mainClass);
     CodeInspector codeInspector = new CodeInspector(appView.appInfo().app);
     MethodSubject fooSubject = codeInspector.clazz(mainClass.getName()).method(signature);
     DexEncodedMethod foo = codeInspector.clazz(mainClass.getName()).method(signature).getMethod();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizationTest.java
index a4f9a92..bb2fc3a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/IdempotentFunctionCallCanonicalizationTest.java
@@ -108,7 +108,7 @@
   }
 
   private static boolean isValueOf(DexMethod method, String descriptor) {
-    return method.getHolder().toDescriptorString().equals(descriptor)
+    return method.holder.toDescriptorString().equals(descriptor)
         && method.getArity() == 1
         && method.proto.returnType.toDescriptorString().equals(descriptor)
         && method.name.toString().equals("valueOf");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 2f5740c..155a1ba 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.InstancePut;
@@ -19,7 +20,6 @@
 import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
 import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterNullCheck;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableSet;
@@ -34,7 +34,7 @@
       int expectedNumberOfNonNull,
       Consumer<IRCode> testAugmentedIRCode)
       throws Exception {
-    AppView<? extends AppInfoWithLiveness> appView = build(testClass);
+    AppView<? extends AppInfo> appView = build(testClass);
     CodeInspector codeInspector = new CodeInspector(appView.appInfo().app);
     MethodSubject fooSubject = codeInspector.clazz(testClass.getName()).method(signature);
     IRCode irCode = fooSubject.buildIR();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
index e98eeb1..f21113d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTestBase.java
@@ -6,44 +6,25 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.shaking.Enqueuer;
-import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
-import com.android.tools.r8.shaking.ProguardClassFilter;
-import com.android.tools.r8.shaking.ProguardKeepRule;
-import com.android.tools.r8.shaking.RootSetBuilder;
-import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
-import java.util.concurrent.ExecutorService;
 
 public abstract class NonNullTrackerTestBase extends TestBase {
 
-  protected AppView<? extends AppInfoWithLiveness> build(Class<?> mainClass) throws Exception {
+  protected AppView<? extends AppInfo> build(Class<?> mainClass) throws Exception {
     Timing timing = new Timing(getClass().getSimpleName());
     AndroidApp app = buildAndroidApp(ToolHelper.getClassAsBytes(mainClass));
     InternalOptions options = new InternalOptions();
     DexApplication dexApplication = new ApplicationReader(app, options, timing).read().toDirect();
-    AppView<? extends AppInfoWithSubtyping> appView =
-        AppView.createForR8(new AppInfoWithSubtyping(dexApplication), options);
+    AppView<? extends AppInfo> appView =
+        AppView.createForD8(new AppInfoWithSubtyping(dexApplication), options);
     appView.setAppServices(AppServices.builder(appView).build());
-    ExecutorService executorService = ThreadUtils.getExecutorService(options);
-    RootSet rootSet =
-        new RootSetBuilder(
-                appView,
-                dexApplication,
-                ImmutableList.of(ProguardKeepRule.defaultKeepAllRule(unused -> {})),
-                options)
-            .run(executorService);
-    Enqueuer enqueuer = new Enqueuer(appView, options, null);
-    return AppView.createForR8(
-        enqueuer.traceApplication(rootSet, ProguardClassFilter.empty(), executorService, timing),
-        options);
+    return appView;
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
index ad0ae87..15e9729 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/SimplifyIfNotNullTest.java
@@ -6,49 +6,58 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.code.Format21t;
-import com.android.tools.r8.code.Format22t;
-import com.android.tools.r8.code.Instruction;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.ir.optimize.nonnull.FieldAccessTest;
 import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterArrayAccess;
 import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterFieldAccess;
 import com.android.tools.r8.ir.optimize.nonnull.NonNullAfterInvoke;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
-import java.util.Arrays;
+import com.google.common.collect.Streams;
 import java.util.List;
 import org.junit.Test;
 
 public class SimplifyIfNotNullTest extends TestBase {
-  private static boolean isIf(Instruction instruction) {
-    return instruction instanceof Format21t || instruction instanceof Format22t;
-  }
 
-  private void buildAndTest(Class<?> testClass, List<MethodSignature> signatures) throws Exception {
-    AndroidApp app = buildAndroidApp(ToolHelper.getClassAsBytes(testClass));
-    AndroidApp r8Result = compileWithR8(app,
-        "-keep class " + testClass.getCanonicalName() + " { *; }");
-    CodeInspector codeInspector = new CodeInspector(r8Result);
+  private void verifyAbsenceOfIf(
+      CodeInspector codeInspector, Class<?> testClass, List<MethodSignature> signatures) {
     for (MethodSignature signature : signatures) {
-      DexEncodedMethod method =
-          codeInspector.clazz(testClass.getName()).method(signature).getMethod();
-      long count = Arrays.stream(method.getCode().asDexCode().instructions)
-          .filter(SimplifyIfNotNullTest::isIf).count();
+      MethodSubject method =
+          codeInspector.clazz(testClass).method(signature);
+      long count = Streams.stream(method.iterateInstructions(InstructionSubject::isIf)).count();
       assertEquals(0, count);
     }
   }
 
+  private void testD8(Class<?> testClass, List<MethodSignature> signatures) throws Exception {
+    CodeInspector codeInspector =
+        testForD8()
+            .addProgramClasses(testClass)
+            .compile()
+            .inspector();
+    verifyAbsenceOfIf(codeInspector, testClass, signatures);
+  }
+
+  private void testR8(Class<?> testClass, List<MethodSignature> signatures) throws Exception {
+    CodeInspector codeInspector =
+        testForR8(Backend.DEX)
+            .addProgramClasses(testClass)
+            .addKeepRules("-keep class " + testClass.getCanonicalName() + " { *; }")
+            .compile()
+            .inspector();
+    verifyAbsenceOfIf(codeInspector, testClass, signatures);
+  }
+
   @Test
   public void nonNullAfterSafeInvokes() throws Exception {
     MethodSignature foo =
         new MethodSignature("foo", "int", new String[]{"java.lang.String"});
     MethodSignature bar =
         new MethodSignature("bar", "int", new String[]{"java.lang.String"});
-    buildAndTest(NonNullAfterInvoke.class, ImmutableList.of(foo, bar));
+    testD8(NonNullAfterInvoke.class, ImmutableList.of(foo, bar));
+    testR8(NonNullAfterInvoke.class, ImmutableList.of(foo, bar));
   }
 
   @Test
@@ -57,7 +66,8 @@
         new MethodSignature("foo", "int", new String[]{"java.lang.String[]"});
     MethodSignature bar =
         new MethodSignature("bar", "int", new String[]{"java.lang.String[]"});
-    buildAndTest(NonNullAfterArrayAccess.class, ImmutableList.of(foo, bar));
+    testD8(NonNullAfterArrayAccess.class, ImmutableList.of(foo, bar));
+    testR8(NonNullAfterArrayAccess.class, ImmutableList.of(foo, bar));
   }
 
   @Test
@@ -68,6 +78,7 @@
         new String[]{FieldAccessTest.class.getCanonicalName()});
     MethodSignature foo2 = new MethodSignature("foo2", "int",
         new String[]{FieldAccessTest.class.getCanonicalName()});
-    buildAndTest(NonNullAfterFieldAccess.class, ImmutableList.of(foo, bar, foo2));
+    testD8(NonNullAfterFieldAccess.class, ImmutableList.of(foo, bar, foo2));
+    testR8(NonNullAfterFieldAccess.class, ImmutableList.of(foo, bar, foo2));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java
index 42b0ce4..3ece2da 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ExtraMethodNullTest.java
@@ -6,16 +6,14 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
-import java.io.IOException;
 import org.junit.Test;
 
 public class ExtraMethodNullTest extends TestBase {
 
   @Test
-  public void test() throws IOException, CompilationFailedException {
+  public void test() throws Exception {
     testForR8(Backend.DEX)
         .addProgramClassesAndInnerClasses(One.class)
         .addKeepMainRule(One.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java
index 292a83b..c5c10bd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InterfaceRenewalInLoopDebugTestRunner.java
@@ -88,7 +88,7 @@
   }
 
   private static boolean isDevirtualizedCall(DexMethod method) {
-    return method.getHolder().toSourceString().equals(IMPL.getTypeName())
+    return method.holder.toSourceString().equals(IMPL.getTypeName())
         && method.getArity() == 0
         && method.proto.returnType.isVoidType()
         && method.name.toString().equals("foo");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
index ee108a7..3d1709a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
@@ -104,7 +104,7 @@
       if (instr instanceof InvokeVirtual) {
         InvokeVirtual invokeVirtual = (InvokeVirtual) instr;
         DexMethod invokedMethod = invokeVirtual.getMethod();
-        if (invokedMethod.getHolder().getName().endsWith("StringBuilder")) {
+        if (invokedMethod.holder.getName().endsWith("StringBuilder")) {
           assertNotEquals("append", invokedMethod.name.toString());
         }
       }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
index 2e6fb4c..e789292 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
@@ -100,7 +100,7 @@
       if (instr instanceof InvokeVirtual) {
         InvokeVirtual invokeVirtual = (InvokeVirtual) instr;
         DexMethod invokedMethod = invokeVirtual.getMethod();
-        if (invokedMethod.getHolder().getName().endsWith("StringBuilder")) {
+        if (invokedMethod.holder.getName().endsWith("StringBuilder")) {
           assertNotEquals("append", invokedMethod.name.toString());
         }
       }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
index f19c5b0..979d911 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetNameTestBase.java
@@ -45,7 +45,7 @@
   }
 
   static boolean isNameReflection(DexMethod method) {
-    return method.getHolder().toDescriptorString().equals(CLASS_DESCRIPTOR)
+    return method.holder.toDescriptorString().equals(CLASS_DESCRIPTOR)
         && method.getArity() == 0
         && method.proto.returnType.toDescriptorString().equals(STRING_DESCRIPTOR)
         && method.name.toString().startsWith("get")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 7b3ed4a..3f4801d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -279,11 +279,11 @@
     return Streams.concat(
         filterInstructionKind(code, SgetObject.class)
             .map(Instruction::getField)
-            .filter(fld -> isTypeOfInterest(fld.clazz))
+            .filter(fld -> isTypeOfInterest(fld.holder))
             .map(DexField::toSourceString),
         filterInstructionKind(code, SputObject.class)
             .map(Instruction::getField)
-            .filter(fld -> isTypeOfInterest(fld.clazz))
+            .filter(fld -> isTypeOfInterest(fld.holder))
             .map(DexField::toSourceString),
         filterInstructionKind(code, InvokeStatic.class)
             .map(insn -> (InvokeStatic) insn)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
index 0578b85..5e4e5f1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringContentCheckTest.java
@@ -194,7 +194,7 @@
   }
 
   private static boolean isStringContentChecker(DexMethod method) {
-    return method.getHolder().toDescriptorString().equals(STRING_DESCRIPTOR)
+    return method.holder.toDescriptorString().equals(STRING_DESCRIPTOR)
         && (method.proto.returnType.isBooleanType()
             || method.proto.returnType.isIntType()
             || method.proto.returnType.toDescriptorString().equals(STRING_DESCRIPTOR))
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java
index 88bf38c..0de9105 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringIsEmptyTest.java
@@ -79,7 +79,7 @@
   }
 
   private static boolean isStringIsEmpty(DexMethod method) {
-    return method.getHolder().toDescriptorString().equals("Ljava/lang/String;")
+    return method.holder.toDescriptorString().equals("Ljava/lang/String;")
         && method.getArity() == 0
         && method.proto.returnType.isBooleanType()
         && method.name.toString().equals("isEmpty");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
index a1709bf..2d1d0a5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringLengthTest.java
@@ -114,7 +114,7 @@
   }
 
   private static boolean isStringLength(DexMethod method) {
-    return method.getHolder().toDescriptorString().equals("Ljava/lang/String;")
+    return method.holder.toDescriptorString().equals("Ljava/lang/String;")
         && method.getArity() == 0
         && method.proto.returnType.isIntType()
         && method.name.toString().equals("length");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
index bd7bb01..60a7c1f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringToStringTest.java
@@ -78,7 +78,7 @@
   }
 
   private static boolean isStringToString(DexMethod method) {
-    return method.getHolder().toDescriptorString().equals(STRING_DESCRIPTOR)
+    return method.holder.toDescriptorString().equals(STRING_DESCRIPTOR)
         && method.getArity() == 0
         && method.proto.returnType.toDescriptorString().equals(STRING_DESCRIPTOR)
         && method.name.toString().equals("toString");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
index 42d7ab4..f8f5fa4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -148,7 +148,7 @@
   }
 
   private static boolean isStringValueOf(DexMethod method) {
-    return method.getHolder().toDescriptorString().equals(STRING_DESCRIPTOR)
+    return method.holder.toDescriptorString().equals(STRING_DESCRIPTOR)
         && method.getArity() == 1
         && method.proto.returnType.toDescriptorString().equals(STRING_DESCRIPTOR)
         && method.name.toString().equals("valueOf");
@@ -211,7 +211,7 @@
         .addProgramClasses(CLASSES)
         .run(MAIN)
         .assertSuccessWithOutput(JAVA_OUTPUT);
-    test(result, 6, 1, 1);
+    test(result, 5, 1, 1);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 30d90eb7..0df662c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -209,7 +209,7 @@
         filterInstructionKind(code, NewInstance.class)
             .map(insn -> ((NewInstance) insn).getType()),
         filterInstructionKind(code, SgetObject.class)
-            .map(insn -> insn.getField().getHolder())
+            .map(insn -> insn.getField().holder)
     )
         .filter(isTypeOfInterest)
         .map(DexType::toSourceString)
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index 0b79b3b..1af095d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -248,7 +248,7 @@
 
   private static int getLambdaSingletons(DexClass clazz) {
     assertEquals(1, clazz.interfaces.size());
-    return clazz.staticFields().length;
+    return clazz.staticFields().size();
   }
 
   private static boolean isLambdaOrGroup(DexClass clazz) {
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index 057c751..9aa9a0f 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -302,8 +302,8 @@
   }
 
   private static int countRenamedClassIdentifier(
-      CodeInspector inspector, DexEncodedField[] fields) {
-    return Arrays.stream(fields)
+      CodeInspector inspector, List<DexEncodedField> fields) {
+    return fields.stream()
         .filter(encodedField -> encodedField.getStaticValue() instanceof DexValueString)
         .reduce(0, (cnt, encodedField) -> {
           String cnstString =
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
index aa2e69d..246ea63 100644
--- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -73,11 +73,11 @@
     boolean metKotlinIntrinsicsNullChecks = false;
     while (it.hasNext()) {
       DexMethod invokedMethod = it.next().getMethod();
-      if (invokedMethod.getHolder().toSourceString().contains("java.net")) {
+      if (invokedMethod.holder.toSourceString().contains("java.net")) {
         continue;
       }
       ClassSubject invokedMethodHolderSubject =
-          inspector.clazz(invokedMethod.getHolder().toSourceString());
+          inspector.clazz(invokedMethod.holder.toSourceString());
       assertThat(invokedMethodHolderSubject, isPresent());
       assertEquals(minification, invokedMethodHolderSubject.isRenamed());
       MethodSubject invokedMethodSubject = invokedMethodHolderSubject.method(
diff --git a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
index f599f4d..9ade2f9 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
@@ -4,11 +4,9 @@
 
 package com.android.tools.r8.naming.signature;
 
-import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
-import java.io.IOException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
@@ -17,37 +15,37 @@
 public class GenericSignatureRenamingTest extends TestBase {
 
   @Test
-  public void testJVM() throws IOException, CompilationFailedException {
+  public void testJVM() throws Exception {
     testForJvm().addTestClasspath().run(Main.class).assertSuccess();
   }
 
   @Test
-  public void testR8Dex() throws IOException, CompilationFailedException {
+  public void testR8Dex() throws Exception {
     test(testForR8(Backend.DEX));
   }
 
   @Test
-  public void testR8CompatDex() throws IOException, CompilationFailedException {
+  public void testR8CompatDex() throws Exception {
     test(testForR8Compat(Backend.DEX));
   }
 
   @Test
-  public void testR8DexNoMinify() throws IOException, CompilationFailedException {
+  public void testR8DexNoMinify() throws Exception {
     test(testForR8(Backend.DEX).addKeepRules("-dontobfuscate"));
   }
 
   @Test
-  public void testR8Cf() throws IOException, CompilationFailedException {
+  public void testR8Cf() throws Exception {
     test(testForR8(Backend.CF));
   }
 
   @Test
-  public void testR8CfNoMinify() throws IOException, CompilationFailedException {
+  public void testR8CfNoMinify() throws Exception {
     test(testForR8(Backend.CF).addKeepRules("-dontobfuscate"));
   }
 
   @Test
-  public void testD8() throws IOException, CompilationFailedException {
+  public void testD8() throws Exception {
     testForD8()
         .addProgramClasses(Main.class)
         .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
@@ -58,7 +56,7 @@
         .assertSuccess();
   }
 
-  private void test(R8TestBuilder builder) throws IOException, CompilationFailedException {
+  private void test(R8TestBuilder builder) throws Exception {
     builder
         .addKeepRules("-dontoptimize")
         .addKeepRules("-keepattributes InnerClasses,EnclosingMethod,Signature")
diff --git a/src/test/java/com/android/tools/r8/proguard/configuration/RepackagingCompatibilityTest.java b/src/test/java/com/android/tools/r8/proguard/configuration/RepackagingCompatibilityTest.java
new file mode 100644
index 0000000..afaf999
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/proguard/configuration/RepackagingCompatibilityTest.java
@@ -0,0 +1,135 @@
+// Copyright (c) 2019, 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.proguard.configuration;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackagingCompatibilityTest extends TestBase {
+
+  private enum Quote {
+    SINGLE,
+    DOUBLE,
+    NONE
+  }
+
+  private static final String expectedOutput = StringUtils.lines("Hello world!");
+  private static final Class<?> mainClass = RepackagingCompatabilityTestClass.class;
+
+  private final String directive;
+  private final Quote quote;
+  private final boolean repackageToRoot;
+
+  @Parameters(name = "Directive: {0}, quote: {1}, repackage to root: {2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ImmutableList.of("-flattenpackagehierarchy", "-repackageclasses"),
+        Quote.values(),
+        BooleanUtils.values());
+  }
+
+  public RepackagingCompatibilityTest(String directive, Quote quote, boolean repackageToRoot) {
+    this.directive = directive;
+    this.quote = quote;
+    this.repackageToRoot = repackageToRoot;
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    runTest(testForR8(Backend.DEX), "R8");
+  }
+
+  @Test
+  public void testProguard() throws Exception {
+    runTest(testForProguard(), "Proguard");
+  }
+
+  private void runTest(TestShrinkerBuilder<?, ?, ?, ?, ?> builder, String shrinker)
+      throws Exception {
+    assumeFalse(
+        String.format(
+            "Only repackage to root when there are no quotes"
+                + " (repackageToRoot: %s, quote: %s, shrinker: %s)",
+            repackageToRoot, quote, shrinker),
+        repackageToRoot && quote != Quote.NONE);
+
+    TestRunResult<?> result =
+        builder
+            .addProgramClasses(mainClass)
+            .addKeepRules(getKeepRules())
+            .run(mainClass)
+            .assertSuccessWithOutput(expectedOutput);
+
+    ClassSubject testClassSubject = result.inspector().clazz(mainClass);
+    assertThat(testClassSubject, isPresent());
+    if (repackageToRoot) {
+      if (directive.equals("-flattenpackagehierarchy")) {
+        assertThat(testClassSubject.getFinalName(), startsWith("a."));
+      } else if (directive.equals("-repackageclasses")) {
+        assertThat(testClassSubject.getFinalName(), not(containsString(".")));
+      } else {
+        fail();
+      }
+    } else {
+      assertThat(testClassSubject.getFinalName(), startsWith("greeter."));
+    }
+  }
+
+  private List<String> getKeepRules() {
+    return ImmutableList.of(
+        // Keep main(), but allow obfuscation
+        "-keep,allowobfuscation class " + mainClass.getTypeName() + " {",
+        "  public static void main(...);",
+        "}",
+        // Ensure main() is not renamed
+        "-keepclassmembernames class " + mainClass.getTypeName() + " {",
+        "  public static void main(...);",
+        "}",
+        getRepackagingRule());
+  }
+
+  private String getRepackagingRule() {
+    if (repackageToRoot) {
+      return directive;
+    }
+    switch (quote) {
+      case SINGLE:
+        return directive + " 'greeter'";
+      case DOUBLE:
+        return directive + " \"greeter\"";
+      case NONE:
+        return directive + " greeter";
+      default:
+        throw new Unreachable();
+    }
+  }
+}
+
+class RepackagingCompatabilityTestClass {
+
+  public static void main(String[] args) {
+    System.out.println("Hello world!");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java b/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
new file mode 100644
index 0000000..ddc20f2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/proguard/configuration/UnusedKeepRuleTest.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2019, 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.proguard.configuration;
+
+import com.android.tools.r8.TestBase;
+import org.junit.Test;
+
+public class UnusedKeepRuleTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    testForR8(Backend.DEX)
+        .addKeepRules("-keep class NotPresent")
+        .compile()
+        // TODO(b/128494963): Should print a warning that the keep rule does not match anything.
+        .assertNoMessages();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index e5f3517..1f6a0c0 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -6,8 +6,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.PrintUsageTest.PrintUsageInspector.ClassSubject;
@@ -21,20 +19,16 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
@@ -50,9 +44,6 @@
   private final List<String> keepRulesFiles;
   private final Consumer<PrintUsageInspector> inspection;
 
-  @Rule
-  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
-
   public PrintUsageTest(
       Backend backend,
       String test,
@@ -68,33 +59,19 @@
   @Before
   public void runR8andGetPrintUsage() throws Exception {
     Path out = temp.getRoot().toPath();
-    R8Command.Builder builder =
-        ToolHelper.addProguardConfigurationConsumer(
-                R8Command.builder(),
-                pgConfig -> {
-                  pgConfig.setPrintUsage(true);
-                  pgConfig.setPrintUsageFile(out.resolve(test + PRINT_USAGE_FILE_SUFFIX));
-                })
-            .addProgramFiles(Paths.get(programFile))
-            .addProguardConfigurationFiles(ListUtils.map(keepRulesFiles, Paths::get));
-
-    if (backend == Backend.DEX) {
-      builder
-          .setOutput(out, OutputMode.DexIndexed)
-          .addLibraryFiles(ToolHelper.getDefaultAndroidJar());
-    } else {
-      builder.setOutput(out, OutputMode.ClassFile).addLibraryFiles(ToolHelper.getJava8RuntimeJar());
-    }
-    ToolHelper.runR8(
-        builder.build(),
-        options -> {
-          // Disable inlining to make this test not depend on inlining decisions.
-          options.enableInlining = false;
-        });
+    testForR8(backend)
+        .addProgramFiles(Paths.get(programFile))
+        .addKeepRuleFiles(ListUtils.map(keepRulesFiles, Paths::get))
+        .addKeepRules(
+            "-printusage " + out.resolve(test + PRINT_USAGE_FILE_SUFFIX)
+        )
+        // Disable inlining to make this test not depend on inlining decisions.
+        .addOptionsModification(o -> o.enableInlining = false)
+        .compile();
   }
 
   @Test
-  public void printUsageTest() throws IOException, ExecutionException {
+  public void printUsageTest() throws IOException {
     Path out = temp.getRoot().toPath();
     Path printUsageFile = out.resolve(test + PRINT_USAGE_FILE_SUFFIX);
     if (inspection != null) {
@@ -118,20 +95,20 @@
 
     List<Object[]> testCases = new ArrayList<>();
     for (Backend backend : Backend.values()) {
-    Set<String> usedInspections = new HashSet<>();
-    for (String test : tests) {
-      File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + test)
-          .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
-      for (File keepFile : keepFiles) {
-        String keepName = keepFile.getName();
-        Consumer<PrintUsageInspector> inspection =
-            getTestOptionalParameter(inspections, usedInspections, test, keepName);
-        if (inspection != null) {
-            testCases.add(
-                new Object[] {backend, test, ImmutableList.of(keepFile.getPath()), inspection});
+      Set<String> usedInspections = new HashSet<>();
+      for (String test : tests) {
+        File[] keepFiles = new File(ToolHelper.EXAMPLES_DIR + test)
+            .listFiles(file -> file.isFile() && file.getName().endsWith(".txt"));
+        for (File keepFile : keepFiles) {
+          String keepName = keepFile.getName();
+          Consumer<PrintUsageInspector> inspection =
+              getTestOptionalParameter(inspections, usedInspections, test, keepName);
+          if (inspection != null) {
+              testCases.add(
+                  new Object[] {backend, test, ImmutableList.of(keepFile.getPath()), inspection});
+          }
         }
       }
-    }
       assert usedInspections.size() == inspections.size();
     }
     return testCases;
@@ -152,6 +129,9 @@
   }
 
   private static void inspectShaking1(PrintUsageInspector inspector) {
+    Optional<ClassSubject> shaking1 = inspector.clazz("shaking1.Shaking");
+    assertTrue(shaking1.isPresent());
+    assertTrue(shaking1.get().method("void", "<init>", ImmutableList.of()));
     assertTrue(inspector.clazz("shaking1.Unused").isPresent());
     assertTrue(inspector.clazz("shaking1.Used").isPresent());
     ClassSubject used = inspector.clazz("shaking1.Used").get();
@@ -165,10 +145,10 @@
     assertTrue(staticFields.get().field("int", "unused"));
     Optional<ClassSubject> subClass1 = inspector.clazz("shaking2.SubClass1");
     assertTrue(subClass1.isPresent());
-    assertTrue(subClass1.get().method("void", "unusedVirtualMethod", Collections.emptyList()));
+    assertTrue(subClass1.get().method("void", "unusedVirtualMethod", ImmutableList.of()));
     Optional<ClassSubject> superClass = inspector.clazz("shaking2.SuperClass");
     assertTrue(superClass.isPresent());
-    assertTrue(superClass.get().method("void", "unusedStaticMethod", Collections.emptyList()));
+    assertTrue(superClass.get().method("void", "unusedStaticMethod", ImmutableList.of()));
   }
 
   private static void inspectShaking4(PrintUsageInspector inspector) {
@@ -188,15 +168,15 @@
     assertFalse(superClass.isPresent());
     Optional<ClassSubject> subClass = inspector.clazz("shaking9.Subclass");
     assertTrue(subClass.isPresent());
-    assertTrue(subClass.get().method("void", "aMethod", Collections.emptyList()));
-    assertFalse(subClass.get().method("void", "<init>", Collections.emptyList()));
+    assertTrue(subClass.get().method("void", "aMethod", ImmutableList.of()));
+    assertFalse(subClass.get().method("void", "<init>", ImmutableList.of()));
   }
 
   private static void inspectShaking12(PrintUsageInspector inspector) {
     assertFalse(inspector.clazz("shaking12.PeopleClass").isPresent());
     Optional<ClassSubject> animal = inspector.clazz("shaking12.AnimalClass");
     assertTrue(animal.isPresent());
-    assertTrue(animal.get().method("java.lang.String", "getName", Collections.emptyList()));
+    assertTrue(animal.get().method("java.lang.String", "getName", ImmutableList.of()));
   }
 
   static class PrintUsageInspector {
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 2171cc4..394e11a 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -453,7 +453,7 @@
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
         assertFalse(rule.getReturnValue().isNull());
-        assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
+        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
         assertEquals("int", rule.getReturnValue().getField().type.toString());
         assertEquals("X", rule.getReturnValue().getField().name.toString());
         matches |= 1 << 4;
@@ -522,7 +522,7 @@
         assertFalse(rule.getReturnValue().isValueRange());
         assertTrue(rule.getReturnValue().isField());
         assertFalse(rule.getReturnValue().isNull());
-        assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
+        assertEquals("com.google.C", rule.getReturnValue().getField().holder.toString());
         assertEquals("int", rule.getReturnValue().getField().type.toString());
         assertEquals("X", rule.getReturnValue().getField().name.toString());
         matches |= 1 << 4;
@@ -1314,7 +1314,8 @@
       parser.parse(createConfigurationForTesting(ImmutableList.of("-keepattributes xxx,")));
       fail();
     } catch (AbortException e) {
-      assertTrue(handler.errors.get(0).getDiagnosticMessage().contains("Expected list element at "));
+      assertTrue(
+          handler.errors.get(0).getDiagnosticMessage().contains("Expected list element at "));
     }
   }
 
@@ -2438,4 +2439,57 @@
     assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS.toString(), rule.typeString());
     assertEquals("a.b.c.**,!**d,!**e,!**f,g,h,i", rule.getClassNames().toString());
   }
+
+  @Test
+  public void directiveAfterRepackagingRuleTest() {
+    List<PackageObfuscationMode> packageObfuscationModes =
+        ImmutableList.of(PackageObfuscationMode.FLATTEN, PackageObfuscationMode.REPACKAGE);
+    for (PackageObfuscationMode packageObfuscationMode : packageObfuscationModes) {
+      String directive =
+          packageObfuscationMode == PackageObfuscationMode.FLATTEN
+              ? "-flattenpackagehierarchy"
+              : "-repackageclasses";
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(createConfigurationForTesting(ImmutableList.of(directive + " -keep class *")));
+      ProguardConfiguration configuration = parser.getConfig();
+      assertEquals(packageObfuscationMode, configuration.getPackageObfuscationMode());
+      assertEquals("", configuration.getPackagePrefix());
+
+      List<ProguardConfigurationRule> rules = configuration.getRules();
+      assertEquals(1, rules.size());
+
+      ProguardConfigurationRule rule = rules.get(0);
+      assertEquals(ProguardKeepRuleType.KEEP.toString(), rule.typeString());
+      assertEquals("*", rule.getClassNames().toString());
+    }
+  }
+
+  @Test
+  public void arobaseAfterRepackagingRuleTest() throws IOException {
+    Path includeFile = writeTextToTempFile("-keep class *");
+    List<PackageObfuscationMode> packageObfuscationModes =
+        ImmutableList.of(PackageObfuscationMode.FLATTEN, PackageObfuscationMode.REPACKAGE);
+    for (PackageObfuscationMode packageObfuscationMode : packageObfuscationModes) {
+      String directive =
+          packageObfuscationMode == PackageObfuscationMode.FLATTEN
+              ? "-flattenpackagehierarchy"
+              : "-repackageclasses";
+      ProguardConfigurationParser parser =
+          new ProguardConfigurationParser(new DexItemFactory(), reporter);
+      parser.parse(
+          createConfigurationForTesting(
+              ImmutableList.of(directive + " @" + includeFile.toAbsolutePath())));
+      ProguardConfiguration configuration = parser.getConfig();
+      assertEquals(packageObfuscationMode, configuration.getPackageObfuscationMode());
+      assertEquals("", configuration.getPackagePrefix());
+
+      List<ProguardConfigurationRule> rules = configuration.getRules();
+      assertEquals(1, rules.size());
+
+      ProguardConfigurationRule rule = rules.get(0);
+      assertEquals(ProguardKeepRuleType.KEEP.toString(), rule.typeString());
+      assertEquals("*", rule.getClassNames().toString());
+    }
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java
index 8f14d3b..ae7d420 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessCfInstructionSubject.java
@@ -19,7 +19,7 @@
 
   @Override
   public TypeSubject holder() {
-    return new TypeSubject(codeInspector, ((CfFieldInstruction) instruction).getField().getHolder());
+    return new TypeSubject(codeInspector, ((CfFieldInstruction) instruction).getField().holder);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java
index a3a6cff..361213a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FieldAccessDexInstructionSubject.java
@@ -19,7 +19,7 @@
 
   @Override
   public TypeSubject holder() {
-    return new TypeSubject(codeInspector, instruction.getField().getHolder());
+    return new TypeSubject(codeInspector, instruction.getField().holder);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index c7b3781..0fb0b30 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -114,12 +114,12 @@
   @Override
   public void forAllFields(Consumer<FoundFieldSubject> inspection) {
     CodeInspector.forAll(
-        Arrays.asList(dexClass.staticFields()),
+        dexClass.staticFields(),
         (dexField, clazz) -> new FoundFieldSubject(codeInspector, dexField, clazz),
         this,
         inspection);
     CodeInspector.forAll(
-        Arrays.asList(dexClass.instanceFields()),
+        dexClass.instanceFields(),
         (dexField, clazz) -> new FoundFieldSubject(codeInspector, dexField, clazz),
         this,
         inspection);
@@ -180,7 +180,7 @@
     return dexClass.accessFlags.isAnnotation();
   }
 
-  private DexEncodedField findField(DexEncodedField[] fields, DexField dexField) {
+  private DexEncodedField findField(List<DexEncodedField> fields, DexField dexField) {
     for (DexEncodedField field : fields) {
       if (field.field.equals(dexField)) {
         return field;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java
index b307112..268fdb2 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeCfInstructionSubject.java
@@ -21,7 +21,7 @@
 
   @Override
   public TypeSubject holder() {
-    return new TypeSubject(codeInspector, invokedMethod().getHolder());
+    return new TypeSubject(codeInspector, invokedMethod().holder);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java
index 7b5d41c..57a0e71 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InvokeDexInstructionSubject.java
@@ -20,7 +20,7 @@
 
   @Override
   public TypeSubject holder() {
-    return new TypeSubject(codeInspector, invokedMethod().getHolder());
+    return new TypeSubject(codeInspector, invokedMethod().holder);
   }
 
   @Override
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index 1548208..1546389 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -601,7 +601,8 @@
   if options.gradle_flags:
     args.extend(options.gradle_flags.split(' '))
 
-  stdout = utils.RunGradlew(args, env_vars=env_vars, quiet=options.quiet)
+  stdout = utils.RunGradlew(args, env_vars=env_vars, quiet=options.quiet,
+                            logging=not options.golem)
 
   apk_base_name = (archives_base_name
       + (('-' + app.flavor) if app.flavor else '') + '-release')
@@ -653,7 +654,8 @@
          '-Pandroid.enableR8.fullMode=' + str(isR8FullMode(shrinker)).lower()]
   env_vars = { 'ANDROID_SERIAL': options.emulator_id }
   stdout = \
-      utils.RunGradlew(args, env_vars=env_vars, quiet=options.quiet, fail=False)
+      utils.RunGradlew(args, env_vars=env_vars, quiet=options.quiet,
+                       fail=False, logging=not options.golem)
 
   xml_test_result_dest = os.path.join(out_dir, 'test_result')
   as_utils.MoveXMLTestResultFileTo(
diff --git a/tools/utils.py b/tools/utils.py
index 1432ea9..ec830f2 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -129,7 +129,7 @@
       print('')
       sys.stdout.write(ProgressLogger.UP)
 
-def RunCmd(cmd, env_vars=None, quiet=False, fail=True):
+def RunCmd(cmd, env_vars=None, quiet=False, fail=True, logging=True):
   PrintCmd(cmd, env=env_vars, quiet=quiet)
   env = os.environ.copy()
   if env_vars:
@@ -137,14 +137,15 @@
   process = subprocess.Popen(
       cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
   stdout = []
-  logger = ProgressLogger(quiet=quiet)
+  logger = ProgressLogger(quiet=quiet) if logging else None
   failed = False
   while True:
     line = process.stdout.readline()
     if line != b'':
       stripped = line.rstrip()
       stdout.append(stripped)
-      logger.log(stripped)
+      if logger:
+        logger.log(stripped)
 
       # TODO(christofferqa): r8 should fail with non-zero exit code.
       if ('AssertionError:' in stripped
@@ -153,7 +154,8 @@
           or 'Compilation failed' in stripped):
         failed = True
     else:
-      logger.done()
+      if logger:
+        logger.done()
       exit_code = process.poll()
       if exit_code or failed:
         for line in stdout:
@@ -165,7 +167,7 @@
 
 def RunGradlew(
     args, clean=True, stacktrace=True, use_daemon=False, env_vars=None,
-    quiet=False, fail=True):
+    quiet=False, fail=True, logging=True):
   cmd = ['./gradlew']
   if clean:
     assert 'clean' not in args
@@ -177,7 +179,7 @@
     assert '--no-daemon' not in args
     cmd.append('--no-daemon')
   cmd.extend(args)
-  return RunCmd(cmd, env_vars=env_vars, quiet=quiet, fail=fail)
+  return RunCmd(cmd, env_vars=env_vars, quiet=quiet, fail=fail, logging=logging)
 
 def IsWindows():
   return defines.IsWindows()