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();
}