Merge "Extend reflective analysis to include java.lang.Enum.valueOf"
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index eed1c47..9d36037 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -108,31 +108,12 @@
         // goto A
         //
         // A: ...y // blockWithNonNullInstruction
-        //
+        boolean split = block.hasCatchHandlers();
         BasicBlock blockWithNonNullInstruction =
-            block.hasCatchHandlers() ? iterator.split(code, blockIterator) : block;
-        // Next, add non-null fake IR, e.g.,
-        // ...x
-        // invoke(rcv, ...)
-        // goto A
-        // ...
-        // A: non_null_rcv <- non-null(rcv)
-        // ...y
-        Value nonNullValue = code.createValue(
-            knownToBeNonNullValue.getTypeLattice(),
-            knownToBeNonNullValue.getLocalInfo());
-        nonNullValueCollector.add(nonNullValue);
-        NonNull nonNull = new NonNull(nonNullValue, knownToBeNonNullValue, current);
-        nonNull.setPosition(current.getPosition());
-        if (blockWithNonNullInstruction !=  block) {
-          // If we split, add non-null IR on top of the new split block.
-          blockWithNonNullInstruction.listIterator().add(nonNull);
-        } else {
-          // Otherwise, just add it to the current block at the position of the iterator.
-          iterator.add(nonNull);
-        }
-        // Then, replace all users of the original value that are dominated by either the current
-        // block or the new split-off block. Since NPE can be explicitly caught, nullness should be
+            split ? iterator.split(code, blockIterator) : block;
+
+        // Find all users of the original value that are dominated by either the current block
+        // or the new split-off block. Since NPE can be explicitly caught, nullness should be
         // propagated through dominance.
         Set<Instruction> users = knownToBeNonNullValue.uniqueUsers();
         Set<Instruction> dominatedUsers = Sets.newIdentityHashSet();
@@ -142,14 +123,13 @@
         for (BasicBlock dominatee : dominatorTree.dominatedBlocks(blockWithNonNullInstruction)) {
           dominatedBlocks.add(dominatee);
           InstructionListIterator dominateeIterator = dominatee.listIterator();
-          if (dominatee == blockWithNonNullInstruction) {
-            // In the block with the inserted non null instruction, skip instructions up to and
-            // including the newly inserted instruction.
-            dominateeIterator.nextUntil(instruction -> instruction == nonNull);
+          if (dominatee == blockWithNonNullInstruction && !split) {
+            // In the block where the non null instruction will be inserted, skip instructions up
+            // to and including the insertion point.
+            dominateeIterator.nextUntil(instruction -> instruction == current);
           }
           while (dominateeIterator.hasNext()) {
             Instruction potentialUser = dominateeIterator.next();
-            assert potentialUser != nonNull;
             if (users.contains(potentialUser)) {
               dominatedUsers.add(potentialUser);
             }
@@ -162,8 +142,35 @@
             dominatedPhiUsersWithPositions.put(user, dominatedPredecessorIndexes);
           }
         }
-        knownToBeNonNullValue.replaceSelectiveUsers(
-            nonNullValue, dominatedUsers, dominatedPhiUsersWithPositions);
+
+        // Only insert non-null instruction if it is ever used.
+        if (!dominatedUsers.isEmpty() || !dominatedPhiUsersWithPositions.isEmpty()) {
+          // Add non-null fake IR, e.g.,
+          // ...x
+          // invoke(rcv, ...)
+          // goto A
+          // ...
+          // A: non_null_rcv <- non-null(rcv)
+          // ...y
+          Value nonNullValue =
+              code.createValue(
+                  knownToBeNonNullValue.getTypeLattice(), knownToBeNonNullValue.getLocalInfo());
+          nonNullValueCollector.add(nonNullValue);
+          NonNull nonNull = new NonNull(nonNullValue, knownToBeNonNullValue, current);
+          nonNull.setPosition(current.getPosition());
+          if (blockWithNonNullInstruction != block) {
+            // If we split, add non-null IR on top of the new split block.
+            blockWithNonNullInstruction.listIterator().add(nonNull);
+          } else {
+            // Otherwise, just add it to the current block at the position of the iterator.
+            iterator.add(nonNull);
+          }
+
+          // Replace all users of the original value that are dominated by either the current
+          // block or the new split-off block.
+          knownToBeNonNullValue.replaceSelectiveUsers(
+              nonNullValue, dominatedUsers, dominatedPhiUsersWithPositions);
+        }
       }
 
       // Add non-null on top of the successor block if the current block ends with a null check.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
