Merge "Revert "Disable bot check when archiving""
diff --git a/build.gradle b/build.gradle
index 6afc31a..b6b1772 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1508,7 +1508,7 @@
     if (project.hasProperty('r8lib') || project.hasProperty('r8lib_no_deps')) {
         def out = new StringBuffer()
         def err = new StringBuffer()
-        def command = "tools/retrace.py"
+        def command = "python tools/retrace.py"
         def header = "RETRACED STACKTRACE";
         if (System.getenv('BUILDBOT_BUILDERNAME') != null
                 && !System.getenv('BUILDBOT_BUILDERNAME').endsWith("_release")) {
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 5131b19..6705f0a 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -85,6 +85,7 @@
 
     builders {
       name: "archive"
+      priority: 25
       mixins: "linux"
       execution_timeout_secs: 1800  # 1/2h
       recipe {
@@ -93,6 +94,7 @@
     }
     builders {
       name: "archive_release"
+      priority: 25
       mixins: "linux"
       execution_timeout_secs: 1800  # 1/2h
       recipe {
diff --git a/infra/config/global/luci-notify.cfg b/infra/config/global/luci-notify.cfg
new file mode 100644
index 0000000..b8d8a32
--- /dev/null
+++ b/infra/config/global/luci-notify.cfg
@@ -0,0 +1,161 @@
+# Defines email notifications for builders.
+# See schema at
+# https://chromium.googlesource.com/infra/luci/luci-go/+/master/luci_notify/api/config/notify.proto
+#
+
+notifiers {
+  name: "r8-failures"
+  notifications {
+    on_change: false
+    on_success: false
+    on_failure: false
+    on_new_failure: true
+    email {
+      # Temporary, to ensure that it works.
+      recipients: "ricow@google.com"
+    }
+    # This means send to all people on the blamelist!
+    notify_blamelist {}
+  }
+  builders {
+    name: "archive"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "archive_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.0.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.0.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.4.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-4.4.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-5.1.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-5.1.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-6.0.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-6.0.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-7.0.0"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-android-7.0.0_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-jctf"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux-jctf_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "d8-linux_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.0.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.0.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.4.4"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-4.4.4_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-5.1.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-5.1.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-6.0.1"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-6.0.1_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-7.0.0"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-android-7.0.0_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-internal"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-internal_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-jctf"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "linux-jctf_release"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "r8cf-linux-jctf"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "windows"
+    bucket: "luci.r8.ci"
+  }
+  builders {
+    name: "windows_release"
+    bucket: "luci.r8.ci"
+  }
+}
+
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index 2b67476..4de5097 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -74,6 +74,9 @@
 job {
   id: "archive"
   acl_sets: "default"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.r8.ci"
@@ -403,6 +406,9 @@
 
 job {
   id: "windows"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   acl_sets: "default"
   buildbucket {
     server: "cr-buildbucket.appspot.com"
@@ -414,6 +420,9 @@
 job {
   id: "windows_release"
   acl_sets: "default"
+  triggering_policy: {
+    max_concurrent_invocations: 3
+  }
   buildbucket {
     server: "cr-buildbucket.appspot.com"
     bucket: "luci.r8.ci"
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 4755dca..8069844 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -63,6 +63,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -275,6 +276,7 @@
       RootSet rootSet;
       String proguardSeedsData = null;
       timing.begin("Strip unused code");
+      Set<DexType> classesToRetainInnerClassAttributeFor = null;
       try {
         Set<DexType> missingClasses = appView.appInfo().getMissingClasses();
         missingClasses = filterMissingClasses(
@@ -351,7 +353,14 @@
           new AbstractMethodRemover(appView.appInfo().withLiveness()).run();
         }
 
-        new AnnotationRemover(appView.appInfo().withLiveness(), appView.graphLense(), options)
+        classesToRetainInnerClassAttributeFor =
+            AnnotationRemover.computeClassesToRetainInnerClassAttributeFor(
+                appView.appInfo().withLiveness(), options);
+        new AnnotationRemover(
+                appView.appInfo().withLiveness(),
+                appView.graphLense(),
+                options,
+                classesToRetainInnerClassAttributeFor)
             .ensureValid(compatibility)
             .run();
 
@@ -530,6 +539,9 @@
       new SourceFileRewriter(appView.appInfo(), options).run();
       timing.end();
 
+      // Collect the already pruned types before creating a new app info without liveness.
+      Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
+
       if (!options.mainDexKeepRules.isEmpty()) {
         appView.setAppInfo(new AppInfoWithSubtyping(application));
         // No need to build a new main dex root set
@@ -593,7 +605,9 @@
             appViewWithLiveness.setAppInfo(
                 appViewWithLiveness
                     .appInfo()
-                    .prunedCopyFrom(application, pruner.getRemovedClasses()));
+                    .prunedCopyFrom(
+                        application,
+                        CollectionUtils.mergeSets(prunedTypes, pruner.getRemovedClasses())));
 
             // Print reasons on the application after pruning, so that we reflect the actual result.
             if (whyAreYouKeepingConsumer != null) {
@@ -603,7 +617,12 @@
               }
             }
             // Remove annotations that refer to types that no longer exist.
-            new AnnotationRemover(appView.appInfo().withLiveness(), appView.graphLense(), options)
+            assert classesToRetainInnerClassAttributeFor != null;
+            new AnnotationRemover(
+                    appView.appInfo().withLiveness(),
+                    appView.graphLense(),
+                    options,
+                    classesToRetainInnerClassAttributeFor)
                 .run();
             if (!mainDexClasses.isEmpty()) {
               // Remove types that no longer exists from the computed main dex list.
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 3d9336e..90f01b2 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -684,13 +684,11 @@
       internal.getProguardConfiguration().getKeepAttributes().lineNumberTable = true;
       internal.getProguardConfiguration().getKeepAttributes().localVariableTable = true;
       internal.getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true;
-      // TODO(zerny): Should we support inlining in debug mode? b/62937285
       internal.enableInlining = false;
       internal.enableClassInlining = false;
       internal.enableHorizontalClassMerging = false;
       internal.enableVerticalClassMerging = false;
       internal.enableClassStaticizer = false;
-      // TODO(zerny): Should we support outlining in debug mode? b/62937285
       internal.outline.enabled = false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index b0cef90..f35021b 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.9-dev";
+  public static final String LABEL = "1.5.10-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 5be17c6..1f62c88 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -162,7 +162,7 @@
         assert guard != null;
         builder
             .append(".catch ")
-            .append(guard == DexItemFactory.catchAllType ? "all" : guard.getInternalName())
+            .append(guard.getInternalName()) // Do we wan't to write 'all' here?
             .append(" from ")
             .append(getLabel(tryCatch.start))
             .append(" to ")
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index f6454f9..ad6c7d2 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -215,7 +215,9 @@
         int computedMinApiLevel = options.minApiLevel;
         for (ProgramResource input : dexSources) {
           DexReader dexReader = new DexReader(input);
-          computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+          if (options.passthroughDexCode) {
+            computedMinApiLevel = validateOrComputeMinApiLevel(computedMinApiLevel, dexReader);
+          }
           dexParsers.add(new DexParser(dexReader, classKind, itemFactory, options.reporter));
         }
         options.minApiLevel = computedMinApiLevel;
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 2042392..6abc3fd 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -174,7 +174,9 @@
             start,
             end,
             target,
-            guard == DexItemFactory.catchAllType ? null : namingLens.lookupInternalName(guard));
+            guard == options.itemFactory.throwableType
+                ? null
+                : namingLens.lookupInternalName(guard));
       }
     }
     for (LocalVariableInfo localVariable : localVariables) {
@@ -280,9 +282,7 @@
     }
     for (CfTryCatch tryCatch : tryCatchRanges) {
       for (DexType guard : tryCatch.guards) {
-        if (guard != DexItemFactory.catchAllType) {
-          registry.registerTypeReference(guard);
-        }
+        registry.registerTypeReference(guard);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 0e58ec0..035b472 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -49,6 +49,8 @@
 
 public class DexItemFactory {
 
+  public static final String throwableDescriptorString = "Ljava/lang/Throwable;";
+
   private final ConcurrentHashMap<DexString, DexString> strings = new ConcurrentHashMap<>();
   private final ConcurrentHashMap<DexString, DexType> types = new ConcurrentHashMap<>();
   private final ConcurrentHashMap<DexField, DexField> fields = new ConcurrentHashMap<>();
@@ -76,8 +78,6 @@
 
   boolean sorted = false;
 
-  public static final DexType catchAllType = new DexType(new DexString("CATCH_ALL"));
-
   // Internal type containing only the null value.
   public static final DexType nullValueType = new DexType(new DexString("NULL"));
 
@@ -86,7 +86,6 @@
   private static final IdentityHashMap<DexItem, DexItem> internalSentinels =
       new IdentityHashMap<>(
           ImmutableMap.of(
-              catchAllType, catchAllType,
               nullValueType, nullValueType,
               unknownTypeName, unknownTypeName));
 
@@ -178,7 +177,7 @@
   public final DexString methodDescriptor = createString("Ljava/lang/reflect/Method;");
   public final DexString enumDescriptor = createString("Ljava/lang/Enum;");
   public final DexString annotationDescriptor = createString("Ljava/lang/annotation/Annotation;");
-  public final DexString throwableDescriptor = createString("Ljava/lang/Throwable;");
+  public final DexString throwableDescriptor = createString(throwableDescriptorString);
   public final DexString exceptionInInitializerErrorDescriptor =
       createString("Ljava/lang/ExceptionInInitializerError;");
   public final DexString objectsDescriptor = createString("Ljava/util/Objects;");
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index fa03827..a09ba4f 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -819,7 +819,7 @@
     public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
       List<DexType> guards =
           Collections.singletonList(
-              type == null ? DexItemFactory.catchAllType : createTypeFromInternalType(type));
+              type == null ? factory.throwableType : createTypeFromInternalType(type));
       List<CfLabel> targets = Collections.singletonList(getLabel(handler));
       tryCatchRanges.add(new CfTryCatch(getLabel(start), getLabel(end), guards, targets));
     }
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 a19d070..9532404 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
@@ -191,10 +191,6 @@
       }
 
       DexType guard = catchHandler.guard;
-      if (guard == DexItemFactory.catchAllType) {
-        return AnalysisAssumption.NONE;
-      }
-
       if (exceptionalExit.isInstanceGet()
           || exceptionalExit.isInstancePut()
           || exceptionalExit.isInvokeMethodWithReceiver()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 589b650..47013dd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -854,7 +854,8 @@
       List<DexType> guards = catchHandlers.getGuards();
       int lastGuardIndex = guards.size() - 1;
       for (int i = 0; i < guards.size(); i++) {
-        assert guards.get(i) != DexItemFactory.catchAllType || i == lastGuardIndex;
+        assert !guards.get(i).toDescriptorString().equals(DexItemFactory.throwableDescriptorString)
+            || i == lastGuardIndex;
       }
       // Check that all successors except maybe the last are catch successors.
       List<Integer> sortedHandlerIndices = new ArrayList<>(catchHandlers.getAllTargets());
@@ -1533,7 +1534,7 @@
       ListIterator<BasicBlock> blockIterator,
       BasicBlock fromBlock,
       InternalOptions options) {
-    if (catchHandlers != null && catchHandlers.hasCatchAll()) {
+    if (catchHandlers != null && catchHandlers.hasCatchAll(options.itemFactory)) {
       return;
     }
     List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock);
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
index 34926fa..237c0cc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -71,9 +71,9 @@
     return uniqueTargets;
   }
 
-  public boolean hasCatchAll() {
-    return getGuards().size() > 0 &&
-        getGuards().get(getGuards().size() - 1) == DexItemFactory.catchAllType;
+  public boolean hasCatchAll(DexItemFactory factory) {
+    return getGuards().size() > 0
+        && getGuards().get(getGuards().size() - 1) == factory.throwableType;
   }
 
   public CatchHandlers<T> removeGuard(DexType guardToBeRemoved) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 2c10fbd..661d3e9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -79,7 +79,8 @@
     static TryHandlerList computeTryHandlers(
         int instructionOffset,
         List<CfTryCatch> tryCatchRanges,
-        Reference2IntMap<CfLabel> labelOffsets) {
+        Reference2IntMap<CfLabel> labelOffsets,
+        DexItemFactory factory) {
       int startOffset = Integer.MIN_VALUE;
       int endOffset = Integer.MAX_VALUE;
       List<DexType> guards = new ArrayList<>();
@@ -103,7 +104,7 @@
           if (seen.add(guard)) {
             guards.add(guard);
             offsets.add(labelOffsets.getInt(tryCatch.targets.get(i)));
-            seenCatchAll = guard == DexItemFactory.catchAllType;
+            seenCatchAll = guard == factory.throwableType;
           }
         }
         if (seenCatchAll) {
@@ -266,7 +267,7 @@
     CfInstruction instruction = code.getInstructions().get(instructionIndex);
     assert builder.isGeneratingClassFiles() == internalOutputMode.isGeneratingClassFiles();
     if (canThrowHelper(instruction)) {
-      TryHandlerList tryHandlers = getTryHandlers(instructionIndex);
+      TryHandlerList tryHandlers = getTryHandlers(instructionIndex, builder.getFactory());
       if (!tryHandlers.isEmpty()) {
         // Ensure the block starts at the start of the try-range (don't enqueue, not a target).
         builder.ensureBlockWithoutEnqueuing(tryHandlers.startOffset);
@@ -295,11 +296,11 @@
     return -1;
   }
 
-  private TryHandlerList getTryHandlers(int instructionOffset) {
+  private TryHandlerList getTryHandlers(int instructionOffset, DexItemFactory factory) {
     if (cachedTryHandlerList == null || !cachedTryHandlerList.validFor(instructionOffset)) {
       cachedTryHandlerList =
           TryHandlerList.computeTryHandlers(
-              instructionOffset, code.getTryCatchRanges(), labelOffsets);
+              instructionOffset, code.getTryCatchRanges(), labelOffsets, factory);
     }
     return cachedTryHandlerList;
   }
@@ -623,8 +624,9 @@
   }
 
   @Override
-  public CatchHandlers<Integer> getCurrentCatchHandlers() {
-    TryHandlerList tryHandlers = getTryHandlers(instructionOffset(currentInstructionIndex));
+  public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
+    TryHandlerList tryHandlers =
+        getTryHandlers(instructionOffset(currentInstructionIndex), builder.getFactory());
     if (tryHandlers.isEmpty()) {
       return null;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index ec2fe4c..6fbe1e2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -42,7 +42,6 @@
 import com.android.tools.r8.graph.DexCode.TryHandler;
 import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
 import com.android.tools.r8.graph.DexDebugEventBuilder;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Argument;
@@ -803,7 +802,7 @@
         DexType type = handlerGroup.getGuards().get(i);
         BasicBlock target = handlerGroup.getAllTargets().get(i);
         int targetOffset = getInfo(target.entry()).getOffset();
-        if (type == DexItemFactory.catchAllType) {
+        if (type == options.itemFactory.throwableType) {
           assert i == handlerGroup.getGuards().size() - 1;
           catchAllOffset = targetOffset;
         } else {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 4738403..0790018 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -53,6 +53,7 @@
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.BiConsumer;
 
 public class DexSourceCode implements SourceCode {
 
@@ -202,14 +203,14 @@
   @Override
   public void buildInstruction(
       IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
-    updateCurrentCatchHandlers(instructionIndex);
+    updateCurrentCatchHandlers(instructionIndex, builder.getFactory());
     updateDebugPosition(instructionIndex, builder);
     currentDexInstruction = code.instructions[instructionIndex];
     currentDexInstruction.buildIR(builder);
   }
 
   @Override
-  public CatchHandlers<Integer> getCurrentCatchHandlers() {
+  public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
     return currentCatchHandlers;
   }
 
@@ -246,7 +247,7 @@
     return true;
   }
 
-  private void updateCurrentCatchHandlers(int instructionIndex) {
+  private void updateCurrentCatchHandlers(int instructionIndex, DexItemFactory factory) {
     Try tryRange = getTryForOffset(instructionOffset(instructionIndex));
     if (tryRange == currentTryRange) {
       return;
@@ -255,9 +256,7 @@
     if (tryRange == null) {
       currentCatchHandlers = null;
     } else {
-      currentCatchHandlers = new CatchHandlers<>(
-          getTryHandlerGuards(tryRange),
-          getTryHandlerOffsets(tryRange));
+      currentCatchHandlers = getCurrentCatchHandlers(factory, tryRange);
     }
   }
 
@@ -391,7 +390,7 @@
         }
         builder.ensureBlockWithoutEnqueuing(tryRangeStartAddress);
         // Edge to exceptional successors.
-        for (Integer handlerOffset : getUniqueTryHandlerOffsets(tryRange)) {
+        for (Integer handlerOffset : getUniqueTryHandlerOffsets(tryRange, builder.getFactory())) {
           builder.ensureExceptionalSuccessorBlock(offset, handlerOffset);
         }
         // If the following instruction is a move-result include it in this (the invokes) block.
@@ -437,31 +436,46 @@
     return null;
   }
 
-  private Set<Integer> getUniqueTryHandlerOffsets(Try tryRange) {
-    return new HashSet<>(getTryHandlerOffsets(tryRange));
-  }
-
-  private List<Integer> getTryHandlerOffsets(Try tryRange) {
-    List<Integer> handlerOffsets = new ArrayList<>();
-    TryHandler handler = code.handlers[tryRange.handlerIndex];
-    for (TypeAddrPair pair : handler.pairs) {
-      handlerOffsets.add(pair.addr);
-    }
-    if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
-      handlerOffsets.add(handler.catchAllAddr);
-    }
-    return handlerOffsets;
-  }
-
-  private List<DexType> getTryHandlerGuards(Try tryRange) {
+  private CatchHandlers<Integer> getCurrentCatchHandlers(DexItemFactory factory, Try tryRange) {
     List<DexType> handlerGuards = new ArrayList<>();
+    List<Integer> handlerOffsets = new ArrayList<>();
+    forEachTryRange(
+        tryRange,
+        factory,
+        (type, addr) -> {
+          handlerGuards.add(type);
+          handlerOffsets.add(addr);
+        });
+    return new CatchHandlers<>(handlerGuards, handlerOffsets);
+  }
+
+  private void forEachTryRange(
+      Try tryRange, DexItemFactory factory, BiConsumer<DexType, Integer> fn) {
     TryHandler handler = code.handlers[tryRange.handlerIndex];
     for (TypeAddrPair pair : handler.pairs) {
-      handlerGuards.add(pair.type);
+      fn.accept(pair.type, pair.addr);
+      if (pair.type == factory.throwableType) {
+        return;
+      }
     }
     if (handler.catchAllAddr != TryHandler.NO_HANDLER) {
-      handlerGuards.add(DexItemFactory.catchAllType);
+      fn.accept(factory.throwableType, handler.catchAllAddr);
     }
-    return handlerGuards;
+
+  }
+
+  private Set<Integer> getUniqueTryHandlerOffsets(Try tryRange, DexItemFactory factory) {
+    return new HashSet<>(getTryHandlerOffsets(tryRange, factory));
+  }
+
+  private List<Integer> getTryHandlerOffsets(Try tryRange, DexItemFactory factory) {
+    List<Integer> handlerOffsets = new ArrayList<>();
+    forEachTryRange(
+        tryRange,
+        factory,
+        (type, addr) -> {
+          handlerOffsets.add(addr);
+        });
+    return handlerOffsets;
   }
 }
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 0f09bbc..2e22480 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
@@ -2216,7 +2216,7 @@
     offsets.put(block, freshOffset);
 
     // Copy over the exceptional successors.
-    for (int offset : source.getCurrentCatchHandlers().getUniqueTargets()) {
+    for (int offset : source.getCurrentCatchHandlers(this).getUniqueTargets()) {
       info.addExceptionalSuccessor(offset);
       BlockInfo target = targets.get(offset);
       assert !target.block.isSealed();
@@ -2252,27 +2252,25 @@
     currentBlock.add(ir);
     if (ir.instructionTypeCanThrow()) {
       assert source.verifyCurrentInstructionCanThrow();
-      CatchHandlers<Integer> catchHandlers = source.getCurrentCatchHandlers();
+      CatchHandlers<Integer> catchHandlers = source.getCurrentCatchHandlers(this);
       if (catchHandlers != null) {
         assert !throwingInstructionInCurrentBlock;
         throwingInstructionInCurrentBlock = true;
         List<BasicBlock> targets = new ArrayList<>(catchHandlers.getAllTargets().size());
         Set<BasicBlock> moveExceptionTargets = Sets.newIdentityHashSet();
-        catchHandlers.forEach((type, targetOffset) -> {
-          DexType exceptionType = type == options.itemFactory.catchAllType
-              ? options.itemFactory.throwableType
-              : type;
-          BasicBlock header = new BasicBlock();
-          header.incrementUnfilledPredecessorCount();
-          ssaWorklist.add(
-              new MoveExceptionWorklistItem(
-                  header, exceptionType, currentInstructionOffset, targetOffset));
-          targets.add(header);
-          BasicBlock target = getTarget(targetOffset);
-          if (!moveExceptionTargets.add(target)) {
-            target.incrementUnfilledPredecessorCount();
-          }
-        });
+        catchHandlers.forEach(
+            (exceptionType, targetOffset) -> {
+              BasicBlock header = new BasicBlock();
+              header.incrementUnfilledPredecessorCount();
+              ssaWorklist.add(
+                  new MoveExceptionWorklistItem(
+                      header, exceptionType, currentInstructionOffset, targetOffset));
+              targets.add(header);
+              BasicBlock target = getTarget(targetOffset);
+              if (!moveExceptionTargets.add(target)) {
+                target.incrementUnfilledPredecessorCount();
+              }
+            });
         currentBlock.linkCatchSuccessors(catchHandlers.getGuards(), targets);
       }
     }
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 0c90322..8b98f07 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
@@ -950,7 +950,6 @@
     previous = printMethod(code, "IR after null tracking (SSA)", previous);
 
     if (!isDebugMode && options.enableInlining && inliner != null) {
-      // TODO(zerny): Should we support inlining in debug mode? b/62937285
       inliner.performInlining(method, code, isProcessedConcurrently, callSiteInformation);
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 93e5355..6574551 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DexCallSite;
 import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexProto;
@@ -96,7 +95,11 @@
       this.handler = handler;
       this.start = start;
       this.end = end;
-      this.type = type;
+      this.type = type == null ? "java/lang/Throwable" : type;
+    }
+
+    boolean isCatchAll() {
+      return type.equals("java/lang/Throwable");
     }
 
     int getStart() {
@@ -617,7 +620,7 @@
   }
 
   @Override
-  public CatchHandlers<Integer> getCurrentCatchHandlers() {
+  public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
     if (generatingMethodSynchronization) {
       return null;
     }
@@ -811,7 +814,7 @@
     Set<String> seen = new HashSet<>();
     // The try-catch blocks are ordered by precedence.
     for (TryCatchBlock tryCatchBlock : getPotentialTryHandlers(insn)) {
-      if (tryCatchBlock.getType() == null) {
+      if (tryCatchBlock.isCatchAll()) {
         handlers.add(tryCatchBlock);
         return handlers;
       }
@@ -839,10 +842,10 @@
   private List<DexType> getTryHandlerGuards(List<TryCatchBlock> tryCatchBlocks) {
     List<DexType> guards = new ArrayList<>();
     for (TryCatchBlock tryCatchBlock : tryCatchBlocks) {
-      guards.add(tryCatchBlock.getType() == null
-          ? DexItemFactory.catchAllType
-          : application.getTypeFromName(tryCatchBlock.getType()));
-
+      guards.add(
+          tryCatchBlock.getType() == null
+              ? application.getFactory().throwableType
+              : application.getTypeFromName(tryCatchBlock.getType()));
     }
     return guards;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
index 9c0dccb..df2b663 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SourceCode.java
@@ -63,7 +63,7 @@
   // Helper to resolve fill-array data and build new-array instructions (dex code only).
   void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset, IRBuilder builder);
 
-  CatchHandlers<Integer> getCurrentCatchHandlers();
+  CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder);
 
   int getMoveExceptionRegister(int instructionIndex);
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 504d01c..3d45805 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -6,7 +6,6 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -180,9 +179,6 @@
     for (int i = 0; i < catchHandlers.size(); ++i) {
       DexType guard = catchHandlers.getGuards().get(i);
       BasicBlock target = catchHandlers.getAllTargets().get(i);
-      if (guard == DexItemFactory.catchAllType) {
-        continue;
-      }
 
       // We can exploit subtyping information to eliminate a catch handler if the guard is
       // subsumed by a previous guard.
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 27ec7dc..5ba7eb1 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
@@ -1453,7 +1453,7 @@
     }
 
     @Override
-    public CatchHandlers<Integer> getCurrentCatchHandlers() {
+    public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
       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 9dff882..ff330e1 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
@@ -581,8 +581,7 @@
               // target.
               return;
             }
-            if (guard != dexItemFactory.catchAllType
-                && !dexItemFactory.npeType.isSubtypeOf(guard, appView.appInfo())) {
+            if (!dexItemFactory.npeType.isSubtypeOf(guard, appView.appInfo())) {
               // TODO(christofferqa): Consider updating previous dominator tree instead of
               // rebuilding it from scratch.
               DominatorTree dominatorTree = new DominatorTree(code, MAY_HAVE_UNREACHABLE_BLOCKS);
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index 50d63e5..031ae7e 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -238,7 +238,7 @@
   }
 
   @Override
-  public final CatchHandlers<Integer> getCurrentCatchHandlers() {
+  public final CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
     return 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 d7426fc..92c07d1 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -13,7 +13,6 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
@@ -25,8 +24,6 @@
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
@@ -44,7 +41,9 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final AppInfoWithLiveness appInfo;
-  private final Reporter reporter;
+  private final ClassNamingStrategy classNamingStrategy;
+  private final PackageNamingStrategy packageNamingStrategy;
+  private final Iterable<? extends DexClass> classes;
   private final PackageObfuscationMode packageObfuscationMode;
   private final boolean isAccessModificationAllowed;
   private final Set<String> noObfuscationPrefixes = Sets.newHashSet();
@@ -62,11 +61,18 @@
 
   private final Namespace topLevelState;
 
-  ClassNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+  ClassNameMinifier(
+      AppView<AppInfoWithLiveness> appView,
+      RootSet rootSet,
+      ClassNamingStrategy classNamingStrategy,
+      PackageNamingStrategy packageNamingStrategy,
+      Iterable<? extends DexClass> classes) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
+    this.classNamingStrategy = classNamingStrategy;
+    this.packageNamingStrategy = packageNamingStrategy;
+    this.classes = classes;
     InternalOptions options = appView.options();
-    this.reporter = options.reporter;
     this.packageObfuscationMode = options.getProguardConfiguration().getPackageObfuscationMode();
     this.isAccessModificationAllowed =
         options.getProguardConfiguration().isAccessModificationAllowed();
@@ -99,8 +105,6 @@
   }
 
   ClassRenaming computeRenaming(Timing timing) {
-    // Use deterministic class order to make sure renaming is deterministic.
-    Iterable<DexProgramClass> classes = appInfo.classesWithDeterministicOrder();
     // Collect names we have to keep.
     timing.begin("reserve");
     for (DexClass clazz : classes) {
@@ -175,7 +179,7 @@
       // return type. As we don't need the class, we can rename it to anything as long as it is
       // unique.
       assert appInfo.definitionFor(type) == null;
-      renaming.put(type, topLevelState.nextTypeName());
+      renaming.put(type, topLevelState.nextTypeName(type));
     }
   }
 
@@ -250,7 +254,7 @@
     if (state == null) {
       state = getStateForClass(type);
     }
-    return state.nextTypeName();
+    return state.nextTypeName(type);
   }
 
   private Namespace getStateForClass(DexType type) {
@@ -344,12 +348,10 @@
     }
   }
 
-  private class Namespace {
+  protected class Namespace {
 
     private final String packageName;
     private final char[] packagePrefix;
-    private int typeCounter = 1;
-    private int packageCounter = 1;
     private final Iterator<String> packageDictionaryIterator;
     private final Iterator<String> classDictionaryIterator;
 
@@ -376,37 +378,37 @@
       return packageName;
     }
 
-    private String nextSuggestedNameForClass() {
+    private DexString nextSuggestedNameForClass(DexType type) {
       StringBuilder nextName = new StringBuilder();
-      if (classDictionaryIterator.hasNext()) {
+      if (!classNamingStrategy.bypassDictionary() && classDictionaryIterator.hasNext()) {
         nextName.append(packagePrefix).append(classDictionaryIterator.next()).append(';');
-        return nextName.toString();
+        return appInfo.dexItemFactory.createString(nextName.toString());
       } else {
-        return StringUtils.numberToIdentifier(packagePrefix, typeCounter++, true);
+        return classNamingStrategy.next(this, type, packagePrefix);
       }
     }
 
-    DexString nextTypeName() {
+    DexString nextTypeName(DexType type) {
       DexString candidate;
       do {
-        candidate = appInfo.dexItemFactory.createString(nextSuggestedNameForClass());
+        candidate = nextSuggestedNameForClass(type);
       } while (usedTypeNames.contains(candidate));
       usedTypeNames.add(candidate);
       return candidate;
     }
 
     private String nextSuggestedNameForSubpackage() {
-      StringBuilder nextName = new StringBuilder();
       // Note that the differences between this method and the other variant for class renaming are
       // 1) this one uses the different dictionary and counter,
       // 2) this one does not append ';' at the end, and
       // 3) this one removes 'L' at the beginning to make the return value a binary form.
-      if (packageDictionaryIterator.hasNext()) {
+      if (!packageNamingStrategy.bypassDictionary() && packageDictionaryIterator.hasNext()) {
+        StringBuilder nextName = new StringBuilder();
         nextName.append(packagePrefix).append(packageDictionaryIterator.next());
+        return nextName.toString().substring(1);
       } else {
-        nextName.append(StringUtils.numberToIdentifier(packagePrefix, packageCounter++, false));
+        return packageNamingStrategy.next(this, packagePrefix);
       }
-      return nextName.toString().substring(1);
     }
 
     String nextPackagePrefix() {
@@ -419,6 +421,20 @@
     }
   }
 
+  protected interface ClassNamingStrategy {
+
+    DexString next(Namespace namespace, DexType type, char[] packagePrefix);
+
+    boolean bypassDictionary();
+  }
+
+  protected interface PackageNamingStrategy {
+
+    String next(Namespace namespace, char[] packagePrefix);
+
+    boolean bypassDictionary();
+  }
+
   /**
    * Compute parent package prefix from the given package prefix.
    *
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 e2127dd..e419d5a 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -19,8 +19,9 @@
 
 class FieldNameMinifier extends MemberNameMinifier<DexField, DexType> {
 
-  FieldNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
-    super(appView, rootSet);
+  FieldNameMinifier(
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
+    super(appView, rootSet, strategy);
   }
 
   @Override
@@ -106,7 +107,8 @@
   private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
     DexField field = encodedField.field;
     if (!state.isReserved(field.name, field.type)) {
-      renaming.put(field, state.assignNewNameFor(field.name, field.type, useUniqueMemberNames));
+      renaming.put(
+          field, state.assignNewNameFor(field, field.name, field.type, useUniqueMemberNames));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index b643be3..6c46119 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -234,7 +234,8 @@
       sourceMethods.addAll(sourceMethodsMap.get(k));
       for (NamingState<DexProto, ?> namingState : globalStateMap.get(k)) {
         collectedStates.add(
-            new MethodNamingState(namingState, unifiedMethod.name, unifiedMethod.proto));
+            new MethodNamingState(
+                namingState, unifiedMethod, unifiedMethod.name, unifiedMethod.proto));
       }
     }
 
@@ -249,7 +250,7 @@
     }
 
     MethodNamingState originState =
-        new MethodNamingState(originStates.get(key), method.name, method.proto);
+        new MethodNamingState(originStates.get(key), method, method.name, method.proto);
     assignNameForInterfaceMethodInAllStates(collectedStates, sourceMethods, originState);
   }
 
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 53957e4..d60de88 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -5,8 +5,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CachedHashValueDexItem;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.naming.NamingState.InternalState;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
@@ -36,7 +38,8 @@
   // which is useful for debugging.
   private final BiMap<DexType, NamingState<StateType, ?>> states = HashBiMap.create();
 
-  MemberNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
+  MemberNameMinifier(
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
     this.appView = appView;
     this.appInfo = appView.appInfo();
     this.rootSet = rootSet;
@@ -45,8 +48,9 @@
     this.useUniqueMemberNames = options.getProguardConfiguration().isUseUniqueClassMemberNames();
     this.overloadAggressively =
         options.getProguardConfiguration().isOverloadAggressivelyWithoutUseUniqueClassMemberNames();
-    this.globalState = NamingState.createRoot(
-        appInfo.dexItemFactory, dictionary, getKeyTransform(), useUniqueMemberNames);
+    this.globalState =
+        NamingState.createRoot(
+            appInfo.dexItemFactory, dictionary, getKeyTransform(), strategy, useUniqueMemberNames);
   }
 
   abstract Function<StateType, ?> getKeyTransform();
@@ -88,4 +92,10 @@
       return useUniqueMemberNames;
     }
   }
+
+  interface MemberNamingStrategy {
+    DexString next(DexReference source, InternalState internalState);
+
+    boolean bypassDictionary();
+  }
 }
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 2cd8712..e97910f 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -91,9 +91,12 @@
   private final Equivalence<DexMethod> equivalence;
 
   private final FrontierState frontierState = new FrontierState();
+  private final MemberNamingStrategy strategy;
 
-  MethodNameMinifier(AppView<AppInfoWithLiveness> appView, RootSet rootSet) {
-    super(appView, rootSet);
+  MethodNameMinifier(
+      AppView<AppInfoWithLiveness> appView, RootSet rootSet, MemberNamingStrategy strategy) {
+    super(appView, rootSet, strategy);
+    this.strategy = strategy;
     equivalence =
         overloadAggressively
             ? MethodSignatureEquivalence.get()
@@ -188,7 +191,8 @@
       DexString renamedName =
           renamingAtThisLevel.computeIfAbsent(
               equivalence.wrap(method),
-              key -> state.assignNewNameFor(method.name, method.proto, useUniqueMemberNames));
+              key ->
+                  state.assignNewNameFor(method, method.name, method.proto, useUniqueMemberNames));
       renaming.put(method, renamedName);
     }
   }
@@ -233,6 +237,7 @@
                           appInfo.dexItemFactory,
                           dictionary,
                           getKeyTransform(),
+                          strategy,
                           useUniqueMemberNames)
                       : parent.createChild());
 
@@ -276,8 +281,11 @@
     private final NamingState<DexProto, ?> parent;
     private final DexString name;
     private final DexProto proto;
+    private final DexMethod method;
 
-    MethodNamingState(NamingState<DexProto, ?> parent, DexString name, DexProto proto) {
+    MethodNamingState(
+        NamingState<DexProto, ?> parent, DexMethod method, DexString name, DexProto proto) {
+      this.method = method;
       assert parent != null;
       this.parent = parent;
       this.name = name;
@@ -285,11 +293,7 @@
     }
 
     DexString assignNewName() {
-      return parent.assignNewNameFor(name, proto, false);
-    }
-
-    void reserveName() {
-      parent.reserveName(name, proto);
+      return parent.assignNewNameFor(method, name, proto, false);
     }
 
     boolean isReserved() {
diff --git a/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
new file mode 100644
index 0000000..f72efe1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/MinifiedRenaming.java
@@ -0,0 +1,186 @@
+// 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.naming;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
+import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
+import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
+import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+class MinifiedRenaming extends NamingLens {
+
+  private final AppInfo appInfo;
+  private final Map<String, String> packageRenaming;
+  private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+
+  MinifiedRenaming(
+      ClassRenaming classRenaming,
+      MethodRenaming methodRenaming,
+      FieldRenaming fieldRenaming,
+      AppInfo appInfo) {
+    this.appInfo = appInfo;
+    this.packageRenaming = classRenaming.packageRenaming;
+    renaming.putAll(classRenaming.classRenaming);
+    renaming.putAll(methodRenaming.renaming);
+    renaming.putAll(methodRenaming.callSiteRenaming);
+    renaming.putAll(fieldRenaming.renaming);
+  }
+
+  @Override
+  public String lookupPackageName(String packageName) {
+    return packageRenaming.getOrDefault(packageName, packageName);
+  }
+
+  @Override
+  public DexString lookupDescriptor(DexType type) {
+    return renaming.getOrDefault(type, type.descriptor);
+  }
+
+  @Override
+  public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
+    if (attribute.getInnerName() == null) {
+      return null;
+    }
+    // The Java reflection library assumes that that inner-class names are separated by a $ and
+    // thus we allow the mapping of an inner name to rely on that too. If the dollar is not
+    // present after pulling off the original inner-name, then we revert to using the simple name
+    // of the inner class as its name.
+    DexType innerType = attribute.getInner();
+    String inner = DescriptorUtils.descriptorToInternalName(innerType.descriptor.toString());
+    String innerName = attribute.getInnerName().toString();
+    int lengthOfPrefix = inner.length() - innerName.length();
+    if (lengthOfPrefix < 0
+        || inner.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
+        || !inner.endsWith(innerName)) {
+      return lookupSimpleName(innerType, options.itemFactory);
+    }
+
+    // At this point we assume the input was of the form: <OuterType>$<index><InnerName>
+    // Find the mapped type and if it remains the same return that, otherwise split at $.
+    String innerTypeMapped =
+        DescriptorUtils.descriptorToInternalName(lookupDescriptor(innerType).toString());
+    if (inner.equals(innerTypeMapped)) {
+      return attribute.getInnerName();
+    }
+    int index = innerTypeMapped.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
+    if (index < 0) {
+      // TODO(b/120639028): Replace this by "assert false" and remove the testing option.
+      // Hitting means we have converted a proper Outer$Inner relationship to an invalid one.
+      assert !options.testing.allowFailureOnInnerClassErrors
+          : "Outer$Inner class was remapped without keeping the dollar separator";
+      return lookupSimpleName(innerType, options.itemFactory);
+    }
+    return options.itemFactory.createString(innerTypeMapped.substring(index + 1));
+  }
+
+  @Override
+  public DexString lookupName(DexMethod method) {
+    return renaming.getOrDefault(method, method.name);
+  }
+
+  @Override
+  public DexString lookupMethodName(DexCallSite callSite) {
+    return renaming.getOrDefault(callSite, callSite.methodName);
+  }
+
+  @Override
+  public DexString lookupName(DexField field) {
+    return renaming.getOrDefault(field, field.name);
+  }
+
+  @Override
+  void forAllRenamedTypes(Consumer<DexType> consumer) {
+    DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
+        .forEach(consumer);
+  }
+
+  @Override
+  <T extends DexItem> Map<String, T> getRenamedItems(
+      Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
+    return renaming.keySet().stream()
+        .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
+        .map(clazz::cast)
+        .collect(ImmutableMap.toImmutableMap(namer, i -> i));
+  }
+
+  /**
+   * Checks whether the target is precise enough to be translated,
+   *
+   * <p>We only track the renaming of actual definitions, Thus, if we encounter a method id that
+   * does not directly point at a definition, we won't find the actual renaming. To avoid
+   * dispatching on every lookup, we assume that the tree has been fully dispatched by {@link
+   * MemberRebindingAnalysis}.
+   *
+   * <p>Library methods are excluded from this check, as those are never renamed.
+   */
+  @Override
+  public boolean checkTargetCanBeTranslated(DexMethod item) {
+    if (item.holder.isArrayType()) {
+      // Array methods are never renamed, so do not bother to check.
+      return true;
+    }
+    DexClass holder = appInfo.definitionFor(item.holder);
+    if (holder == null || holder.isLibraryClass()) {
+      return true;
+    }
+    // We don't know which invoke type this method is used for, so checks that it has been
+    // rebound either way.
+    DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item);
+    DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
+    DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
+    DexClass staticTargetHolder =
+        staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
+    DexClass directTargetHolder =
+        directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
+    DexClass virtualTargetHolder =
+        virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
+    return (directTarget == null && staticTarget == null && virtualTarget == null)
+        || (virtualTarget != null && virtualTarget.method == item)
+        || (directTarget != null && directTarget.method == item)
+        || (staticTarget != null && staticTarget.method == item)
+        || (directTargetHolder != null && directTargetHolder.isLibraryClass())
+        || (virtualTargetHolder != null && virtualTargetHolder.isLibraryClass())
+        || (staticTargetHolder != null && staticTargetHolder.isLibraryClass());
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    renaming.forEach(
+        (item, str) -> {
+          if (item instanceof DexType) {
+            builder.append("[c] ");
+          } else if (item instanceof DexMethod) {
+            builder.append("[m] ");
+          } else if (item instanceof DexField) {
+            builder.append("[f] ");
+          }
+          builder.append(item.toSourceString());
+          builder.append(" -> ");
+          builder.append(str.toSourceString());
+          builder.append('\n');
+        });
+    return builder.toString();
+  }
+}
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 be4fd39..9aa8502 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -3,34 +3,28 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.naming;
 
-import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItem;
-import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.naming.ClassNameMinifier.ClassNamingStrategy;
 import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
+import com.android.tools.r8.naming.ClassNameMinifier.Namespace;
+import com.android.tools.r8.naming.ClassNameMinifier.PackageNamingStrategy;
 import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
+import com.android.tools.r8.naming.MemberNameMinifier.MemberNamingStrategy;
 import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
-import com.android.tools.r8.optimize.MemberRebindingAnalysis;
+import com.android.tools.r8.naming.NamingState.InternalState;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableMap;
-import java.util.IdentityHashMap;
-import java.util.Map;
+import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
 import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.Predicate;
 
 public class Minifier {
 
@@ -54,7 +48,14 @@
   public NamingLens run(Timing timing) {
     assert options.enableMinification;
     timing.begin("MinifyClasses");
-    ClassNameMinifier classNameMinifier = new ClassNameMinifier(appView, rootSet);
+    ClassNameMinifier classNameMinifier =
+        new ClassNameMinifier(
+            appView,
+            rootSet,
+            new MinificationClassNamingStrategy(appInfo.dexItemFactory),
+            new MinificationPackageNamingStrategy(),
+            // Use deterministic class order to make sure renaming is deterministic.
+            appInfo.classesWithDeterministicOrder());
     ClassRenaming classRenaming = classNameMinifier.computeRenaming(timing);
     timing.end();
 
@@ -62,16 +63,19 @@
             classRenaming, MethodRenaming.empty(), FieldRenaming.empty(), appInfo)
         .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
 
+    MemberNamingStrategy minifyMembers = new MinifierMemberNamingStrategy(appView.dexItemFactory());
     timing.begin("MinifyMethods");
     MethodRenaming methodRenaming =
-        new MethodNameMinifier(appView, rootSet).computeRenaming(desugaredCallSites, timing);
+        new MethodNameMinifier(appView, rootSet, minifyMembers)
+            .computeRenaming(desugaredCallSites, timing);
     timing.end();
 
     assert new MinifiedRenaming(classRenaming, methodRenaming, FieldRenaming.empty(), appInfo)
         .verifyNoCollisions(appInfo.classes(), appInfo.dexItemFactory);
 
     timing.begin("MinifyFields");
-    FieldRenaming fieldRenaming = new FieldNameMinifier(appView, rootSet).computeRenaming(timing);
+    FieldRenaming fieldRenaming =
+        new FieldNameMinifier(appView, rootSet, minifyMembers).computeRenaming(timing);
     timing.end();
 
     NamingLens lens = new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo);
@@ -84,159 +88,73 @@
     return lens;
   }
 
-  private static class MinifiedRenaming extends NamingLens {
+  static class MinificationClassNamingStrategy implements ClassNamingStrategy {
 
-    private final AppInfo appInfo;
-    private final Map<String, String> packageRenaming;
-    private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
+    private final DexItemFactory factory;
+    private final Object2IntMap<Namespace> namespaceCounters = new Object2IntLinkedOpenHashMap<>();
 
-    private MinifiedRenaming(
-        ClassRenaming classRenaming,
-        MethodRenaming methodRenaming,
-        FieldRenaming fieldRenaming,
-        AppInfo appInfo) {
-      this.appInfo = appInfo;
-      this.packageRenaming = classRenaming.packageRenaming;
-      renaming.putAll(classRenaming.classRenaming);
-      renaming.putAll(methodRenaming.renaming);
-      renaming.putAll(methodRenaming.callSiteRenaming);
-      renaming.putAll(fieldRenaming.renaming);
+    MinificationClassNamingStrategy(DexItemFactory factory) {
+      this.factory = factory;
+      namespaceCounters.defaultReturnValue(1);
     }
 
     @Override
-    public String lookupPackageName(String packageName) {
-      return packageRenaming.getOrDefault(packageName, packageName);
+    public DexString next(Namespace namespace, DexType type, char[] packagePrefix) {
+      int counter = namespaceCounters.put(namespace, namespaceCounters.getInt(namespace) + 1);
+      DexString string =
+          factory.createString(StringUtils.numberToIdentifier(packagePrefix, counter, true));
+      return string;
     }
 
     @Override
-    public DexString lookupDescriptor(DexType type) {
-      return renaming.getOrDefault(type, type.descriptor);
+    public boolean bypassDictionary() {
+      return false;
+    }
+  }
+
+  static class MinificationPackageNamingStrategy implements PackageNamingStrategy {
+
+    private final Object2IntMap<Namespace> namespaceCounters = new Object2IntLinkedOpenHashMap<>();
+
+    public MinificationPackageNamingStrategy() {
+      namespaceCounters.defaultReturnValue(1);
     }
 
     @Override
-    public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
-      if (attribute.getInnerName() == null) {
-        return null;
-      }
-      // The Java reflection library assumes that that inner-class names are separated by a $ and
-      // thus we allow the mapping of an inner name to rely on that too. If the dollar is not
-      // present after pulling off the original inner-name, then we revert to using the simple name
-      // of the inner class as its name.
-      DexType innerType = attribute.getInner();
-      String inner = DescriptorUtils.descriptorToInternalName(innerType.descriptor.toString());
-      String innerName = attribute.getInnerName().toString();
-      int lengthOfPrefix = inner.length() - innerName.length();
-      if (lengthOfPrefix < 0
-          || inner.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR, lengthOfPrefix - 1) < 0
-          || !inner.endsWith(innerName)) {
-        return lookupSimpleName(innerType, options.itemFactory);
-      }
-
-      // At this point we assume the input was of the form: <OuterType>$<index><InnerName>
-      // Find the mapped type and if it remains the same return that, otherwise split at $.
-      String innerTypeMapped =
-          DescriptorUtils.descriptorToInternalName(lookupDescriptor(innerType).toString());
-      if (inner.equals(innerTypeMapped)) {
-        return attribute.getInnerName();
-      }
-      int index = innerTypeMapped.lastIndexOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
-      if (index < 0) {
-        // TODO(b/120639028): Replace this by "assert false" and remove the testing option.
-        // Hitting means we have converted a proper Outer$Inner relationship to an invalid one.
-        assert !options.testing.allowFailureOnInnerClassErrors
-            : "Outer$Inner class was remapped without keeping the dollar separator";
-        return lookupSimpleName(innerType, options.itemFactory);
-      }
-      return options.itemFactory.createString(innerTypeMapped.substring(index + 1));
+    public String next(Namespace namespace, char[] packagePrefix) {
+      // Note that the differences between this method and the other variant for class renaming are
+      // 1) this one uses the different dictionary and counter,
+      // 2) this one does not append ';' at the end, and
+      // 3) this one removes 'L' at the beginning to make the return value a binary form.
+      int counter = namespaceCounters.put(namespace, namespaceCounters.getInt(namespace) + 1);
+      return StringUtils.numberToIdentifier(packagePrefix, counter, false).substring(1);
     }
 
     @Override
-    public DexString lookupName(DexMethod method) {
-      return renaming.getOrDefault(method, method.name);
+    public boolean bypassDictionary() {
+      return false;
+    }
+  }
+
+  static class MinifierMemberNamingStrategy implements MemberNamingStrategy {
+
+    char[] EMPTY_CHAR_ARRAY = new char[0];
+
+    private final DexItemFactory factory;
+
+    public MinifierMemberNamingStrategy(DexItemFactory factory) {
+      this.factory = factory;
     }
 
     @Override
-    public DexString lookupMethodName(DexCallSite callSite) {
-      return renaming.getOrDefault(callSite, callSite.methodName);
+    public DexString next(DexReference dexReference, InternalState internalState) {
+      int counter = internalState.incrementAndGet();
+      return factory.createString(StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, counter, false));
     }
 
     @Override
-    public DexString lookupName(DexField field) {
-      return renaming.getOrDefault(field, field.name);
-    }
-
-    @Override
-    void forAllRenamedTypes(Consumer<DexType> consumer) {
-      DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
-          .forEach(consumer);
-    }
-
-    @Override
-    <T extends DexItem> Map<String, T> getRenamedItems(
-        Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
-      return renaming.keySet().stream()
-          .filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
-          .map(clazz::cast)
-          .collect(ImmutableMap.toImmutableMap(namer, i -> i));
-    }
-
-    /**
-     * Checks whether the target is precise enough to be translated,
-     * <p>
-     * We only track the renaming of actual definitions, Thus, if we encounter a method id that
-     * does not directly point at a definition, we won't find the actual renaming. To avoid
-     * dispatching on every lookup, we assume that the tree has been fully dispatched by
-     * {@link MemberRebindingAnalysis}.
-     * <p>
-     * Library methods are excluded from this check, as those are never renamed.
-     */
-    @Override
-    public boolean checkTargetCanBeTranslated(DexMethod item) {
-      if (item.holder.isArrayType()) {
-        // Array methods are never renamed, so do not bother to check.
-        return true;
-      }
-      DexClass holder = appInfo.definitionFor(item.holder);
-      if (holder == null || holder.isLibraryClass()) {
-        return true;
-      }
-      // We don't know which invoke type this method is used for, so checks that it has been
-      // rebound either way.
-      DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item);
-      DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item);
-      DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item);
-      DexClass staticTargetHolder =
-          staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null;
-      DexClass directTargetHolder =
-          directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null;
-      DexClass virtualTargetHolder =
-          virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null;
-      return (directTarget == null && staticTarget == null && virtualTarget == null)
-          || (virtualTarget != null && virtualTarget.method == item)
-          || (directTarget != null && directTarget.method == item)
-          || (staticTarget != null && staticTarget.method == item)
-          || (directTargetHolder != null && directTargetHolder.isLibraryClass())
-          || (virtualTargetHolder != null && virtualTargetHolder.isLibraryClass())
-          || (staticTargetHolder != null && staticTargetHolder.isLibraryClass());
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder builder = new StringBuilder();
-      renaming.forEach((item, str) -> {
-        if (item instanceof DexType) {
-          builder.append("[c] ");
-        } else if (item instanceof DexMethod) {
-          builder.append("[m] ");
-        } else if (item instanceof DexField) {
-          builder.append("[f] ");
-        }
-        builder.append(item.toSourceString());
-        builder.append(" -> ");
-        builder.append(str.toSourceString());
-        builder.append('\n');
-      });
-      return builder.toString();
+    public boolean bypassDictionary() {
+      return false;
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index d414b3b..a3352ad 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -5,8 +5,9 @@
 
 import com.android.tools.r8.graph.CachedHashValueDexItem;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.naming.MemberNameMinifier.MemberNamingStrategy;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
@@ -27,14 +28,17 @@
   private final DexItemFactory itemFactory;
   private final List<String> dictionary;
   private final Function<ProtoType, KeyType> keyTransform;
+  private final MemberNamingStrategy strategy;
   private final boolean useUniqueMemberNames;
 
   static <S, T extends CachedHashValueDexItem> NamingState<T, S> createRoot(
       DexItemFactory itemFactory,
       List<String> dictionary,
       Function<T, S> keyTransform,
+      MemberNamingStrategy strategy,
       boolean useUniqueMemberNames) {
-    return new NamingState<>(null, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
+    return new NamingState<>(
+        null, itemFactory, dictionary, keyTransform, strategy, useUniqueMemberNames);
   }
 
   private NamingState(
@@ -42,16 +46,19 @@
       DexItemFactory itemFactory,
       List<String> dictionary,
       Function<ProtoType, KeyType> keyTransform,
+      MemberNamingStrategy strategy,
       boolean useUniqueMemberNames) {
     this.parent = parent;
     this.itemFactory = itemFactory;
     this.dictionary = dictionary;
     this.keyTransform = keyTransform;
+    this.strategy = strategy;
     this.useUniqueMemberNames = useUniqueMemberNames;
   }
 
   public NamingState<ProtoType, KeyType> createChild() {
-    return new NamingState<>(this, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
+    return new NamingState<>(
+        this, itemFactory, dictionary, keyTransform, strategy, useUniqueMemberNames);
   }
 
   private InternalState findInternalStateFor(KeyType key) {
@@ -85,12 +92,13 @@
     return state.getAssignedNameFor(name, key);
   }
 
-  public DexString assignNewNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
+  public DexString assignNewNameFor(
+      DexReference source, DexString original, ProtoType proto, boolean markAsUsed) {
     KeyType key = keyTransform.apply(proto);
     DexString result = getAssignedNameFor(original, key);
     if (result == null) {
       InternalState state = getOrCreateInternalStateFor(key);
-      result = state.getNameFor(original, key, markAsUsed);
+      result = state.getNameFor(source, original, key, markAsUsed);
     }
     return result;
   }
@@ -146,7 +154,6 @@
   class InternalState {
 
     private static final int INITIAL_NAME_COUNT = 1;
-    private final char[] EMPTY_CHAR_ARRAY = new char[0];
 
     protected final DexItemFactory itemFactory;
     private final InternalState parentInternalState;
@@ -193,6 +200,10 @@
       reservedNames.add(name);
     }
 
+    public int incrementAndGet() {
+      return nameCount++;
+    }
+
     DexString getAssignedNameFor(DexString original, KeyType proto) {
       DexString result = null;
       if (renamings != null) {
@@ -215,13 +226,14 @@
       return result;
     }
 
-    DexString getNameFor(DexString original, KeyType proto, boolean markAsUsed) {
+    DexString getNameFor(
+        DexReference source, DexString original, KeyType proto, boolean markAsUsed) {
       DexString name = getAssignedNameFor(original, proto);
       if (name != null) {
         return name;
       }
       do {
-        name = itemFactory.createString(nextSuggestedName());
+        name = nextSuggestedName(source);
       } while (!isAvailable(name));
       if (markAsUsed) {
         addRenaming(original, proto, name);
@@ -236,11 +248,11 @@
       renamings.put(original, proto, newName);
     }
 
-    String nextSuggestedName() {
-      if (dictionaryIterator.hasNext()) {
-        return dictionaryIterator.next();
+    DexString nextSuggestedName(DexReference source) {
+      if (!strategy.bypassDictionary() && dictionaryIterator.hasNext()) {
+        return itemFactory.createString(dictionaryIterator.next());
       } else {
-        return StringUtils.numberToIdentifier(EMPTY_CHAR_ARRAY, nameCount++, false);
+        return strategy.next(source, this);
       }
     }
 
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 e55fc6e..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
@@ -161,6 +161,9 @@
     public DexType parsedTypeName(String name) {
       DexType type = appInfo.dexItemFactory.createType(getDescriptorFromClassBinaryName(name));
       type = appView.graphLense().lookupType(type);
+      if (appInfo.wasPruned(type)) {
+        type = appInfo.dexItemFactory.objectType;
+      }
       DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
       renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
       return type;
@@ -185,8 +188,16 @@
         // Pick the renamed inner class from the fully renamed binary name.
         String fullRenamedBinaryName =
             getClassBinaryNameFromDescriptor(renamedDescriptor.toString());
-        renamedSignature.append(
-            fullRenamedBinaryName.substring(enclosingRenamedBinaryName.length() + 1));
+        int innerClassPos = enclosingRenamedBinaryName.length() + 1;
+        if (innerClassPos < fullRenamedBinaryName.length()) {
+          renamedSignature.append(fullRenamedBinaryName.substring(innerClassPos));
+        } else {
+          reporter.warning(
+              new StringDiagnostic(
+                  "Should have retained InnerClasses attribute of " + type + ".",
+                  appView.appInfo().originFor(type)));
+          renamedSignature.append(name);
+        }
       } else {
         // Did not find the class - keep the inner class name as is.
         // TODO(110085899): Warn about missing classes in signatures?
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 3fa9db5..25e0832 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.shaking;
 
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexAnnotationElement;
@@ -18,7 +19,12 @@
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.IdentityHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class AnnotationRemover {
 
@@ -26,12 +32,18 @@
   private final GraphLense lense;
   private final ProguardKeepAttributes keep;
   private final InternalOptions options;
+  private final Set<DexType> classesToRetainInnerClassAttributeFor;
 
-  public AnnotationRemover(AppInfoWithLiveness appInfo, GraphLense lense, InternalOptions options) {
+  public AnnotationRemover(
+      AppInfoWithLiveness appInfo,
+      GraphLense lense,
+      InternalOptions options,
+      Set<DexType> classesToRetainInnerClassAttributeFor) {
     this.appInfo = appInfo;
     this.lense = lense;
     this.keep = options.getProguardConfiguration().getKeepAttributes();
     this.options = options;
+    this.classesToRetainInnerClassAttributeFor = classesToRetainInnerClassAttributeFor;
   }
 
   /**
@@ -140,6 +152,79 @@
     return this;
   }
 
+  private static boolean hasGenericEnclosingClass(
+      DexProgramClass clazz,
+      Map<DexType, DexProgramClass> enclosingClasses,
+      Set<DexProgramClass> genericClasses) {
+    while (true) {
+      DexProgramClass enclosingClass = enclosingClasses.get(clazz.type);
+      if (enclosingClass == null) {
+        return false;
+      }
+      if (genericClasses.contains(enclosingClass)) {
+        return true;
+      }
+      clazz = enclosingClass;
+    }
+  }
+
+  private static boolean hasSignatureAnnotation(DexProgramClass clazz, DexItemFactory itemFactory) {
+    for (DexAnnotation annotation : clazz.annotations.annotations) {
+      if (DexAnnotation.isSignatureAnnotation(annotation, itemFactory)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static Set<DexType> computeClassesToRetainInnerClassAttributeFor(
+      AppInfoWithLiveness appInfo, InternalOptions options) {
+    // In case of minification for certain inner classes we need to retain their InnerClass
+    // attributes because their minified name still needs to be in hierarchical format
+    // (enclosing$inner) otherwise the GenericSignatureRewriter can't produce the correct,
+    // renamed signature.
+
+    // More precisely:
+    // - we're going to retain the InnerClass attribute that refers to the same class as 'inner'
+    // - for live, inner, nonstatic classes
+    // - that are enclosed by a class with a generic signature.
+
+    // In compat mode we always keep all InnerClass attributes (if requested).
+    // If not requested we never keep any. In these cases don't compute eligible classes.
+    if (options.forceProguardCompatibility
+        || !options.getProguardConfiguration().getKeepAttributes().innerClasses) {
+      return Collections.emptySet();
+    }
+
+    // Build lookup table and set of the interesting classes.
+    // enclosingClasses.get(clazz) gives the enclosing class of 'clazz'
+    Map<DexType, DexProgramClass> enclosingClasses = new IdentityHashMap<>();
+    Set<DexProgramClass> genericClasses = Sets.newIdentityHashSet();
+
+    Iterable<DexProgramClass> programClasses = appInfo.classes();
+    for (DexProgramClass clazz : programClasses) {
+      if (hasSignatureAnnotation(clazz, options.itemFactory)) {
+        genericClasses.add(clazz);
+      }
+      for (InnerClassAttribute innerClassAttribute : clazz.getInnerClasses()) {
+        if ((innerClassAttribute.getAccess() & Constants.ACC_STATIC) == 0
+            && innerClassAttribute.getOuter() == clazz.type) {
+          enclosingClasses.put(innerClassAttribute.getInner(), clazz);
+        }
+      }
+    }
+
+    Set<DexType> result = Sets.newIdentityHashSet();
+    for (DexProgramClass clazz : programClasses) {
+      if (clazz.getInnerClassAttributeForThisClass() != null
+          && appInfo.liveTypes.contains(clazz.type)
+          && hasGenericEnclosingClass(clazz, enclosingClasses, genericClasses)) {
+        result.add(clazz.type);
+      }
+    }
+    return result;
+  }
+
   public void run() {
     for (DexProgramClass clazz : appInfo.classes()) {
       stripAttributes(clazz);
@@ -208,6 +293,15 @@
     return false;
   }
 
+  private static boolean hasInnerClassesFromSet(DexProgramClass clazz, Set<DexType> innerClasses) {
+    for (InnerClassAttribute attr : clazz.getInnerClasses()) {
+      if (attr.getOuter() == clazz.type && innerClasses.contains(attr.getInner())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   private void stripAttributes(DexProgramClass clazz) {
     // If [clazz] is mentioned by a keep rule, it could be used for reflection, and we therefore
     // need to keep the enclosing method and inner classes attributes, if requested. In Proguard
@@ -215,15 +309,40 @@
     // To ensure reflection from both inner to outer and and outer to inner for kept classes - even
     // if only one side is kept - keep the attributes is any class mentioned in these attributes
     // is kept.
-    if (appInfo.isPinned(clazz.type)
-        || enclosingMethodPinned(clazz)
-        || innerClassPinned(clazz)
-        || options.forceProguardCompatibility) {
+    boolean keptAnyway =
+        appInfo.isPinned(clazz.type)
+            || enclosingMethodPinned(clazz)
+            || innerClassPinned(clazz)
+            || options.forceProguardCompatibility;
+    boolean keepForThisInnerClass = false;
+    boolean keepForThisEnclosingClass = false;
+    if (!keptAnyway) {
+      keepForThisInnerClass = classesToRetainInnerClassAttributeFor.contains(clazz.type);
+      keepForThisEnclosingClass =
+          hasInnerClassesFromSet(clazz, classesToRetainInnerClassAttributeFor);
+    }
+    if (keptAnyway || keepForThisInnerClass || keepForThisEnclosingClass) {
       if (!keep.enclosingMethod) {
         clazz.clearEnclosingMethod();
       }
       if (!keep.innerClasses) {
         clazz.clearInnerClasses();
+      } else if (!keptAnyway) {
+        // We're keeping this only because of classesToRetainInnerClassAttributeFor.
+        final boolean finalKeepForThisInnerClass = keepForThisInnerClass;
+        final boolean finalKeepForThisEnclosingClass = keepForThisEnclosingClass;
+        clazz.removeInnerClasses(
+            ica -> {
+              if (finalKeepForThisInnerClass && ica.getInner() == clazz.type) {
+                return false;
+              }
+              if (finalKeepForThisEnclosingClass
+                  && ica.getOuter() == clazz.type
+                  && classesToRetainInnerClassAttributeFor.contains(ica.getInner())) {
+                return false;
+              }
+              return true;
+            });
       }
     } else {
       // These attributes are only relevant for reflection, and this class is not used for
@@ -232,5 +351,4 @@
       clazz.clearInnerClasses();
     }
   }
-
 }
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 07023b5..3fbb72c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -60,6 +60,7 @@
 import com.android.tools.r8.shaking.RootSetBuilder.ConsequentRootSet;
 import com.android.tools.r8.shaking.RootSetBuilder.IfRuleEvaluator;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
+import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.Timing;
@@ -392,8 +393,8 @@
 
   private void enqueueFirstNonSerializableClassInitializer(DexClass clazz, KeepReason reason) {
     assert clazz.isProgramClass() && clazz.isSerializable(appInfo);
-    // Clime up the class hierarchy. Break out if the definition is not found, or hit the library
-    // classes, which are kept by definition, or encounter the first non-serializable class.
+    // Climb up the class hierarchy. Break out if the definition is not found, or hit the library
+    // classes which are kept by definition, or encounter the first non-serializable class.
     while (clazz != null && clazz.isProgramClass() && clazz.isSerializable(appInfo)) {
       clazz = appInfo.definitionFor(clazz.superType);
     }
@@ -2255,7 +2256,7 @@
       this.prunedTypes =
           removedClasses == null
               ? previous.prunedTypes
-              : mergeSets(previous.prunedTypes, removedClasses);
+              : CollectionUtils.mergeSets(previous.prunedTypes, removedClasses);
       this.switchMaps = previous.switchMaps;
       this.ordinalsMaps = previous.ordinalsMaps;
       assert Sets.intersection(instanceFieldReads.keySet(), staticFieldReads.keySet()).isEmpty();
@@ -2310,7 +2311,8 @@
       // after second tree shaking.
       this.callSites = previous.callSites;
       this.brokenSuperInvokes = lense.rewriteMethodsConservatively(previous.brokenSuperInvokes);
-      this.prunedTypes = rewriteItems(previous.prunedTypes, lense::lookupType);
+      // Don't rewrite pruned types - the removed types are identified by their original name.
+      this.prunedTypes = previous.prunedTypes;
       this.mayHaveSideEffects =
           rewriteReferenceKeys(previous.mayHaveSideEffects, lense::lookupReference);
       this.noSideEffects = rewriteReferenceKeys(previous.noSideEffects, lense::lookupReference);
@@ -2561,13 +2563,6 @@
       return instantiatedLambdas.contains(type);
     }
 
-    private static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
-      ImmutableSet.Builder<T> builder = ImmutableSet.builder();
-      builder.addAll(first);
-      builder.addAll(second);
-      return builder.build();
-    }
-
     @Override
     public boolean hasLiveness() {
       return true;
@@ -2608,6 +2603,10 @@
       return prunedTypes.contains(type);
     }
 
+    public Set<DexType> getPrunedTypes() {
+      return prunedTypes;
+    }
+
     public DexEncodedMethod lookup(Type type, DexMethod target, DexType invocationContext) {
       DexType holder = target.getHolder();
       if (!holder.isClassType()) {
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 fe401a1..2152d3c 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1069,12 +1069,12 @@
           method -> {
             if (method.isObsolete()) {
               method.unsetObsolete();
-              if (method.hasCode()) {
-                method.getCode().setOwner(method);
-              }
+            }
+            if (method.hasCode()) {
+              method.getCode().setOwner(method);
             }
           });
-      assert Streams.stream(target.methods())
+      assert Streams.concat(Streams.stream(source.methods()), Streams.stream(target.methods()))
           .allMatch(
               method ->
                   !method.isObsolete()
diff --git a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
new file mode 100644
index 0000000..6584f77
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -0,0 +1,18 @@
+// 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.utils;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Set;
+
+public class CollectionUtils {
+  public static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
+    ImmutableSet.Builder<T> builder = ImmutableSet.builder();
+    builder.addAll(first);
+    builder.addAll(second);
+    return builder.build();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index 2253f1f..3f0d0be 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -8,6 +8,7 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -205,11 +206,11 @@
   public void testRegress37658666() throws Exception {
     runTest(
         "regress_37658666.Regress",
-        (expectedBytes, actualBytes) -> {
+        (expected, actual) -> {
           // javac emits LDC(-0.0f) instead of the shorter FCONST_0 FNEG emitted by CfConstNumber.
           String ldc = "methodVisitor.visitLdcInsn(new Float(\"-0.0\"));";
           String constNeg = "methodVisitor.visitInsn(FCONST_0);\nmethodVisitor.visitInsn(FNEG);";
-          assertEquals(asmToString(expectedBytes).replace(ldc, constNeg), asmToString(actualBytes));
+          assertEquals(expected.replace(ldc, constNeg), actual);
         });
   }
 
@@ -294,10 +295,12 @@
   }
 
   private void runTest(String clazz) throws Exception {
-    runTest(clazz, null);
+    runTest(
+        clazz, (expected, actual) -> assertEquals("Class " + clazz + " differs", expected, actual));
   }
 
-  private void runTest(String clazz, BiConsumer<byte[], byte[]> comparator) throws Exception {
+  private void runTest(String clazz, BiConsumer<String, String> comparator) throws Exception {
+    assert comparator != null;
     String pkg = clazz.substring(0, clazz.lastIndexOf('.'));
     String suffix = "_debuginfo_all";
     Path inputJar = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, pkg + suffix + JAR_EXTENSION);
@@ -324,17 +327,30 @@
     for (String descriptor : expected.getClassDescriptors()) {
       byte[] expectedBytes = getClassAsBytes(expected, descriptor);
       byte[] actualBytes = getClassAsBytes(actual, descriptor);
-      if (comparator != null) {
-        comparator.accept(expectedBytes, actualBytes);
-      } else if (!Arrays.equals(expectedBytes, actualBytes)) {
-        assertEquals(
-            "Class " + descriptor + " differs",
-            asmToString(expectedBytes),
-            asmToString(actualBytes));
+      if (!Arrays.equals(expectedBytes, actualBytes)) {
+        String expectedString = replaceCatchThrowableByCatchAll(asmToString(expectedBytes));
+        String actualString = asmToString(actualBytes);
+        comparator.accept(expectedString, actualString);
       }
     }
   }
 
+  private static String replaceCatchThrowableByCatchAll(String content) {
+    String catchThrowablePrefix = "methodVisitor.visitTryCatchBlock(";
+    String catchThrowableSuffix = ", \"java/lang/Throwable\");";
+    StringBuilder expected = new StringBuilder();
+    List<String> expectedLines = StringUtils.splitLines(content);
+    for (String line : expectedLines) {
+      if (line.startsWith(catchThrowablePrefix) && line.endsWith(catchThrowableSuffix)) {
+        expected.append(line.replace("\"java/lang/Throwable\"", "null"));
+      } else {
+        expected.append(line);
+      }
+      expected.append(StringUtils.LINE_SEPARATOR);
+    }
+    return expected.toString();
+  }
+
   private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) {
     ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors());
     Collections.sort(descriptorList);
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..3693f87
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodTest.java
@@ -0,0 +1,83 @@
+// 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.desugaring.interfacemethods;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+
+public class InvokeSuperInDefaultInterfaceMethodTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    String expectedOutput = StringUtils.lines("I.m()", "J.m()", "JImpl.m()", "I.m()", "KImpl.m()");
+
+    testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+
+    testForR8(Backend.DEX)
+        .addInnerClasses(InvokeSuperInDefaultInterfaceMethodTest.class)
+        .addKeepMainRule(TestClass.class)
+        .enableClassInliningAnnotations()
+        .enableMergeAnnotations()
+        .setMinApi(AndroidApiLevel.M)
+        .run(TestClass.class)
+        .assertSuccessWithOutput(expectedOutput);
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      new JImpl().m();
+      new KImpl().m();
+    }
+  }
+
+  @NeverMerge
+  interface I {
+
+    default void m() {
+      System.out.println("I.m()");
+    }
+  }
+
+  @NeverMerge
+  interface J extends I {
+
+    @Override
+    default void m() {
+      I.super.m();
+      System.out.println("J.m()");
+    }
+  }
+
+  @NeverMerge
+  interface K extends I {
+
+    // Intentionally does not override I.m().
+  }
+
+  @NeverClassInline
+  static class JImpl implements J {
+
+    @Override
+    public void m() {
+      J.super.m();
+      System.out.println("JImpl.m()");
+    }
+  }
+
+  @NeverClassInline
+  static class KImpl implements K {
+
+    @Override
+    public void m() {
+      K.super.m();
+      System.out.println("KImpl.m()");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
new file mode 100644
index 0000000..698cf50
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
@@ -0,0 +1,117 @@
+// 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.desugaring.interfacemethods;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.smali.SmaliBuilder;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest extends TestBase {
+
+  private final boolean includeInterfaceMethodOnJ;
+
+  @Parameterized.Parameters(name = "Include interface method on J: {0}")
+  public static Boolean[] data() {
+    return BooleanUtils.values();
+  }
+
+  public InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest(
+      boolean includeInterfaceMethodOnJ) {
+    this.includeInterfaceMethodOnJ = includeInterfaceMethodOnJ;
+  }
+
+  @Test
+  public void test() throws Exception {
+    // Note that the expected output is independent of the presence of J.m().
+    String expectedOutput = StringUtils.lines("I.m()", "JImpl.m()");
+
+    byte[] dex = buildProgramDexFileData();
+    if (ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1)) {
+      AndroidApp app =
+          AndroidApp.builder()
+              .addDexProgramData(buildProgramDexFileData(), Origin.unknown())
+              .build();
+      assertEquals(expectedOutput, runOnArt(app, "TestClass"));
+    }
+
+    testForR8(Backend.DEX)
+        .addProgramDexFileData(dex)
+        .addKeepMainRule("TestClass")
+        .setMinApi(AndroidApiLevel.M)
+        .run("TestClass")
+        .assertSuccessWithOutput(expectedOutput);
+  }
+
+  // Using Smali instead of Jasmin because interfaces are broken in Jasmin.
+  private byte[] buildProgramDexFileData() throws Exception {
+    SmaliBuilder smaliBuilder = new SmaliBuilder();
+    smaliBuilder.setMinApi(AndroidApiLevel.N);
+
+    smaliBuilder.addClass("TestClass");
+
+    // public void main(String[] args) { new JImpl().m(); }
+    smaliBuilder.addMainMethod(
+        1,
+        "new-instance v0, LJImpl;",
+        "invoke-direct {v0}, LJImpl;-><init>()V",
+        "invoke-virtual {v0}, LJImpl;->m()V",
+        "return-void");
+
+    smaliBuilder.addInterface("I");
+
+    // default void m() { System.out.println("In I.m()"); }
+    smaliBuilder.addInstanceMethod(
+        "void",
+        "m",
+        2,
+        "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "const-string v1, \"I.m()\"",
+        "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "return-void");
+
+    smaliBuilder.addInterface("J", "java.lang.Object", ImmutableList.of("I"));
+    if (includeInterfaceMethodOnJ) {
+      smaliBuilder.addInstanceMethod(
+          "void",
+          "m",
+          2,
+          "invoke-super {p0}, LI;->m()V",
+          "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+          "const-string v1, \"J.m()\"",
+          "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+          "return-void");
+    }
+
+    smaliBuilder.addClass("JImpl", "java.lang.Object", ImmutableList.of("J"));
+    smaliBuilder.addDefaultConstructor();
+
+    // default void m() { I.super.m(); System.out.println("In JImpl.m()"); }
+    smaliBuilder.addInstanceMethod(
+        "void",
+        "m",
+        2,
+        // Note: invoke-super instruction to the non-immediate interface I.
+        "invoke-super {p0}, LI;->m()V",
+        "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;",
+        "const-string v1, \"JImpl.m()\"",
+        "invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
+        "return-void");
+
+    return smaliBuilder.compile();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
index a9e2157..37b0cdc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/TrivialGotoEliminationTest.java
@@ -132,9 +132,7 @@
         new Value(
             0,
             TypeLatticeElement.fromDexType(
-                DexItemFactory.catchAllType,
-                Nullability.definitelyNotNull(),
-                appInfo),
+                app.dexItemFactory.throwableType, Nullability.definitelyNotNull(), appInfo),
             null);
     instruction = new Argument(value);
     instruction.setPosition(position);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java
new file mode 100644
index 0000000..76e9cfa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineDefaultInterfaceMethodTest.java
@@ -0,0 +1,87 @@
+// 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.ir.optimize.inliner.interfacemethods;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+
+public class InlineDefaultInterfaceMethodTest extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    String expectedOutput = StringUtils.lines("Hello world!");
+
+    CodeInspector inspector =
+        testForR8(Backend.DEX)
+            .addInnerClasses(InlineDefaultInterfaceMethodTest.class)
+            .addKeepMainRule(TestClass.class)
+            .setMinApi(AndroidApiLevel.M)
+            .enableClassInliningAnnotations()
+            .enableMergeAnnotations()
+            .minification(false)
+            .run(TestClass.class)
+            .assertSuccessWithOutput(expectedOutput)
+            .inspector();
+
+    // TODO(b/124017330): interface methods should have been inlined into C.method().
+    ClassSubject classSubject =
+        inspector.clazz(I.class.getTypeName() + COMPANION_CLASS_NAME_SUFFIX);
+    assertThat(classSubject, isPresent());
+    assertThat(classSubject.uniqueMethodWithName(DEFAULT_METHOD_PREFIX + "hello"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName(DEFAULT_METHOD_PREFIX + "space"), isPresent());
+    assertThat(classSubject.uniqueMethodWithName(DEFAULT_METHOD_PREFIX + "world"), isPresent());
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      C obj = new C();
+      obj.method();
+    }
+  }
+
+  @NeverMerge
+  interface I {
+
+    default void hello() {
+      System.out.print("Hello");
+    }
+
+    default void space() {
+      System.out.print(" ");
+    }
+
+    default void world() {
+      System.out.println("world!");
+    }
+  }
+
+  @NeverClassInline
+  static class C implements I {
+
+    @NeverInline
+    public void method() {
+      // invoke-virtual
+      hello();
+      // invoke-interface
+      I self = this;
+      self.space();
+      // invoke-super
+      I.super.world();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineStaticInterfaceMethodTest.java
similarity index 95%
rename from src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineStaticInterfaceMethodTest.java
rename to src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineStaticInterfaceMethodTest.java
index af29bc7..fa636d5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineStaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/interfacemethods/InlineStaticInterfaceMethodTest.java
@@ -2,7 +2,7 @@
 // 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.ir.optimize.inliner;
+package com.android.tools.r8.ir.optimize.inliner.interfacemethods;
 
 import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java
new file mode 100644
index 0000000..a73b766
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/NestedStringBuilderTest.java
@@ -0,0 +1,65 @@
+// 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.ir.optimize.string;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.Streams;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+class NestedStringBuilders {
+
+  public static void main(String... args) {
+    System.out.println(concat("one", args[0]) + "two");
+  }
+
+  @ForceInline
+  public static String concat(String one, String two) {
+    return one + two;
+  }
+}
+
+@RunWith(Parameterized.class)
+public class NestedStringBuilderTest extends TestBase {
+  private static final Class<?> MAIN = NestedStringBuilders.class;
+  private final Backend backend;
+
+  @Parameterized.Parameters(name = "Backend: {0}")
+  public static Backend[] data() {
+    return Backend.values();
+  }
+
+  public NestedStringBuilderTest(Backend backend) {
+    this.backend = backend;
+  }
+
+  private void test(TestCompileResult result) throws Exception {
+    CodeInspector codeInspector = result.inspector();
+    ClassSubject mainClass = codeInspector.clazz(MAIN);
+    MethodSubject main = mainClass.mainMethod();
+    long count = Streams.stream(main.iterateInstructions(instructionSubject ->
+        instructionSubject.isNewInstance(StringBuilder.class.getTypeName()))).count();
+    // TODO(b/113859361): should be 1 after merging StringBuilder's
+    assertEquals(2, count);
+  }
+
+  @Test
+  public void b113859361() throws Exception {
+    TestCompileResult result = testForR8(backend)
+        .addProgramClasses(MAIN)
+        .enableInliningAnnotations()
+        .addKeepMainRule(MAIN)
+        .compile();
+    test(result);
+  }
+
+}
\ No newline at end of file
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 a9cf86e..bd7bb01 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
@@ -20,7 +20,6 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Streams;
 import java.util.List;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -104,7 +103,6 @@
   }
 
   @Test
-  @Ignore("b/119399513")
   public void testD8() throws Exception {
     assumeTrue("Only run D8 for Dex backend", backend == Backend.DEX);
 
@@ -124,7 +122,6 @@
   }
 
   @Test
-  @Ignore("b/119399513")
   public void testR8() throws Exception {
     TestRunResult result = testForR8(backend)
         .addProgramClasses(CLASSES)
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 19c931e..b722683 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -4,15 +4,18 @@
 
 package com.android.tools.r8.kotlin;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import com.android.tools.r8.KotlinTestBase;
 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.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.Code;
@@ -24,7 +27,6 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -32,7 +34,6 @@
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,63 +43,65 @@
 import org.junit.Assume;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public abstract class AbstractR8KotlinTestBase extends TestBase {
+public abstract class AbstractR8KotlinTestBase extends KotlinTestBase {
 
   // This is the name of the Jasmin-generated class which contains the "main" method which will
   // invoke the tested method.
   private static final String JASMIN_MAIN_CLASS = "TestMain";
 
-  @Parameter(0) public boolean allowAccessModification;
-  @Parameter(1) public KotlinTargetVersion targetVersion;
+  protected final boolean allowAccessModification;
 
   private final List<Path> classpath = new ArrayList<>();
   private final List<Path> extraClasspath = new ArrayList<>();
 
-  @Parameters(name = "allowAccessModification: {0} target: {1}")
+  @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}")
   public static Collection<Object[]> data() {
-    return buildParameters(BooleanUtils.values(), KotlinTargetVersion.values());
+    return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values());
+  }
+
+  protected AbstractR8KotlinTestBase(
+      KotlinTargetVersion kotlinTargetVersion, boolean allowAccessModification) {
+    super(kotlinTargetVersion);
+    this.allowAccessModification = allowAccessModification;
   }
 
   protected void addExtraClasspath(Path path) {
     extraClasspath.add(path);
   }
 
-  protected static void checkMethodIsInvokedAtLeastOnce(DexCode dexCode,
-      MethodSignature... methodSignatures) {
+  protected static void checkMethodIsInvokedAtLeastOnce(
+      DexCode dexCode, MethodSignature... methodSignatures) {
     for (MethodSignature methodSignature : methodSignatures) {
       checkMethodIsInvokedAtLeastOnce(dexCode, methodSignature);
     }
   }
 
-  private static void checkMethodIsInvokedAtLeastOnce(DexCode dexCode,
-      MethodSignature methodSignature) {
+  private static void checkMethodIsInvokedAtLeastOnce(
+      DexCode dexCode, MethodSignature methodSignature) {
     assertTrue("No invoke to '" + methodSignature.toString() + "'",
         Arrays.stream(dexCode.instructions)
             .filter((instr) -> instr.getMethod() != null)
             .anyMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name)));
   }
 
-  protected static void checkMethodIsNeverInvoked(DexCode dexCode,
-      MethodSignature... methodSignatures) {
+  protected static void checkMethodIsNeverInvoked(
+      DexCode dexCode, MethodSignature... methodSignatures) {
     for (MethodSignature methodSignature : methodSignatures) {
       checkMethodIsNeverInvoked(dexCode, methodSignature);
     }
   }
 
-  private static void checkMethodIsNeverInvoked(DexCode dexCode,
-      MethodSignature methodSignature) {
+  private static void checkMethodIsNeverInvoked(DexCode dexCode, MethodSignature methodSignature) {
     assertTrue("At least one invoke to '" + methodSignature.toString() + "'",
         Arrays.stream(dexCode.instructions)
             .filter((instr) -> instr.getMethod() != null)
             .noneMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name)));
   }
 
-  protected static void checkMethodsPresence(ClassSubject classSubject,
-      Map<MethodSignature, Boolean> presenceMap) {
+  protected static void checkMethodsPresence(
+      ClassSubject classSubject, Map<MethodSignature, Boolean> presenceMap) {
     presenceMap.forEach(((methodSignature, isPresent) -> {
       MethodSubject methodSubject = classSubject.method(methodSignature);
       String methodDesc = methodSignature.toString();
@@ -118,8 +121,8 @@
     return classSubject;
   }
 
-  protected FieldSubject checkFieldIsKept(ClassSubject classSubject, String fieldType,
-      String fieldName) {
+  protected FieldSubject checkFieldIsKept(
+      ClassSubject classSubject, String fieldType, String fieldName) {
     // Field must exist in the input.
     checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, true);
     FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
@@ -128,8 +131,13 @@
     return fieldSubject;
   }
 
-  protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType,
-      String fieldName) {
+  protected FieldSubject checkFieldIsKept(ClassSubject classSubject, String fieldName) {
+    FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
+    assertThat(fieldSubject, isPresent());
+    return fieldSubject;
+  }
+
+  protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType, String fieldName) {
     // Field must NOT exist in the input.
     checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, false);
     FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
@@ -137,32 +145,47 @@
     assertFalse(fieldSubject.isPresent());
   }
 
-  protected void checkMethodIsAbsent(ClassSubject classSubject,
-      MethodSignature methodSignature) {
+  protected FieldSubject checkFieldIsAbsent(ClassSubject classSubject, String fieldName) {
+    FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
+    assertThat(fieldSubject, not(isPresent()));
+    return fieldSubject;
+  }
+
+  protected void checkMethodIsAbsent(ClassSubject classSubject, MethodSignature methodSignature) {
     checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, false);
     checkMethodPresenceInOutput(classSubject, methodSignature, false);
   }
 
-  protected MethodSubject checkMethodIsKept(ClassSubject classSubject,
-      MethodSignature methodSignature) {
+  protected MethodSubject checkMethodIsKept(
+      ClassSubject classSubject, MethodSignature methodSignature) {
     checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
     return checkMethodIsKeptOrRemoved(classSubject, methodSignature, true);
   }
 
-  protected void checkMethodIsRemoved(ClassSubject classSubject,
-      MethodSignature methodSignature) {
+  protected MethodSubject checkMethodIsKept(ClassSubject classSubject, String methodName) {
+    MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+    assertThat(methodSubject, isPresent());
+    return methodSubject;
+  }
+
+  protected void checkMethodIsRemoved(ClassSubject classSubject, MethodSignature methodSignature) {
     checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
     checkMethodIsKeptOrRemoved(classSubject, methodSignature, false);
   }
 
-  protected MethodSubject checkMethodIsKeptOrRemoved(ClassSubject classSubject,
-      MethodSignature methodSignature, boolean isPresent) {
+  protected void checkMethodIsRemoved(ClassSubject classSubject, String methodName) {
+    MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+    assertThat(methodSubject, not(isPresent()));
+  }
+
+  protected MethodSubject checkMethodIsKeptOrRemoved(
+      ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
     checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
     return checkMethodPresenceInOutput(classSubject, methodSignature, isPresent);
   }
 
-  private MethodSubject checkMethodPresenceInOutput(ClassSubject classSubject,
-      MethodSignature methodSignature, boolean isPresent) {
+  private MethodSubject checkMethodPresenceInOutput(
+      ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
     MethodSubject methodSubject = classSubject.method(methodSignature);
     assertNotNull(methodSubject);
 
@@ -272,8 +295,8 @@
     }
   }
 
-  private void checkMethodPresenceInInput(String className, MethodSignature methodSignature,
-      boolean isPresent) {
+  private void checkMethodPresenceInInput(
+      String className, MethodSignature methodSignature, boolean isPresent) {
     boolean foundMethod = AsmUtils.doesMethodExist(classpath, className,
         methodSignature.name, methodSignature.toDescriptor());
     if (isPresent != foundMethod) {
@@ -285,8 +308,8 @@
     }
   }
 
-  private void checkFieldPresenceInInput(String className, String fieldType, String fieldName,
-      boolean isPresent) {
+  private void checkFieldPresenceInInput(
+      String className, String fieldType, String fieldName, boolean isPresent) {
     boolean foundField = AsmUtils.doesFieldExist(classpath, className, fieldName, fieldType);
     if (isPresent != foundField) {
       throw new AssertionError(
@@ -296,18 +319,8 @@
     }
   }
 
-  private Path getKotlinJarFile(String folder) {
-    return Paths.get(ToolHelper.TESTS_BUILD_DIR, "kotlinR8TestResources",
-        targetVersion.getFolderName(), folder + FileUtils.JAR_EXTENSION);
-  }
-
-  private Path getJavaJarFile(String folder) {
-    return Paths.get(ToolHelper.TESTS_BUILD_DIR, "kotlinR8TestResources",
-        targetVersion.getFolderName(), folder + ".java" + FileUtils.JAR_EXTENSION);
-  }
-
   @FunctionalInterface
-  interface AndroidAppInspector {
+  public interface AndroidAppInspector {
 
     void inspectApp(AndroidApp androidApp) throws Exception;
   }
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 1a690e2..30d90eb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -10,6 +10,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.code.NewInstance;
 import com.android.tools.r8.code.SgetObject;
 import com.android.tools.r8.graph.DexClass;
@@ -33,6 +34,11 @@
 
 public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase {
 
+  public KotlinClassInlinerTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   private static boolean isLambda(DexClass clazz) {
     return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
         (isKStyleLambdaOrGroup(clazz) || isJStyleLambdaOrGroup(clazz));
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index ced08f5..3f02c70 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -9,6 +9,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -16,6 +17,11 @@
 
 public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase {
 
+  public KotlinClassStaticizerTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void testCompanionAndRegularObjects() throws Exception {
     assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
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 7e9269d..0b79b3b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -9,6 +9,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
@@ -39,6 +40,11 @@
         opts.forceProguardCompatibility = true;
       };
 
+  public KotlinLambdaMergingTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   abstract static class LambdaOrGroup {
     abstract boolean match(DexClass clazz);
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
index 343dfdf..7b43112 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.function.Consumer;
 import org.junit.Test;
@@ -17,6 +18,11 @@
       o.enableLambdaMerging = true;
     };
 
+  public KotlinLambdaMergingWithReprocessingTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void testMergingKStyleLambdasAndReprocessing() throws Exception {
     final String mainClassName = "reprocess_merged_lambdas_kstyle.MainKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithFailedInliningTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
similarity index 71%
rename from src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithFailedInliningTest.java
rename to src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index f17a729..c5ee56d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithFailedInliningTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -3,11 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.kotlin;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.utils.InternalOptions;
 import java.util.function.Consumer;
 import org.junit.Test;
 
-public class KotlinLambdaMergingWithFailedInliningTest extends AbstractR8KotlinTestBase {
+public class KotlinLambdaMergingWithSmallInliningBudgetTest extends AbstractR8KotlinTestBase {
   private Consumer<InternalOptions> optionsModifier =
       o -> {
         o.enableTreeShaking = true;
@@ -18,6 +19,11 @@
         o.inliningInstructionAllowance = 3;
       };
 
+  public KotlinLambdaMergingWithSmallInliningBudgetTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void testJStyleRunnable() throws Exception {
     final String mainClassName = "lambdas_jstyle_runnable.MainKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 1515689..0048b1b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -9,6 +9,7 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -64,6 +65,11 @@
   private Consumer<InternalOptions> disableClassStaticizer =
       opts -> opts.enableClassStaticizer = false;
 
+  public R8KotlinAccessorTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void testCompanionProperty_primitivePropertyIsAlwaysInlined() throws Exception {
     final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 9224405..7e16b2f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.kotlin;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -37,6 +38,11 @@
 
   private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false;
 
+  public R8KotlinDataClassTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void test_dataclass_gettersOnly() throws Exception {
     final String mainClassName = "dataclass.MainGettersOnlyKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index c90df04..c74b790 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.kotlin;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -20,6 +21,11 @@
   private static final TestKotlinDataClass KOTLIN_INTRINSICS_CLASS =
       new TestKotlinDataClass("kotlin.jvm.internal.Intrinsics");
 
+  public R8KotlinIntrinsicsTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void testParameterNullCheckIsInlined() throws Exception {
     final String extraRules = keepClassMethod("intrinsics.IntrinsicsKt",
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index bc23757..d310c6e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.kotlin.TestKotlinClass.KotlinProperty;
 import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
 import com.android.tools.r8.naming.MemberNaming;
@@ -90,6 +91,11 @@
         o.enableClassStaticizer = false;
       };
 
+  public R8KotlinPropertiesTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void testMutableProperty_getterAndSetterAreRemoveIfNotUsed() throws Exception {
     String mainClass = addMainToClasspath("properties/MutablePropertyKt",
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 0ebc393..b4b0c06 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -5,6 +5,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -18,6 +19,11 @@
   private static final String FOLDER = "non_null";
   private static final String STRING = "java.lang.String";
 
+  public SimplifyIfNotNullKotlinTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+    super(targetVersion, allowAccessModification);
+  }
+
   @Test
   public void test_example1() throws Exception {
     final TestKotlinClass ex1 = new TestKotlinClass("non_null.Example1Kt");
diff --git a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
index 685df6a..d135bf9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
+++ b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
@@ -16,7 +16,7 @@
  *
  * <p>See https://kotlinlang.org/docs/reference/classes.html</p>
  */
-class TestKotlinClass {
+public class TestKotlinClass {
 
   /**
    * This is the suffix appended by Kotlin compiler to getter and setter method names of
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 30db56e..bdf815e 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -888,7 +888,7 @@
     }
 
     @Override
-    public CatchHandlers<Integer> getCurrentCatchHandlers() {
+    public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
       return null;
     }
 
diff --git a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
new file mode 100644
index 0000000..bb9af02
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
@@ -0,0 +1,106 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public abstract class AbstractR8KotlinNamingTestBase extends AbstractR8KotlinTestBase {
+
+  protected final boolean minification;
+
+  @Parameters(name = "target: {0}, allowAccessModification: {1}, minification: {2}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        KotlinTargetVersion.values(), BooleanUtils.values(), BooleanUtils.values());
+  }
+
+  AbstractR8KotlinNamingTestBase(
+      KotlinTargetVersion kotlinTargetVersion,
+      boolean allowAccessModification,
+      boolean minification) {
+    super(kotlinTargetVersion, allowAccessModification);
+    this.minification = minification;
+  }
+
+  protected ClassSubject checkClassIsRenamed(CodeInspector inspector, String className) {
+    ClassSubject classSubject = inspector.clazz(className);
+    assertThat(classSubject, isRenamed());
+    return classSubject;
+  }
+
+  protected ClassSubject checkClassIsNotRenamed(CodeInspector inspector, String className) {
+    ClassSubject classSubject = inspector.clazz(className);
+    assertThat(classSubject, not(isRenamed()));
+    return classSubject;
+  }
+
+  protected FieldSubject checkFieldIsRenamed(
+      ClassSubject classSubject, String fieldType, String fieldName) {
+    FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
+    assertThat(fieldSubject, isRenamed());
+    return fieldSubject;
+  }
+
+  protected FieldSubject checkFieldIsRenamed(ClassSubject classSubject, String fieldName) {
+    FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
+    assertThat(fieldSubject, isRenamed());
+    return fieldSubject;
+  }
+
+  protected FieldSubject checkFieldIsNotRenamed(
+      ClassSubject classSubject, String fieldType, String fieldName) {
+    FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
+    assertThat(fieldSubject, not(isRenamed()));
+    return fieldSubject;
+  }
+
+  protected FieldSubject checkFieldIsNotRenamed(ClassSubject classSubject, String fieldName) {
+    FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
+    assertThat(fieldSubject, not(isRenamed()));
+    return fieldSubject;
+  }
+
+  protected MethodSubject checkMethodIsRenamed(
+      ClassSubject classSubject, MethodSignature methodSignature) {
+    MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
+    assertThat(methodSubject, isRenamed());
+    return methodSubject;
+  }
+
+  protected MethodSubject checkMethodIsRenamed(ClassSubject classSubject, String methodName) {
+    MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
+    assertThat(methodSubject, isRenamed());
+    return methodSubject;
+  }
+
+  protected MethodSubject checkMethodIsNotRenamed(
+      ClassSubject classSubject, MethodSignature methodSignature) {
+    MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
+    assertThat(methodSubject, not(isRenamed()));
+    return methodSubject;
+  }
+
+  protected MethodSubject checkMethodIsNotRenamed(ClassSubject classSubject, String methodName) {
+    MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
+    assertThat(methodSubject, not(isRenamed()));
+    return methodSubject;
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
new file mode 100644
index 0000000..aa2e69d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -0,0 +1,150 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.kotlin.TestKotlinClass;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class KotlinIntrinsicsIdentifierTest extends AbstractR8KotlinNamingTestBase {
+  private static final String FOLDER = "intrinsics_identifiers";
+
+  public KotlinIntrinsicsIdentifierTest(
+      KotlinTargetVersion targetVersion, boolean allowAccessModification, boolean minification) {
+    super(targetVersion, allowAccessModification, minification);
+  }
+
+  @Test
+  public void test_example1() throws Exception {
+    TestKotlinClass ex1 = new TestKotlinClass("intrinsics_identifiers.Example1Kt");
+    String targetClassName = "ToBeRenamedClass";
+    String targetFieldName = "toBeRenamedField";
+    String targetMethodName = "toBeRenamedMethod";
+    test(ex1, targetClassName, targetFieldName, targetMethodName);
+  }
+
+  @Test
+  public void test_example2() throws Exception {
+    TestKotlinClass ex2 = new TestKotlinClass("intrinsics_identifiers.Example2Kt");
+    String targetClassName = "AnotherClass";
+    String targetFieldName = "anotherField";
+    String targetMethodName = "anotherMethod";
+    test(ex2, targetClassName, targetFieldName, targetMethodName);
+  }
+
+  @Test
+  public void test_example3() throws Exception {
+    TestKotlinClass ex3 = new TestKotlinClass("intrinsics_identifiers.Example3Kt");
+    String mainClassName = ex3.getClassName();
+    TestCompileResult result = testForR8(Backend.DEX)
+        .addProgramFiles(getKotlinJarFile(FOLDER))
+        .addProgramFiles(getJavaJarFile(FOLDER))
+        .addKeepMainRule(mainClassName)
+        .minification(minification)
+        .compile();
+    CodeInspector codeInspector = result.inspector();
+    MethodSubject main = codeInspector.clazz(ex3.getClassName()).mainMethod();
+    assertThat(main, isPresent());
+    verifyKotlinIntrinsicsRenamed(codeInspector, main);
+  }
+
+  private void verifyKotlinIntrinsicsRenamed(CodeInspector inspector, MethodSubject main) {
+    Iterator<InstructionSubject> it = main.iterateInstructions(InstructionSubject::isInvokeStatic);
+    assertTrue(it.hasNext());
+    boolean metKotlinIntrinsicsNullChecks = false;
+    while (it.hasNext()) {
+      DexMethod invokedMethod = it.next().getMethod();
+      if (invokedMethod.getHolder().toSourceString().contains("java.net")) {
+        continue;
+      }
+      ClassSubject invokedMethodHolderSubject =
+          inspector.clazz(invokedMethod.getHolder().toSourceString());
+      assertThat(invokedMethodHolderSubject, isPresent());
+      assertEquals(minification, invokedMethodHolderSubject.isRenamed());
+      MethodSubject invokedMethodSubject = invokedMethodHolderSubject.method(
+          invokedMethod.proto.returnType.toSourceString(),
+          invokedMethod.name.toString(),
+          Arrays.stream(invokedMethod.proto.parameters.values)
+              .map(DexType::toSourceString)
+              .collect(Collectors.toList()));
+      assertThat(invokedMethodSubject, isPresent());
+      assertEquals(minification, invokedMethodSubject.isRenamed());
+      if (invokedMethodSubject.getOriginalName().startsWith("check")
+          && invokedMethodSubject.getOriginalName().endsWith("Null")
+          && invokedMethodHolderSubject.getOriginalDescriptor()
+              .contains("kotlin/jvm/internal/Intrinsics")) {
+        metKotlinIntrinsicsNullChecks = true;
+      }
+    }
+    assertTrue(metKotlinIntrinsicsNullChecks);
+  }
+
+  private void test(
+      TestKotlinClass testMain,
+      String targetClassName,
+      String targetFieldName,
+      String targetMethodName) throws Exception {
+    String mainClassName = testMain.getClassName();
+    TestRunResult result = testForR8(Backend.DEX)
+        .addProgramFiles(getKotlinJarFile(FOLDER))
+        .addProgramFiles(getJavaJarFile(FOLDER))
+        .enableProguardTestOptions()
+        .addKeepMainRule(mainClassName)
+        .addKeepRules(StringUtils.lines(
+            "-neverclassinline class **." + targetClassName,
+            "-nevermerge class **." + targetClassName,
+            "-neverinline class **." + targetClassName + " { <methods>; }"
+        ))
+        .minification(minification)
+        .run(mainClassName);
+    CodeInspector codeInspector = result.inspector();
+
+    MethodSubject main = codeInspector.clazz(testMain.getClassName()).mainMethod();
+    assertThat(main, isPresent());
+    verifyKotlinIntrinsicsRenamed(codeInspector, main);
+    // Examine all const-string and verify that identifiers are not introduced.
+    Iterator<InstructionSubject> it =
+        main.iterateInstructions(i -> i.isConstString(JumboStringMode.ALLOW));
+    assertTrue(it.hasNext());
+    while (it.hasNext()) {
+      String identifier = it.next().getConstString();
+      if (identifier.contains("arg")) {
+        continue;
+      }
+      assertEquals(!minification, identifier.equals(targetMethodName));
+      assertEquals(!minification, identifier.equals(targetFieldName));
+    }
+
+    targetClassName = FOLDER + "." + targetClassName;
+    ClassSubject clazz = minification
+        ? checkClassIsRenamed(codeInspector, targetClassName)
+        : checkClassIsNotRenamed(codeInspector, targetClassName);
+    if (minification) {
+      checkFieldIsRenamed(clazz, targetFieldName);
+      checkMethodIsRenamed(clazz, targetMethodName);
+    } else {
+      checkFieldIsNotRenamed(clazz, targetFieldName);
+      checkMethodIsNotRenamed(clazz, targetMethodName);
+    }
+  }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
new file mode 100644
index 0000000..0971e8c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b126592786/B126592786.java
@@ -0,0 +1,111 @@
+// 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.naming.b126592786;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B126592786 extends TestBase {
+
+  private final Backend backend;
+  private final boolean minify;
+
+  @Parameterized.Parameters(name = "Backend: {0} minify: {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(Backend.values(), BooleanUtils.values());
+  }
+
+  public B126592786(Backend backend, boolean minify) {
+    this.backend = backend;
+    this.minify = minify;
+  }
+
+  public void runTest(boolean genericTypeLive) throws Exception {
+    Class<?> mainClass = genericTypeLive ? MainGenericTypeLive.class : MainGenericTypeNotLive.class;
+    testForR8(backend)
+        .minification(minify)
+        .addProgramClasses(A.class, GenericType.class, mainClass)
+        .addKeepMainRule(mainClass)
+        .addKeepRules(
+            "-keepclassmembers @" + Marker.class.getTypeName() + " class * {",
+            "  <fields>;",
+            "}",
+            "-keepattributes InnerClasses,EnclosingMethod,Signature ")
+        .compile()
+        .inspect(inspector -> {
+            String genericTypeDescriptor = "Ljava/lang/Object;";
+            if (genericTypeLive) {
+              ClassSubject genericType = inspector.clazz(GenericType.class);
+              assertThat(genericType, isRenamed(minify));
+              genericTypeDescriptor = genericType.getFinalDescriptor();
+            }
+            String expectedSignature = "Ljava/util/List<" + genericTypeDescriptor + ">;";
+            FieldSubject list = inspector.clazz(A.class).uniqueFieldWithName("list");
+            assertThat(list, isPresent());
+            assertThat(list.getSignatureAnnotation(), isPresent());
+            assertEquals(expectedSignature, list.getSignatureAnnotationValue());
+        })
+        .run(mainClass)
+        .assertSuccess();
+  }
+
+  @Test
+  public void testGenericClassNotLive() throws Exception {
+    runTest(false);
+  }
+
+  @Test
+  public void testGenericClassLive() throws Exception {
+    runTest(true);
+  }
+}
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@interface Marker {
+}
+
+@Marker
+class A {
+
+  List<GenericType> list;
+}
+
+@Marker
+class GenericType {
+
+}
+
+class MainGenericTypeNotLive {
+
+  public static void main(String[] args) {
+    System.out.println(A.class);
+  }
+}
+
+class MainGenericTypeLive {
+
+  public static void main(String[] args) {
+    System.out.println(A.class);
+    System.out.println(GenericType.class);
+  }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..f599f4d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
@@ -0,0 +1,176 @@
+// 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.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;
+import org.junit.Test;
+
+public class GenericSignatureRenamingTest extends TestBase {
+
+  @Test
+  public void testJVM() throws IOException, CompilationFailedException {
+    testForJvm().addTestClasspath().run(Main.class).assertSuccess();
+  }
+
+  @Test
+  public void testR8Dex() throws IOException, CompilationFailedException {
+    test(testForR8(Backend.DEX));
+  }
+
+  @Test
+  public void testR8CompatDex() throws IOException, CompilationFailedException {
+    test(testForR8Compat(Backend.DEX));
+  }
+
+  @Test
+  public void testR8DexNoMinify() throws IOException, CompilationFailedException {
+    test(testForR8(Backend.DEX).addKeepRules("-dontobfuscate"));
+  }
+
+  @Test
+  public void testR8Cf() throws IOException, CompilationFailedException {
+    test(testForR8(Backend.CF));
+  }
+
+  @Test
+  public void testR8CfNoMinify() throws IOException, CompilationFailedException {
+    test(testForR8(Backend.CF).addKeepRules("-dontobfuscate"));
+  }
+
+  @Test
+  public void testD8() throws IOException, CompilationFailedException {
+    testForD8()
+        .addProgramClasses(Main.class)
+        .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
+        .setMode(CompilationMode.RELEASE)
+        .compile()
+        .assertNoMessages()
+        .run(Main.class)
+        .assertSuccess();
+  }
+
+  private void test(R8TestBuilder builder) throws IOException, CompilationFailedException {
+    builder
+        .addKeepRules("-dontoptimize")
+        .addKeepRules("-keepattributes InnerClasses,EnclosingMethod,Signature")
+        .addKeepMainRule(Main.class)
+        .addProgramClasses(Main.class)
+        .addProgramClassesAndInnerClasses(A.class, B.class, CY.class, CYY.class)
+        .setMode(CompilationMode.RELEASE)
+        .compile()
+        .assertNoMessages()
+        .run(Main.class)
+        .assertSuccess();
+  }
+}
+
+class A<T> {
+  class Y {
+
+    class YY {}
+
+    class ZZ extends YY {
+      public YY yy;
+
+      YY newYY() {
+        return new YY();
+      }
+    }
+
+    ZZ zz() {
+      return new ZZ();
+    }
+  }
+
+  class Z extends Y {}
+
+  static class S {}
+
+  Y newY() {
+    return new Y();
+  }
+
+  Z newZ() {
+    return new Z();
+  }
+
+  Y.ZZ newZZ() {
+    return new Y().zz();
+  }
+}
+
+class B<T extends A<T>> {}
+
+class CY<T extends A<T>.Y> {}
+
+class CYY<T extends A<T>.Y.YY> {}
+
+class Main {
+
+  private static void check(boolean b, String message) {
+    if (!b) {
+      throw new RuntimeException("Check failed: " + message);
+    }
+  }
+
+  public static void main(String[] args) {
+    A.Z z = new A().newZ();
+    A.Y.YY yy = new A().newZZ().yy;
+
+    B b = new B();
+    CY cy = new CY();
+
+    CYY cyy = new CYY();
+    A.S s = new A.S();
+
+    // Check if names of Z and ZZ shows A as a superclass.
+    Class classA = new A().getClass();
+    String nameA = classA.getName();
+
+    TypeVariable[] v = classA.getTypeParameters();
+    check(v != null && v.length == 1, classA + " expected to have 1 type parameter.");
+
+    Class classZ = new A().newZ().getClass();
+    String nameZ = classZ.getName();
+    check(nameZ.startsWith(nameA + "$"), nameZ + " expected to start with " + nameA + "$.");
+
+    Class classZZ = new A().newZZ().getClass();
+    String nameZZ = classZZ.getName();
+    check(nameZZ.startsWith(nameA + "$"), nameZZ + " expected to start with " + nameA + "$.");
+
+    // Check that the owner of the superclass of Z is A.
+    Class ownerClassOfSuperOfZ = getEnclosingClass(classZ.getGenericSuperclass());
+
+    check(
+        ownerClassOfSuperOfZ == A.class,
+        ownerClassOfSuperOfZ + " expected to be equal to " + A.class);
+
+    // Check that the owner-owner of the superclass of Z is A.
+    Class ownerOfownerOfSuperOfZZ =
+        getEnclosingClass(classZZ.getGenericSuperclass()).getEnclosingClass();
+
+    check(
+        ownerOfownerOfSuperOfZZ == A.class,
+        ownerOfownerOfSuperOfZZ + " expected to be equal to " + A.class);
+  }
+
+  private static Class getEnclosingClass(Type type) {
+    if (type instanceof ParameterizedType) {
+      // On the JVM it's a ParameterizedType.
+      return (Class) ((ParameterizedType) ((ParameterizedType) type).getOwnerType()).getRawType();
+    } else {
+      // On the ART it's Class.
+      check(type instanceof Class, type + " expected to be a ParameterizedType or Class.");
+      return ((Class) type).getEnclosingClass();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
index 1dffcee..dc980d7 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuilder.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.smali;
 
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.Smali;
@@ -106,8 +107,8 @@
 
   public class InterfaceBuilder extends Builder {
 
-    InterfaceBuilder(String name, String superName) {
-      super(name, superName, ImmutableList.of());
+    InterfaceBuilder(String name, String superName, List<String> implementedInterfaces) {
+      super(name, superName, implementedInterfaces);
     }
 
     public String toString() {
@@ -125,6 +126,7 @@
 
   private String currentClassName;
   private final Map<String, Builder> classes = new HashMap<>();
+  private AndroidApiLevel minApi = AndroidApiLevel.I_MR1;
 
   public SmaliBuilder() {
     // No default class.
@@ -138,6 +140,10 @@
     addClass(name, superName);
   }
 
+  public void setMinApi(AndroidApiLevel minApi) {
+    this.minApi = minApi;
+  }
+
   private List<String> getSource(String clazz) {
     return classes.get(clazz).source;
   }
@@ -169,9 +175,13 @@
   }
 
   public void addInterface(String name, String superName) {
+    addInterface(name, superName, ImmutableList.of());
+  }
+
+  public void addInterface(String name, String superName, List<String> implementedInterfaces) {
     assert !classes.containsKey(name);
     currentClassName = name;
-    classes.put(name, new InterfaceBuilder(name, superName));
+    classes.put(name, new InterfaceBuilder(name, superName, implementedInterfaces));
   }
 
   public void setSourceFile(String file) {
@@ -360,7 +370,7 @@
   }
 
   public byte[] compile() throws IOException, RecognitionException, ExecutionException {
-    return Smali.compile(buildSource());
+    return Smali.compile(buildSource(), minApi.getLevel());
   }
 
   public AndroidApp build() throws IOException, RecognitionException, ExecutionException {
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index b99cbdc..3e3fd4e 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -105,6 +105,7 @@
     SingleFileConsumer consumer = new SingleFileConsumer();
     AndroidApp app = AndroidApp.builder().addDexProgramData(data, Origin.unknown()).build();
     InternalOptions options = new InternalOptions();
+    options.minApiLevel = apiLevel;
     options.programConsumer = consumer;
     ExecutorService executor = ThreadUtils.getExecutorService(1);
     try {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
index 3f8cd81..20da5d0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
@@ -90,4 +90,9 @@
   public String getFinalSignatureAttribute() {
     return null;
   }
+
+  @Override
+  public AnnotationSubject annotation(String name) {
+    return new AbsentAnnotationSubject();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 93a1c37..5f1b405 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -142,6 +142,14 @@
   }
 
   @Override
+  public String getConstString() {
+    if (instruction instanceof CfConstString) {
+      return ((CfConstString) instruction).getString().toSourceString();
+    }
+    return null;
+  }
+
+  @Override
   public boolean isConstClass() {
     return instruction instanceof CfConstClass;
   }
@@ -193,6 +201,12 @@
   }
 
   @Override
+  public boolean isNewInstance(String type) {
+    return isNewInstance()
+        && ((CfNew) instruction).getType().toString().equals(type);
+  }
+
+  @Override
   public boolean isCheckCast() {
     return instruction instanceof CfCheckCast;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
index 2d7aec8..663a070 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfTryCatchSubject.java
@@ -58,7 +58,7 @@
 
   @Override
   public boolean hasCatchAll() {
-    return isCatching(DexItemFactory.catchAllType.toDescriptorString());
+    return isCatching(DexItemFactory.throwableDescriptorString);
   }
 
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index c9b7ee5..ba23a0a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -252,6 +252,17 @@
   }
 
   @Override
+  public String getConstString() {
+    if (instruction instanceof ConstString) {
+      return ((ConstString) instruction).BBBB.toSourceString();
+    }
+    if (instruction instanceof ConstStringJumbo) {
+      return ((ConstStringJumbo) instruction).BBBBBBBB.toSourceString();
+    }
+    return null;
+  }
+
+  @Override
   public boolean isConstClass() {
     return instruction instanceof ConstClass;
   }
@@ -303,6 +314,12 @@
   }
 
   @Override
+  public boolean isNewInstance(String type) {
+    return isNewInstance()
+        && ((NewInstance) instruction).getType().toString().equals(type);
+  }
+
+  @Override
   public boolean isCheckCast() {
     return instruction instanceof CheckCast;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index df23da9..0d63e38 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexValue;
 import com.android.tools.r8.naming.MemberNaming;
@@ -128,6 +129,14 @@
   }
 
   @Override
+  public AnnotationSubject annotation(String name) {
+    DexAnnotation annotation = codeInspector.findAnnotation(name, dexField.annotations);
+    return annotation == null
+        ? new AbsentAnnotationSubject()
+        : new FoundAnnotationSubject(annotation);
+  }
+
+  @Override
   public String toString() {
     return dexField.toSourceString();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index dd7cd85..4d7c0fc 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -288,5 +288,4 @@
         ? new AbsentAnnotationSubject()
         : new FoundAnnotationSubject(annotation);
   }
-
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 0528185..f2eb753 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -48,6 +48,8 @@
 
   boolean isConstString(String value, JumboStringMode jumboStringMode);
 
+  String getConstString();
+
   boolean isConstClass();
 
   boolean isConstClass(String type);
@@ -68,6 +70,8 @@
 
   boolean isNewInstance();
 
+  boolean isNewInstance(String type);
+
   boolean isCheckCast();
 
   boolean isCheckCast(String type);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
index af1fe14..a5cca23 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MemberSubject.java
@@ -4,6 +4,9 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.graph.DexAnnotationElement;
+import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.naming.MemberNaming.Signature;
 
 public abstract class MemberSubject extends Subject {
@@ -48,6 +51,34 @@
     return finalSignature == null ? null : finalSignature.name;
   }
 
+  public abstract AnnotationSubject annotation(String name);
+
+  public AnnotationSubject getSignatureAnnotation() {
+    return annotation("dalvik.annotation.Signature");
+  }
+
+  public String getSignatureAnnotationValue() {
+    AnnotationSubject annotation = getSignatureAnnotation();
+    if (!annotation.isPresent()) {
+      return null;
+    }
+
+    assert annotation.getAnnotation().elements.length == 1;
+    DexAnnotationElement element = annotation.getAnnotation().elements[0];
+    assert element.name.toString().equals("value");
+    assert element.value instanceof DexValue.DexValueArray;
+    DexValue.DexValueArray array = (DexValue.DexValueArray) element.value;
+    StringBuilder builder = new StringBuilder();
+    for (DexValue value : array.getValues()) {
+      if (value instanceof DexValueString) {
+        builder.append(((DexValueString) value).value);
+      } else {
+        builder.append(value.toString());
+      }
+    }
+    return builder.toString();
+  }
+
   public FieldSubject asFieldSubject() {
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 1575372..8fb4001 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -72,6 +72,4 @@
   public boolean isMethodSubject() {
     return true;
   }
-
-  public abstract AnnotationSubject annotation(String name);
 }
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java b/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java
new file mode 100644
index 0000000..6218d16
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java
@@ -0,0 +1,14 @@
+// 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 intrinsics_identifiers;
+
+class ToBeRenamedClass {
+  String toBeRenamedField = "SUFFIX";
+  String toBeRenamedMethod(String arg) {
+    return arg + toBeRenamedField;
+  }
+  void updateField(String arg) {
+    toBeRenamedField = arg;
+  }
+}
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt
new file mode 100644
index 0000000..4409232
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt
@@ -0,0 +1,17 @@
+// 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 intrinsics_identifiers
+
+fun main(args: Array<String>) {
+  val instance = ToBeRenamedClass()
+  println(instance.toBeRenamedField)
+  println(instance.toBeRenamedMethod("arg1"))
+
+  if (instance.toBeRenamedField.equals("arg2")) {
+    instance.updateField("arg3")
+    println(instance.toBeRenamedField)
+    println(instance.toBeRenamedMethod("arg4"))
+  }
+}
+
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt
new file mode 100644
index 0000000..c6c5e09
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt
@@ -0,0 +1,27 @@
+// 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 intrinsics_identifiers
+
+class AnotherClass {
+  var anotherField : String = "PREFIX"
+  fun anotherMethod(arg: String) : String {
+    return anotherField + arg
+  }
+  fun updateField(arg: String) : Unit {
+    anotherField = arg
+  }
+}
+
+fun main(args: Array<String>) {
+  val instance = AnotherClass()
+  println(instance.anotherField)
+  println(instance.anotherMethod("arg1"))
+
+  if (instance.anotherField.equals("arg2")) {
+    instance.updateField("arg3")
+    println(instance.anotherField)
+    println(instance.anotherMethod("arg4"))
+  }
+}
+
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt
new file mode 100644
index 0000000..47b1d00
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt
@@ -0,0 +1,14 @@
+// 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 intrinsics_identifiers
+
+import java.net.URI
+
+fun main(args: Array<String>) {
+  // By specifying non-null type of variables for library uses,
+  // kotlin.jvm.internal.Intrinsics#check*Null(...) is added by kotlinc.
+  val uri : URI = URI.create("google.com")
+  val host : String = uri.host
+  println(host)
+}
\ No newline at end of file
diff --git a/tools/archive.py b/tools/archive.py
index c842c69..687844a 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -8,6 +8,7 @@
 import jdk
 import optparse
 import os
+import resource
 import shutil
 import subprocess
 import sys
@@ -90,6 +91,11 @@
 def GetMavenUrl(is_master):
   return GetVersionDestination('http://storage.googleapis.com/', '', is_master)
 
+def PrintResourceInfo():
+  (soft, hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
+  print('INFO: Open files soft limit: %s' % soft)
+  print('INFO: Open files hard limit: %s' % hard)
+
 def Main():
   (options, args) = ParseOptions()
   if not utils.is_bot() and not options.dry_run:
@@ -99,6 +105,7 @@
     print("Archiving is disabled on old bots.")
     return
 
+  PrintResourceInfo()
   # Create maven release which uses a build that exclude dependencies.
   create_maven_release.main(["--out", utils.LIBS])
 
diff --git a/tools/run_on_as_app.py b/tools/run_on_as_app.py
index d95453f..aa7ed29 100755
--- a/tools/run_on_as_app.py
+++ b/tools/run_on_as_app.py
@@ -511,8 +511,9 @@
     apk_dest = os.path.join(out_dir, unsigned_apk_name)
     as_utils.MoveFile(unsigned_apk, apk_dest, quiet=options.quiet)
 
-  assert ('r8' not in shrinker
-      or CheckIsBuiltWithExpectedR8(apk_dest, temp_dir, shrinker, options))
+  # TODO(mkroghj) Re-enable this assertion when fix works in Golem.
+  # assert ('r8' not in shrinker
+  #     or CheckIsBuiltWithExpectedR8(apk_dest, temp_dir, shrinker, options))
 
   profile_dest_dir = os.path.join(out_dir, 'profile')
   as_utils.MoveProfileReportTo(profile_dest_dir, stdout, quiet=options.quiet)