Merge "Add Proguard 6.0 to third_party"
diff --git a/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java b/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
index 00cb542..0a633f3 100644
--- a/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
+++ b/src/bspatch/java/com/android/tools/r8/dex/BSPatch.java
@@ -316,14 +316,14 @@
     @Override
     public void writeResult() throws IOException {
       if (dexPath != null) {
-        Segment[] segments = DexFileReader.parseMapFrom(dexPath);
-        for (Segment segment : segments) {
-          int y = segment.offset / width;
+        DexSection[] sections = DexParser.parseMapFrom(dexPath);
+        for (DexSection section : sections) {
+          int y = section.offset / width;
           for (int x = 0; x < width; x++) {
             int val = (x / 10) % 2 == 0 ? 0 : 0xffffff;
             image.setRGB(x, y, val);
           }
-          System.out.println(segment);
+          System.out.println(section);
         }
       }
       ImageIO.write(image, "png", output.toFile());
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index a7bb291..4a26889 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -122,7 +122,7 @@
 
   static void runForTesting(AndroidApp inputApp, InternalOptions options)
       throws IOException, CompilationException {
-    ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
+    ExecutorService executor = ThreadUtils.getExecutorService(options);
     try {
       run(inputApp, options, executor);
     } finally {
@@ -146,13 +146,13 @@
 
   private static void run(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
       throws IOException, CompilationException {
+    Timing timing = new Timing("D8");
     try {
       // Disable global optimizations.
       options.enableMinification = false;
       options.enableInlining = false;
       options.outline.enabled = false;
 
-      Timing timing = new Timing("DX timer");
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
       AppInfo appInfo = new AppInfo(app);
       app = optimize(app, appInfo, options, timing, executor);
@@ -171,6 +171,10 @@
       throw new AssertionError(e); // unwrapping method should have thrown
     } finally {
       options.signalFinishedToProgramConsumer();
+      // Dump timings.
+      if (options.printTimes) {
+        timing.report();
+      }
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 5d77ad3..fed4a00 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 = "v1.1.7-dev";
+  public static final String LABEL = "v1.1.8-dev";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
index 908372a..8c6574c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntry.java
@@ -11,6 +11,7 @@
 
 public class DexDebugEntry {
 
+  public final boolean lineEntry;
   public final int address;
   public final int line;
   public final DexString sourceFile;
@@ -21,6 +22,7 @@
   public final Position callerPosition;
 
   public DexDebugEntry(
+      boolean lineEntry,
       int address,
       int line,
       DexString sourceFile,
@@ -29,6 +31,7 @@
       ImmutableMap<Integer, DebugLocalInfo> locals,
       DexMethod method,
       Position callerPosition) {
+    this.lineEntry = lineEntry;
     this.address = address;
     this.line = line;
     this.sourceFile = sourceFile;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index a1bc201..b672e26 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -98,6 +98,7 @@
   @Override
   public void visit(DexDebugEvent.AdvancePC advancePC) {
     positionState.visit(advancePC);
+    entryEventReceived(false);
   }
 
   @Override
@@ -113,7 +114,7 @@
   @Override
   public void visit(DexDebugEvent.Default defaultEvent) {
     positionState.visit(defaultEvent);
-    defaultEventReceived();
+    entryEventReceived(true);
   }
 
   @Override
@@ -153,11 +154,12 @@
     getEntry(restartLocal.registerNum).reset();
   }
 
-  public void defaultEventReceived() {
+  private void entryEventReceived(boolean lineEntry) {
     if (pending != null) {
       // Local changes contribute to the pending position entry.
       entries.add(
           new DexDebugEntry(
+              pending.lineEntry,
               pending.address,
               pending.line,
               pending.sourceFile,
@@ -169,6 +171,7 @@
     }
     pending =
         new DexDebugEntry(
+            lineEntry,
             positionState.getCurrentPc(),
             positionState.getCurrentLine(),
             positionState.getCurrentFile(),
@@ -184,7 +187,7 @@
   public List<DexDebugEntry> build() {
     // Flush any pending entry.
     if (pending != null) {
-      defaultEventReceived(); // To flush 'pending'.
+      entryEventReceived(false); // To flush 'pending'.
       pending = null;
     }
     List<DexDebugEntry> result = entries;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index bee0910..69fb348 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -216,7 +216,9 @@
   private void emitLocalChanges(int pc) {
     // If pc advanced since the locals changed and locals indeed have changed, emit the changes.
     if (localsChanged()) {
-      emitAdvancementEvents(emittedPc, emittedPosition, pc, emittedPosition, events, factory);
+      assert emittedPc != pc;
+      int pcDelta = emittedPc == NO_PC_INFO ? pc : pc - emittedPc;
+      events.add(factory.createAdvancePC(pcDelta));
       emittedPc = pc;
       emitLocalChangeEvents(emittedLocals, pendingLocals, lastKnownLocals, events, factory);
       pendingLocalChanges = false;
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index bb4891b..ff8bd02 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -44,11 +45,15 @@
 
   public final boolean hasDebugPositions;
 
+  private final InternalOptions options;
+
   public IRCode(
+      InternalOptions options,
       DexEncodedMethod method,
       LinkedList<BasicBlock> blocks,
       ValueNumberGenerator valueNumberGenerator,
       boolean hasDebugPositions) {
+    this.options = options;
     this.method = method;
     this.blocks = blocks;
     this.valueNumberGenerator = valueNumberGenerator;
@@ -251,7 +256,10 @@
     ImmutableList.Builder<BasicBlock> builder = ImmutableList.builder();
     BasicBlock entryBlock = blocks.getFirst();
     depthFirstSorting(visitedBlock, entryBlock, builder);
-    return builder.build().reverse();
+    ImmutableList<BasicBlock> ordered = builder.build().reverse();
+    return options.testing.placeExceptionalBlocksLast
+        ? reorderExceptionalBlocksLastForTesting(ordered)
+        : ordered;
   }
 
   private void depthFirstSorting(Set<BasicBlock> visitedBlock, BasicBlock block,
@@ -265,6 +273,23 @@
     }
   }
 
+  // Reorder the blocks forcing all exceptional blocks to be at the end.
+  private static ImmutableList<BasicBlock> reorderExceptionalBlocksLastForTesting(
+      ImmutableList<BasicBlock> blocks) {
+    ImmutableList.Builder<BasicBlock> reordered = ImmutableList.builder();
+    for (BasicBlock block : blocks) {
+      if (!block.entry().isMoveException()) {
+        reordered.add(block);
+      }
+    }
+    for (BasicBlock block : blocks) {
+      if (block.entry().isMoveException()) {
+        reordered.add(block);
+      }
+    }
+    return reordered.build();
+  }
+
   public void print(CfgPrinter printer) {
     ensureBlockNumbering();
     for (BasicBlock block : blocks) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index eb58c4d..17ebfb5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.cf.LoadStoreHelper;
 import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.graph.AppInfo.ResolutionResult;
 import com.android.tools.r8.graph.AppInfoWithSubtyping;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -118,23 +119,49 @@
     if (targets == null || targets.isEmpty()) {
       return Constraint.NEVER;
     }
+
     Constraint result = Constraint.ALWAYS;
+
+    // Perform resolution and derive inlining constraints based on the accessibility of the
+    // resolution result.
+    ResolutionResult resolutionResult = info.resolveMethod(method.holder, method);
+    DexEncodedMethod resolutionTarget = resolutionResult.asResultOfResolve();
+    if (resolutionTarget == null) {
+      // This will fail at runtime.
+      return Constraint.NEVER;
+    }
+    DexType methodHolder = resolutionTarget.method.holder;
+    DexClass methodClass = info.definitionFor(methodHolder);
+    assert methodClass != null;
+    Constraint methodConstraint = Constraint
+        .deriveConstraint(invocationContext, methodHolder, resolutionTarget.accessFlags, info);
+    result = Constraint.min(result, methodConstraint);
+    // We also have to take the constraint of the enclosing class of the resolution result
+    // into account. We do not allow inlining this method if it is calling something that
+    // is inaccessible. Inlining in that case could move the code to another package making a
+    // call succeed that should not succeed. Conversely, if the resolution result is accessible,
+    // we have to make sure that inlining cannot make it inaccessible.
+    Constraint classConstraint = Constraint
+        .deriveConstraint(invocationContext, methodHolder, methodClass.accessFlags, info);
+    result = Constraint.min(result, classConstraint);
+    if (result == Constraint.NEVER) {
+      return result;
+    }
+
+    // For each of the actual potential targets, derive constraints based on the accessibility
+    // of the method itself.
     for (DexEncodedMethod target : targets) {
-      DexType methodHolder = target.method.holder;
-      DexClass methodClass = info.definitionFor(methodHolder);
-      if ((methodClass != null)) {
-        Constraint methodConstraint = Constraint
-            .deriveConstraint(invocationContext, methodHolder, target.accessFlags, info);
-        result = Constraint.min(result, methodConstraint);
-        // We also have to take the constraint of the enclosing class into account.
-        Constraint classConstraint = Constraint
-            .deriveConstraint(invocationContext, methodHolder, methodClass.accessFlags, info);
-        result = Constraint.min(result, classConstraint);
-      }
+      methodHolder = target.method.holder;
+      methodClass = info.definitionFor(methodHolder);
+      assert methodClass != null;
+      methodConstraint = Constraint
+          .deriveConstraint(invocationContext, methodHolder, target.accessFlags, info);
+      result = Constraint.min(result, methodConstraint);
       if (result == Constraint.NEVER) {
-        break;
+        return result;
       }
     }
+
     return result;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index baf97f6..82840f8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -52,6 +52,7 @@
 
   @Override
   public void buildDex(DexBuilder builder) {
+    assert builder.getOptions().canUseNotInstruction();
     com.android.tools.r8.code.Instruction instruction;
     int dest = builder.allocatedRegister(dest(), getNumber());
     int src = builder.allocatedRegister(source(), getNumber());
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 140184d..55e4b83 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
@@ -234,7 +234,7 @@
       currentPosition = Position.none();
     } else {
       currentPosition = getCanonicalPositionAppendCaller(current);
-      if (current.address == offset) {
+      if (current.lineEntry && current.address == offset) {
         builder.addDebugPosition(currentPosition);
       }
     }
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 0a27017..98c1015 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
@@ -390,7 +390,7 @@
     joinPredecessorsWithIdenticalPhis();
 
     // Package up the IR code.
-    IRCode ir = new IRCode(method, blocks, valueNumberGenerator, hasDebugPositions);
+    IRCode ir = new IRCode(options, method, blocks, valueNumberGenerator, hasDebugPositions);
 
     // Split critical edges to make sure that we have a place to insert phi moves if
     // necessary.
@@ -1258,7 +1258,13 @@
   public void addNot(NumericType type, int dest, int value) {
     Value in = readNumericRegister(value, type);
     Value out = writeNumericRegister(dest, type, ThrowingInfo.NO_THROW);
-    Not instruction = new Not(type, out, in);
+    Instruction instruction;
+    if (options.canUseNotInstruction()) {
+      instruction = new Not(type, out, in);
+    } else {
+      Value minusOne = readLiteral(ValueType.fromNumericType(type), -1);
+      instruction = new Xor(type, out, in, minusOne);
+    }
     assert !instruction.instructionTypeCanThrow();
     addInstruction(instruction);
   }
@@ -1616,6 +1622,22 @@
     return readRegister(register, ValueType.fromNumericType(type));
   }
 
+  public Value readLiteral(ValueType type, long constant) {
+    if (type == ValueType.INT) {
+      return readIntLiteral(constant);
+    } else {
+      assert type == ValueType.LONG;
+      return readLongLiteral(constant);
+    }
+  }
+
+  public Value readLongLiteral(long constant) {
+    Value value = new Value(valueNumberGenerator.next(), ValueType.LONG, null);
+    ConstNumber number = new ConstNumber(value, constant);
+    add(number);
+    return number.outValue();
+  }
+
   public Value readIntLiteral(long constant) {
     Value value = new Value(valueNumberGenerator.next(), ValueType.INT, null);
     ConstNumber number = new ConstNumber(value, constant);
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 bb4b629..39cea77 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
@@ -229,6 +229,7 @@
       throws ExecutionException, ApiLevelException {
     removeLambdaDeserializationMethods();
 
+    timing.begin("IR conversion");
     convertClassesToDex(application.classes(), executor);
 
     // Build a new application with jumbo string info,
@@ -239,6 +240,7 @@
     desugarInterfaceMethods(builder, ExcludeDexResources);
 
     handleSynthesizedClassMapping(builder);
+    timing.end();
 
     return builder.build();
   }
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 e517da9..53416c6 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -9,7 +9,6 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
@@ -23,8 +22,8 @@
   }
 
   @Override
-  Function<DexType, ?> getKeyTransform(ProguardConfiguration config) {
-    if (config.isOverloadAggressively()) {
+  Function<DexType, ?> getKeyTransform() {
+    if (overloadAggressively) {
       // Use the type as the key, hence reuse names per type.
       return a -> a;
     } else {
@@ -88,9 +87,9 @@
   private void renameField(DexEncodedField encodedField, NamingState<DexType, ?> state) {
     DexField field = encodedField.field;
     if (!state.isReserved(field.name, field.type)) {
-      DexString candidate = state.assignNewNameFor(field.name, field.type, false);
-      renaming.put(field, candidate);
-      state.addRenaming(field.name, field.type, candidate);
+      renaming.put(
+          field,
+          state.assignNewNameFor(field.name, field.type, useUniqueMemberNames));
     }
   }
 }
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 4a288be..1fa8aaa 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNameMinifier.java
@@ -7,7 +7,6 @@
 import com.android.tools.r8.graph.CachedHashValueDexItem;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
@@ -25,17 +24,19 @@
   protected final Map<DexType, NamingState<StateType, ?>> states = new IdentityHashMap<>();
   protected final NamingState<StateType, ?> globalState;
   protected final boolean useUniqueMemberNames;
+  protected final boolean overloadAggressively;
 
   MemberNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     this.appInfo = appInfo;
     this.rootSet = rootSet;
     this.dictionary = options.proguardConfiguration.getObfuscationDictionary();
-    this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary,
-        getKeyTransform(options.proguardConfiguration));
     this.useUniqueMemberNames = options.proguardConfiguration.isUseUniqueClassMemberNames();
+    this.overloadAggressively = options.proguardConfiguration.isOverloadAggressively();
+    this.globalState =
+        NamingState.createRoot(appInfo.dexItemFactory, dictionary, getKeyTransform());
   }
 
-  abstract Function<StateType, ?> getKeyTransform(ProguardConfiguration config);
+  abstract Function<StateType, ?> getKeyTransform();
 
   protected NamingState<StateType, ?> computeStateIfAbsent(
       DexType type, Function<DexType, NamingState<StateType, ?>> f) {
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 7b939ae..936a06a 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.MethodJavaSignatureEquivalence;
@@ -90,19 +89,17 @@
 class MethodNameMinifier extends MemberNameMinifier<DexMethod, DexProto> {
 
   private final Equivalence<DexMethod> equivalence;
-  private final ProguardConfiguration config;
 
   MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, InternalOptions options) {
     super(appInfo, rootSet, options);
-    this.config = options.proguardConfiguration;
-    equivalence = config.isOverloadAggressively()
+    equivalence = overloadAggressively
         ? MethodSignatureEquivalence.get()
         : MethodJavaSignatureEquivalence.get();
   }
 
   @Override
-  Function<DexProto, ?> getKeyTransform(ProguardConfiguration config) {
-    if (config.isOverloadAggressively()) {
+  Function<DexProto, ?> getKeyTransform() {
+    if (overloadAggressively) {
       // Use the full proto as key, hence reuse names based on full signature.
       return a -> a;
     } else {
@@ -334,8 +331,7 @@
         computeStateIfAbsent(
             libraryFrontier,
             ignore -> parent == null
-                ? NamingState
-                .createRoot(appInfo.dexItemFactory, dictionary, getKeyTransform(config))
+                ? NamingState.createRoot(appInfo.dexItemFactory, dictionary, getKeyTransform())
                 : parent.createChild());
     if (holder != null) {
       boolean keepAll = holder.isLibraryClass() || holder.accessFlags.isAnnotation();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 902e378..6fc9e17 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -76,7 +76,7 @@
     }
   }
 
-  public boolean printTimes = false;
+  public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
 
   // Flag to toggle if DEX code objects should pass-through without IR processing.
   public boolean passthroughDexCode = false;
@@ -392,6 +392,7 @@
         Function.identity();
 
     public boolean invertConditionals = false;
+    public boolean placeExceptionalBlocksLast = false;
   }
 
   public boolean canUseInvokePolymorphicOnVarHandle() {
diff --git a/src/test/examples/inlining/IFace.java b/src/test/examples/inlining/IFace.java
new file mode 100644
index 0000000..d0402f2
--- /dev/null
+++ b/src/test/examples/inlining/IFace.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, 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 inlining;
+
+public interface IFace {
+  int foo();
+}
diff --git a/src/test/examples/inlining/Inlining.java b/src/test/examples/inlining/Inlining.java
index 77b2979..0705587 100644
--- a/src/test/examples/inlining/Inlining.java
+++ b/src/test/examples/inlining/Inlining.java
@@ -4,6 +4,7 @@
 package inlining;
 
 import inlining.Nullability.Factor;
+import inlining.pkg.InterfaceImplementationContainer;
 import inlining.pkg.OtherPublicClass;
 import inlining.pkg.PublicClass;
 import inlining.pkg.Subclass;
@@ -211,6 +212,8 @@
     } catch (Throwable unexpected) {
       System.out.println("Unexpected exception for notInlinableOnThrow");
     }
+
+    System.out.println(callInterfaceMethod(InterfaceImplementationContainer.getIFace()));
   }
 
   private static boolean intCmpExpression(A a, A b) {
@@ -218,6 +221,11 @@
   }
 
   @CheckDiscarded
+  private static int callInterfaceMethod(IFace i) {
+    return i.foo();
+  }
+
+  @CheckDiscarded
   private static int intConstantInline() {
     return 42;
   }
diff --git a/src/test/examples/inlining/pkg/InterfaceImplementationContainer.java b/src/test/examples/inlining/pkg/InterfaceImplementationContainer.java
new file mode 100644
index 0000000..5135fa2
--- /dev/null
+++ b/src/test/examples/inlining/pkg/InterfaceImplementationContainer.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2018, 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 inlining.pkg;
+
+import inlining.IFace;
+
+public class InterfaceImplementationContainer {
+  private static class IFaceImplementation implements IFace {
+    @Override
+    public int foo() {
+      return 42;
+    }
+  }
+
+  public static IFace getIFace() {
+    return new IFaceImplementation();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java
new file mode 100644
index 0000000..0142dba
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTest.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2018, 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.debug;
+
+public class BreakAtTryAndCatchTest {
+  int i = 0;
+
+  void foo() {
+    try { bar(); } catch (RuntimeException e) { baz(); }
+    baz();
+  }
+
+  int bar() {
+    if (i++ % 2 == 0) {
+      System.out.println("bar return " + i);
+      return i;
+    }
+    System.out.println("bar throw " + i);
+    throw new RuntimeException("" + i);
+  }
+
+  void baz() {
+    System.out.println("baz");
+  }
+
+  public static void main(String[] args) {
+    BreakAtTryAndCatchTest test = new BreakAtTryAndCatchTest();
+    test.foo();
+    test.foo();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java
new file mode 100644
index 0000000..ca2f53f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/BreakAtTryAndCatchTestRunner.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2018, 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.debug;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BreakAtTryAndCatchTestRunner extends DebugTestBase {
+
+  private static final Class CLASS = BreakAtTryAndCatchTest.class;
+  private static final String FILE = CLASS.getSimpleName() + ".java";
+  private static final String NAME = CLASS.getCanonicalName();
+
+  private final String name;
+  private final DebugTestConfig config;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static Collection<Object[]> setup() {
+    DelayedDebugTestConfig cf =
+        temp -> new CfDebugTestConfig().addPaths(ToolHelper.getClassPathForTests());
+    DelayedDebugTestConfig d8 =
+        temp -> new D8DebugTestConfig().compileAndAddClasses(temp, CLASS);
+    DelayedDebugTestConfig d8Reordered =
+        temp -> new D8DebugTestConfig().compileAndAdd(
+            temp,
+            Collections.singletonList(ToolHelper.getClassFileForTestClass(CLASS)),
+            options -> options.testing.placeExceptionalBlocksLast = true);
+    return ImmutableList.of(
+        new Object[]{"CF", cf},
+        new Object[]{"D8", d8},
+        new Object[]{"D8/reorder", d8Reordered}
+    );
+  }
+
+  public BreakAtTryAndCatchTestRunner(String name, DelayedDebugTestConfig config) {
+    this.name = name;
+    this.config = config.getConfig(temp);
+  }
+
+  @Test
+  public void testHitOnEntryOnly() throws Throwable {
+    Assume.assumeFalse("b/72933440", name.equals("D8/reorder"));
+    Assume.assumeFalse("b/73803266",
+        name.equals("D8") && ToolHelper.getDexVm() == DexVm.ART_6_0_1_HOST);
+    runDebugTest(
+        config,
+        NAME,
+        breakpoint(NAME, "foo", 10),
+        run(),
+        checkLine(FILE, 10), // hit line entry, bar does not throw
+        run(),
+        checkLine(FILE, 10), // hit line entry, bar does throw
+        breakpoint(NAME, "main", 31),
+        run(),
+        checkLine(FILE, 31), // No more hits on line, continue to main.
+        run());
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java b/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
index b588590..9fe7e23 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalChangeOnSameLineTestRunner.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.debug;
 
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
 import org.junit.Assume;
@@ -18,6 +19,7 @@
   private static final String FILE = CLASS.getSimpleName() + ".java";
   private static final String NAME = CLASS.getCanonicalName();
 
+  private final String name;
   private final DebugTestConfig config;
 
   @Parameterized.Parameters(name = "{0}")
@@ -30,13 +32,15 @@
   }
 
   public LocalChangeOnSameLineTestRunner(String name, DelayedDebugTestConfig config) {
+    this.name = name;
     this.config = config.getConfig(temp);
   }
 
   /** Test that only hit the break point at line 15 once. */
   @Test
   public void testHitBreakpointOnce() throws Throwable {
-    Assume.assumeFalse("b/72933440 : invalid line info table", config instanceof D8DebugTestConfig);
+    Assume.assumeFalse("b/73803266",
+        name.equals("D8") && ToolHelper.getDexVm() == DexVm.ART_6_0_1_HOST);
     runDebugTest(
         config,
         NAME,
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
index 5d15735..4ce3b5a 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoInspector.java
@@ -125,14 +125,14 @@
   }
 
   private void checkConsistentEntries() {
-    DexDebugEntry previousEntry = null;
+    DexDebugEntry previousLineEntry = null;
     for (DexDebugEntry entry : entries) {
-      if (previousEntry != null) {
+      if (entry.lineEntry) {
         assertTrue(
             "More than one entry defined for PC " + StringUtils.hexString(entry.address, 2),
-            entry.address > previousEntry.address);
+            previousLineEntry == null || entry.address > previousLineEntry.address);
+        previousLineEntry = entry;
       }
-      previousEntry = entry;
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
index d6f86d8..2da7d8c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/ConstantRemovalTest.java
@@ -123,9 +123,10 @@
     LinkedList<BasicBlock> blocks = new LinkedList<>();
     blocks.add(block);
 
-    IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+    InternalOptions options = new InternalOptions();
+    IRCode code = new IRCode(options, null, blocks, new ValueNumberGenerator(), false);
     PeepholeOptimizer.optimize(code,
-        new MockLinearScanRegisterAllocator(code, new InternalOptions()));
+        new MockLinearScanRegisterAllocator(code, options));
 
     // Check that all four constant number instructions remain.
     assertEquals(4,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 9c03ee7..1c72986 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -39,6 +39,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -111,9 +112,13 @@
     assertEquals(javaResult.stdout, artOutput);
   }
 
-  private void checkAbsent(ClassSubject clazz, String name) {
+  private void checkAbsentBooleanMethod(ClassSubject clazz, String name) {
+    checkAbsent(clazz, "boolean", name, Collections.emptyList());
+  }
+
+  private void checkAbsent(ClassSubject clazz, String returnType, String name, List<String> args) {
     assertTrue(clazz.isPresent());
-    MethodSubject method = clazz.method("boolean", name, Collections.emptyList());
+    MethodSubject method = clazz.method(returnType, name, args);
     assertFalse(method.isPresent());
   }
 
@@ -134,20 +139,24 @@
     DexInspector inspector = new DexInspector(getGeneratedDexFile().toAbsolutePath(),
         getGeneratedProguardMap());
     ClassSubject clazz = inspector.clazz("inlining.Inlining");
+
     // Simple constant inlining.
-    checkAbsent(clazz, "longExpression");
-    checkAbsent(clazz, "intExpression");
-    checkAbsent(clazz, "doubleExpression");
-    checkAbsent(clazz, "floatExpression");
+    checkAbsentBooleanMethod(clazz, "longExpression");
+    checkAbsentBooleanMethod(clazz, "intExpression");
+    checkAbsentBooleanMethod(clazz, "doubleExpression");
+    checkAbsentBooleanMethod(clazz, "floatExpression");
     // Simple return argument inlining.
-    checkAbsent(clazz, "longArgumentExpression");
-    checkAbsent(clazz, "intArgumentExpression");
-    checkAbsent(clazz, "doubleArgumentExpression");
-    checkAbsent(clazz, "floatArgumentExpression");
+    checkAbsentBooleanMethod(clazz, "longArgumentExpression");
+    checkAbsentBooleanMethod(clazz, "intArgumentExpression");
+    checkAbsentBooleanMethod(clazz, "doubleArgumentExpression");
+    checkAbsentBooleanMethod(clazz, "floatArgumentExpression");
+    // Static method calling interface method. The interface method implementation is in
+    // a private class in another package.
+    checkAbsent(clazz, "int", "callInterfaceMethod", ImmutableList.of("inlining.IFace"));
 
     clazz = inspector.clazz("inlining.Nullability");
-    checkAbsent(clazz, "inlinableWithPublicField");
-    checkAbsent(clazz, "inlinableWithControlFlow");
+    checkAbsentBooleanMethod(clazz, "inlinableWithPublicField");
+    checkAbsentBooleanMethod(clazz, "inlinableWithControlFlow");
   }
 
   @Test
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 699ebb4..6e02a17 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
@@ -62,7 +62,7 @@
     // Check that the goto in block0 remains. There was a bug in the trivial goto elimination
     // that ended up removing that goto changing the code to start with the unreachable
     // throw.
-    IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+    IRCode code = new IRCode(null, null, blocks, new ValueNumberGenerator(), false);
     CodeRewriter.collapsTrivialGotos(null, code);
     assertTrue(code.blocks.get(0).isTrivialGoto());
     assertTrue(blocks.contains(block0));
@@ -130,7 +130,7 @@
     // Check that the goto in block0 remains. There was a bug in the trivial goto elimination
     // that ended up removing that goto changing the code to start with the unreachable
     // throw.
-    IRCode code = new IRCode(null, blocks, new ValueNumberGenerator(), false);
+    IRCode code = new IRCode(null, null, blocks, new ValueNumberGenerator(), false);
     CodeRewriter.collapsTrivialGotos(null, code);
     assertTrue(block0.getInstructions().get(1).isIf());
     assertEquals(block1, block0.getInstructions().get(1).asIf().fallthroughBlock());
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 4362d7d..64d092b 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.utils.StringUtils.BraceType;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import jasmin.ClassFile;
 import java.io.ByteArrayOutputStream;
 import java.io.StringReader;
@@ -144,6 +145,10 @@
       return addMethod("static", name, argumentTypes, returnType, lines);
     }
 
+    public MethodSignature addMainMethod(Iterable<String> lines) {
+      return addMainMethod(Iterables.toArray(lines, String.class));
+    }
+
     public MethodSignature addMainMethod(String... lines) {
       return addStaticMethod("main", ImmutableList.of("[Ljava/lang/String;"), "V", lines);
     }
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index af8382e..bdedf43 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -116,9 +116,8 @@
 
   static final Map<String, TestPredicate> FAILING_TESTS =
       ImmutableMap.<String, TestPredicate>builder()
-          // ART line-table currently returns too many line entries.
-          .put("LineTableDuplicatesTest", RunJdwpTests::isJava)
           // Other failures on various older runtimes.
+          .put("LineTableDuplicatesTest", or(RunJdwpTests::isJava, RunJdwpTests::isAndroidNOrAbove))
           .put("ArrayReference.GetValuesTest", RunJdwpTests::isAndroidLOrAbove)
           .put("ArrayReference.LengthTest", RunJdwpTests::isAndroidLOrAbove)
           .put("ArrayReference.SetValues003Test", RunJdwpTests::isAndroidNOrAbove)
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
index 365ae83..51523f2 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/ValidNameConflictTest.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.utils.DexInspector.FieldSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -33,6 +34,7 @@
 @RunWith(Parameterized.class)
 public class ValidNameConflictTest extends JasminTestBase {
   private final String CLASS_NAME = "Example";
+  private final String SUPER_CLASS = "Super";
   private final String ANOTHER_CLASS = "Test";
   private final String MSG = "Expected to be seen at the end.";
 
@@ -53,20 +55,15 @@
     return testCases;
   }
 
-  private JasminBuilder buildFieldNameConflictClassFile() throws Exception {
-    JasminBuilder builder = new JasminBuilder();
-    ClassBuilder classBuilder = builder.addClass(CLASS_NAME);
-    classBuilder.addStaticField("same", "Ljava/lang/Object;", null);
-    classBuilder.addStaticField("same", "Ljava/lang/String;", "\"" + MSG + "\"");
-    classBuilder.addMainMethod(
-        ".limit stack 3",
-        ".limit locals 4",
-        "  ldc Example",
-        "  invokevirtual java/lang/Class/getDeclaredFields()[Ljava/lang/reflect/Field;",
-        "  astore_0",  // Field[]
+  private Iterable<String> buildCodeForVisitingDeclaredMembers(
+      Iterable<String> prologue, Iterable<String> argumentLoadingAndCall) {
+    return Iterables.concat(
+        prologue,
+        ImmutableList.of(
+        "  astore_0",  // Member[]
         "  aload_0",
         "  arraylength",
-        "  istore_1",  // Field[].length
+        "  istore_1",  // Member[].length
         "  iconst_0",
         "  istore_2",  // counter
         "loop:",
@@ -75,22 +72,39 @@
         "  if_icmpge end",
         "  aload_0",
         "  iload_2",
-        "  aaload",  // Field[counter]
+        "  aaload",  // Member[counter]
         "  astore_3",
         "  getstatic java/lang/System/out Ljava/io/PrintStream;",
-        "  aload_3",
-        "  aconst_null",
-        "  invokevirtual java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;",
+        "  aload_3"),
+        argumentLoadingAndCall,
+        ImmutableList.of(
         "  invokevirtual java/io/PrintStream/println(Ljava/lang/Object;)V",
         "  iinc 2 1",  // counter++
         "  goto loop",
         "end:",
-        "  return");
+        "  return"));
+  }
+
+  private JasminBuilder buildFieldNameConflictClassFile() {
+    JasminBuilder builder = new JasminBuilder();
+    ClassBuilder classBuilder = builder.addClass(CLASS_NAME);
+    classBuilder.addStaticField("same", "Ljava/lang/Object;", null);
+    classBuilder.addStaticField("same", "Ljava/lang/String;", "\"" + MSG + "\"");
+    classBuilder.addMainMethod(
+        buildCodeForVisitingDeclaredMembers(
+            ImmutableList.of(
+                ".limit stack 3",
+                ".limit locals 4",
+                "  ldc " + CLASS_NAME,
+                "  invokevirtual java/lang/Class/getDeclaredFields()[Ljava/lang/reflect/Field;"),
+            ImmutableList.of(
+                "  aconst_null",
+                "  invokevirtual java/lang/reflect/Field/get(Ljava/lang/Object;)Ljava/lang/Object;")));
     return builder;
   }
 
   @Test
-  public void remainFieldNameConflictDueToKeepRules() throws Exception {
+  public void remainFieldNameConflict_keepRules() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildFieldNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -123,7 +137,7 @@
 
 
   @Test
-  public void remainFieldNameConflictWithUseUniqueClassMemberNames() throws Exception {
+  public void remainFieldNameConflict_useuniqueclassmembernames() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildFieldNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -152,7 +166,38 @@
   }
 
   @Test
-  public void resolveFieldNameConflictWithoutAnyOption() throws Exception {
+  public void remainFieldNameConflict_useuniqueclassmembernames_overloadaggressively()
+      throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    JasminBuilder builder = buildFieldNameConflictClassFile();
+    ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+    assertEquals(0, javaOutput.exitCode);
+
+    List<String> pgConfigs = ImmutableList.of(
+        keepMainProguardConfiguration(CLASS_NAME),
+        "-useuniqueclassmembernames",
+        "-overloadaggressively",  // no-op
+        "-dontshrink");
+    AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+    DexInspector dexInspector = new DexInspector(app);
+    ClassSubject clazz = dexInspector.clazz(CLASS_NAME);
+    assertTrue(clazz.isPresent());
+    FieldSubject f1 = clazz.field("java.lang.String", "same");
+    assertTrue(f1.isPresent());
+    assertTrue(f1.isRenamed());
+    FieldSubject f2 = clazz.field("java.lang.Object", "same");
+    assertTrue(f2.isPresent());
+    assertTrue(f2.isRenamed());
+    assertEquals(f1.getField().field.name, f2.getField().field.name);
+
+    ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(javaOutput.stdout, artOutput.stdout);
+  }
+
+  @Test
+  public void resolveFieldNameConflict_no_options() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildFieldNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -172,8 +217,7 @@
     FieldSubject f2 = clazz.field("java.lang.Object", "same");
     assertTrue(f2.isPresent());
     assertTrue(f2.isRenamed());
-    // TODO(b/73149686): R8 should be able to fix this conflict w/o -overloadaggressively.
-    assertEquals(f1.getField().field.name, f2.getField().field.name);
+    assertNotEquals(f1.getField().field.name, f2.getField().field.name);
 
     ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
     assertEquals(0, artOutput.exitCode);
@@ -181,7 +225,7 @@
   }
 
   @Test
-  public void resolveFieldNameConflictEvenWithOverloadAggressively() throws Exception {
+  public void remainFieldNameConflict_overloadaggressively() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildFieldNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -202,7 +246,6 @@
     FieldSubject f2 = clazz.field("java.lang.Object", "same");
     assertTrue(f2.isPresent());
     assertTrue(f2.isRenamed());
-    // TODO(b/72858955): R8 should resolve this field name conflict.
     assertEquals(f1.getField().field.name, f2.getField().field.name);
 
     ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
@@ -210,7 +253,7 @@
     assertEquals(javaOutput.stdout, artOutput.stdout);
   }
 
-  private JasminBuilder buildMethodNameConflictClassFile() throws Exception {
+  private JasminBuilder buildMethodNameConflictClassFile() {
     JasminBuilder builder = new JasminBuilder();
     ClassBuilder classBuilder = builder.addClass(ANOTHER_CLASS);
     classBuilder.addStaticMethod("same", ImmutableList.of(), "Ljava/lang/Object;",
@@ -221,41 +264,23 @@
         "areturn");
     classBuilder = builder.addClass(CLASS_NAME);
     classBuilder.addMainMethod(
-        ".limit stack 3",
-        ".limit locals 4",
-        "  ldc Test",
-        "  invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;",
-        "  astore_0",  // Method[]
-        "  aload_0",
-        "  arraylength",
-        "  istore_1",  // Method[].length
-        "  iconst_0",
-        "  istore_2",  // counter
-        "loop:",
-        "  iload_2",
-        "  iload_1",
-        "  if_icmpge end",
-        "  aload_0",
-        "  iload_2",
-        "  aaload",  // Method[counter]
-        "  astore_3",
-        "  getstatic java/lang/System/out Ljava/io/PrintStream;",
-        "  aload_3",
-        "  aconst_null",
-        "  aconst_null",
-        "  checkcast [Ljava/lang/Object;",
-        "  invokevirtual java/lang/reflect/Method/invoke"
-            + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
-        "  invokevirtual java/io/PrintStream/println(Ljava/lang/Object;)V",
-        "  iinc 2 1",  // counter++
-        "  goto loop",
-        "end:",
-        "  return");
+        buildCodeForVisitingDeclaredMembers(
+            ImmutableList.of(
+                ".limit stack 3",
+                ".limit locals 4",
+                "  ldc " + ANOTHER_CLASS,
+                "  invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;"),
+            ImmutableList.of(
+                "  aconst_null",
+                "  aconst_null",
+                "  checkcast [Ljava/lang/Object;",
+                "  invokevirtual java/lang/reflect/Method/invoke"
+                    + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
     return builder;
   }
 
   @Test
-  public void remainMethodNameConflictDueToKeepRules() throws Exception {
+  public void remainMethodNameConflict_keepRules() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildMethodNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -287,7 +312,7 @@
   }
 
   @Test
-  public void remainMethodNameConflictWithUseUniqueClassMemberNames() throws Exception {
+  public void remainMethodNameConflict_useuniqueclassmembernames() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildMethodNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -316,7 +341,39 @@
   }
 
   @Test
-  public void resolveMethodNameConflictWithoutAnyOption() throws Exception {
+  public void remainMethodNameConflict_useuniqueclassmembernames_overloadaggressively()
+      throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    JasminBuilder builder = buildMethodNameConflictClassFile();
+    ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+    assertEquals(0, javaOutput.exitCode);
+
+    List<String> pgConfigs = ImmutableList.of(
+        keepMainProguardConfiguration(CLASS_NAME),
+        "-useuniqueclassmembernames",
+        "-overloadaggressively",  // no-op
+        "-dontshrink");
+    AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+    DexInspector dexInspector = new DexInspector(app);
+    ClassSubject clazz = dexInspector.clazz(ANOTHER_CLASS);
+    assertTrue(clazz.isPresent());
+    MethodSubject m1 = clazz.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(m1.isPresent());
+    assertTrue(m1.isRenamed());
+    MethodSubject m2 = clazz.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(m2.isPresent());
+    assertTrue(m2.isRenamed());
+    assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+    ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(javaOutput.stdout, artOutput.stdout);
+  }
+
+
+  @Test
+  public void resolveMethodNameConflict_no_options() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildMethodNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -345,7 +402,7 @@
   }
 
   @Test
-  public void resolveMethodNameConflictEvenWithOverloadAggressively() throws Exception {
+  public void remainMethodNameConflict_overloadaggressively() throws Exception {
     Assume.assumeTrue(ToolHelper.artSupported());
     JasminBuilder builder = buildMethodNameConflictClassFile();
     ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
@@ -366,7 +423,6 @@
     MethodSubject m2 = clazz.method("java.lang.Object", "same", ImmutableList.of());
     assertTrue(m2.isPresent());
     assertTrue(m2.isRenamed());
-    // TODO(b/73149686): R8 should be able to fix this conflict even w/ -overloadaggressively.
     assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
 
     ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
@@ -374,4 +430,265 @@
     assertEquals(javaOutput.stdout, artOutput.stdout);
   }
 
+  private JasminBuilder buildMethodNameConflictInHierarchy() {
+    JasminBuilder builder = new JasminBuilder();
+    ClassBuilder classBuilder = builder.addClass(SUPER_CLASS);
+    classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/Object;",
+        "aconst_null",
+        "areturn");
+    classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/String;",
+        "ldc \"" + MSG + "\"",
+        "areturn");
+    classBuilder = builder.addClass(ANOTHER_CLASS, SUPER_CLASS);
+    classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/Object;",
+        "aload_0",
+        "invokespecial " + SUPER_CLASS + "/same()Ljava/lang/Object;",
+        "areturn");
+    classBuilder.addVirtualMethod("same", ImmutableList.of(), "Ljava/lang/String;",
+        "aload_0",
+        "invokespecial " + SUPER_CLASS + "/same()Ljava/lang/String;",
+        "areturn");
+    classBuilder = builder.addClass(CLASS_NAME);
+    classBuilder.addMainMethod(
+        buildCodeForVisitingDeclaredMembers(
+            ImmutableList.of(
+                ".limit stack 3",
+                ".limit locals 5",
+                "  new " + ANOTHER_CLASS,
+                "  dup",
+                "  invokespecial " + ANOTHER_CLASS + "/<init>()V",
+                "  astore 4",
+                "  ldc " + ANOTHER_CLASS,
+                "  invokevirtual java/lang/Class/getDeclaredMethods()[Ljava/lang/reflect/Method;"),
+            ImmutableList.of(
+                "  aload 4",  // instance
+                "  aconst_null",
+                "  checkcast [Ljava/lang/Object;",
+                "  invokevirtual java/lang/reflect/Method/invoke"
+                    + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;")));
+    return builder;
+  }
+
+  @Test
+  public void remainMethodNameConflictInHierarchy_keepRules() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    JasminBuilder builder = buildMethodNameConflictInHierarchy();
+    ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+    assertEquals(0, javaOutput.exitCode);
+
+    List<String> pgConfigs = ImmutableList.of(
+        "-keep class " + ANOTHER_CLASS + " {\n"
+            + "  <methods>;"
+            + "}\n",
+        keepMainProguardConfiguration(CLASS_NAME),
+        "-useuniqueclassmembernames",
+        "-dontshrink");
+    AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+    DexInspector dexInspector = new DexInspector(app);
+    ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+    assertTrue(sup.isPresent());
+    MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(m1.isPresent());
+    assertFalse(m1.isRenamed());
+    MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(m2.isPresent());
+    assertFalse(m2.isRenamed());
+    assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+    ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+    assertTrue(sub.isPresent());
+    MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(subM1.isPresent());
+    assertFalse(subM1.isRenamed());
+    MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(subM2.isPresent());
+    assertFalse(subM2.isRenamed());
+    assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+    // No matter what, overloading methods should be renamed to the same name.
+    assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+    assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+    ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(javaOutput.stdout, artOutput.stdout);
+  }
+
+  @Test
+  public void remainMethodNameConflictInHierarchy_useuniqueclassmembernames() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    JasminBuilder builder = buildMethodNameConflictInHierarchy();
+    ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+    assertEquals(0, javaOutput.exitCode);
+
+    List<String> pgConfigs = ImmutableList.of(
+        keepMainProguardConfiguration(CLASS_NAME),
+        "-useuniqueclassmembernames",
+        "-dontshrink");
+    AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+    DexInspector dexInspector = new DexInspector(app);
+    ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+    assertTrue(sup.isPresent());
+    MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(m1.isPresent());
+    assertTrue(m1.isRenamed());
+    MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(m2.isPresent());
+    assertTrue(m2.isRenamed());
+    assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+    ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+    assertTrue(sub.isPresent());
+    MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(subM1.isPresent());
+    assertTrue(subM1.isRenamed());
+    MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(subM2.isPresent());
+    assertTrue(subM2.isRenamed());
+    assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+    // No matter what, overloading methods should be renamed to the same name.
+    assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+    assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+    ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(javaOutput.stdout, artOutput.stdout);
+  }
+
+  @Test
+  public void remainMethodNameConflictInHierarchy_useuniqueclassmembernames_overloadaggressively()
+      throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    JasminBuilder builder = buildMethodNameConflictInHierarchy();
+    ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+    assertEquals(0, javaOutput.exitCode);
+
+    List<String> pgConfigs = ImmutableList.of(
+        keepMainProguardConfiguration(CLASS_NAME),
+        "-useuniqueclassmembernames",
+        "-overloadaggressively",  // no-op
+        "-dontshrink");
+    AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+    DexInspector dexInspector = new DexInspector(app);
+    ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+    assertTrue(sup.isPresent());
+    MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(m1.isPresent());
+    assertTrue(m1.isRenamed());
+    MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(m2.isPresent());
+    assertTrue(m2.isRenamed());
+    assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+    ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+    assertTrue(sub.isPresent());
+    MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(subM1.isPresent());
+    assertTrue(subM1.isRenamed());
+    MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(subM2.isPresent());
+    assertTrue(subM2.isRenamed());
+    assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+    // No matter what, overloading methods should be renamed to the same name.
+    assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+    assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+    ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(javaOutput.stdout, artOutput.stdout);
+  }
+
+
+  @Test
+  public void resolveMethodNameConflictInHierarchy_no_options() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    JasminBuilder builder = buildMethodNameConflictInHierarchy();
+    ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+    assertEquals(0, javaOutput.exitCode);
+
+    List<String> pgConfigs = ImmutableList.of(
+        keepMainProguardConfiguration(CLASS_NAME),
+        "-dontshrink");
+    AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+    DexInspector dexInspector = new DexInspector(app);
+    ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+    assertTrue(sup.isPresent());
+    MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(m1.isPresent());
+    assertTrue(m1.isRenamed());
+    MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(m2.isPresent());
+    assertTrue(m2.isRenamed());
+    // TODO(b/73149686): R8 should be able to fix this conflict w/o -overloadaggressively.
+    assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+    ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+    assertTrue(sub.isPresent());
+    MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(subM1.isPresent());
+    assertTrue(subM1.isRenamed());
+    MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(subM2.isPresent());
+    assertTrue(subM2.isRenamed());
+    // TODO(b/73149686): R8 should be able to fix this conflict w/o -overloadaggressively.
+    assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+    // No matter what, overloading methods should be renamed to the same name.
+    assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+    assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+    ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(javaOutput.stdout, artOutput.stdout);
+  }
+
+  @Test
+  public void remainMethodNameConflictInHierarchy_overloadaggressively() throws Exception {
+    Assume.assumeTrue(ToolHelper.artSupported());
+    JasminBuilder builder = buildMethodNameConflictInHierarchy();
+    ProcessResult javaOutput = runOnJavaNoVerifyRaw(builder, CLASS_NAME);
+    assertEquals(0, javaOutput.exitCode);
+
+    List<String> pgConfigs = ImmutableList.of(
+        keepMainProguardConfiguration(CLASS_NAME),
+        "-overloadaggressively",
+        "-dontshrink");
+    AndroidApp app = compileWithR8(builder, pgConfigs, null);
+
+    DexInspector dexInspector = new DexInspector(app);
+
+    ClassSubject sup = dexInspector.clazz(SUPER_CLASS);
+    assertTrue(sup.isPresent());
+    MethodSubject m1 = sup.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(m1.isPresent());
+    assertTrue(m1.isRenamed());
+    MethodSubject m2 = sup.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(m2.isPresent());
+    assertTrue(m2.isRenamed());
+    assertEquals(m1.getMethod().method.name, m2.getMethod().method.name);
+
+    ClassSubject sub = dexInspector.clazz(ANOTHER_CLASS);
+    assertTrue(sub.isPresent());
+    MethodSubject subM1 = sub.method("java.lang.String", "same", ImmutableList.of());
+    assertTrue(subM1.isPresent());
+    assertTrue(subM1.isRenamed());
+    MethodSubject subM2 = sub.method("java.lang.Object", "same", ImmutableList.of());
+    assertTrue(subM2.isPresent());
+    assertTrue(subM2.isRenamed());
+    assertEquals(subM1.getMethod().method.name, subM2.getMethod().method.name);
+
+    // No matter what, overloading methods should be renamed to the same name.
+    assertEquals(m1.getMethod().method.name, subM1.getMethod().method.name);
+    assertEquals(m2.getMethod().method.name, subM2.getMethod().method.name);
+
+    ProcessResult artOutput = runOnArtRaw(app, CLASS_NAME, dexVm);
+    assertEquals(0, artOutput.exitCode);
+    assertEquals(javaOutput.stdout, artOutput.stdout);
+  }
 }