index 564a81c..e569ad2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullTrackerTest.java
@@ -69,6 +69,8 @@
             || NonNullTracker.throwsOnNullInput(prev)
             || (prev.isIf() && prev.asIf().isZeroTest())
             || !curr.getBlock().getPredecessors().contains(prev.getBlock()));
+        // Make sure non-null is used.
+        assertTrue(curr.outValue().numberOfAllUsers() > 0);
         count++;
       }
     }
@@ -114,7 +116,7 @@
     buildAndTest(NonNullAfterInvoke.class, foo, 1, this::checkInvokeGetsNonNullReceiver);
     MethodSignature bar =
         new MethodSignature("bar", "int", new String[]{"java.lang.String"});
-    buildAndTest(NonNullAfterInvoke.class, bar, 2, this::checkInvokeGetsNullReceiver);
+    buildAndTest(NonNullAfterInvoke.class, bar, 1, this::checkInvokeGetsNullReceiver);
   }
 
   @Test
@@ -176,6 +178,6 @@
     buildAndTest(NonNullAfterNullCheck.class, bar, 1, this::checkInvokeGetsNonNullReceiver);
     MethodSignature baz =
         new MethodSignature("baz", "int", new String[]{"java.lang.String"});
-    buildAndTest(NonNullAfterNullCheck.class, baz, 2, this::checkInvokeGetsNullReceiver);
+    buildAndTest(NonNullAfterNullCheck.class, baz, 1, this::checkInvokeGetsNullReceiver);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
index 7ef1c3f..e1c61c1 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -61,17 +62,47 @@
     return StringUtils.splitLines(ToolHelper.runRetrace(mapFile, stackTraceFile));
   }
 
+  private boolean isDalvik() {
+    return backend == Backend.DEX && ToolHelper.getDexVm().isOlderThanOrEqual(DexVm.ART_4_4_4_HOST);
+  }
+
   private List<String> extractStackTrace(ProcessResult result) {
     ImmutableList.Builder<String> builder = ImmutableList.builder();
     List<String> stderr = StringUtils.splitLines(result.stderr);
     Iterator<String> iterator = stderr.iterator();
-    while (iterator.hasNext()) {
-      String line = iterator.next();
-      if (line.startsWith("Exception in thread \"main\"")) {
-        break;
-      }
+
+    // A Dalvik stacktrace looks like this:
+    // W(209693) threadid=1: thread exiting with uncaught exception (group=0xf616cb20)  (dalvikvm)
+    // java.lang.NullPointerException
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:133)
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:139)
+    // \tat com.android.tools.r8.naming.retrace.Main.main(:145)
+    // \tat dalvik.system.NativeStart.main(Native Method)
+    //
+    // An Art 5.1.1 and 6.0.1 stacktrace looks like this:
+    // java.lang.NullPointerException: throw with null exception
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:154)
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:160)
+    // \tat com.android.tools.r8.naming.retrace.Main.main(:166)
+    //
+    // An Art 7.0.0 and latest stacktrace looks like this:
+    // Exception in thread "main" java.lang.NullPointerException: throw with null exception
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:150)
+    // \tat com.android.tools.r8.naming.retrace.Main.a(:156)
+    // \tat com.android.tools.r8.naming.retrace.Main.main(:162)
+    int last = stderr.size();
+    if (isDalvik()) {
+      // Skip the bottom frame "dalvik.system.NativeStart.main".
+      last--;
     }
-    iterator.forEachRemaining(builder::add);
+    // Take all lines from the bottom starting with "\tat ".
+    int first = last;
+    while (first - 1 >= 0 && stderr.get(first - 1).startsWith("\tat ")) {
+      first--;
+    }
+    for (int i = first; i < last; i++) {
+      builder.add(stderr.get(i));
+    }
     return builder.build();
   }