Add non-default implementation of peekNext() / peekPrevious() where applicable
And make IteratorUtils.peekPrevious() return null (instead of throw)
when !iterator.hasPrevious().
These methods sound like they should not have side-effects, but they
can change which value is removed when remove() is called.
E.g.: For A->B->C->D:
it.next() --> current = A
it.next() --> current = B
it.peekNext()
it.next() --> current = C
it.previous() --> current = C
This change tries to guard against remove() being called after peek
methods by making it trigger InvalidStateException() in most cases.
Bug: None
Change-Id: I224e79043f96ea3028b3d95969bdc0f9e1d2f534
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 54531af..640d3bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -84,6 +84,16 @@
}
@Override
+ public Instruction peekNext() {
+ // Reset current since listIterator.remove() changes based on whether next() or previous() was
+ // last called.
+ // E.g.: next() -> current=C
+ // peekNext(): next() -> current=D, previous() -> current=D
+ current = null;
+ return IteratorUtils.peekNext(listIterator);
+ }
+
+ @Override
public boolean hasPrevious() {
return listIterator.hasPrevious();
}
@@ -100,6 +110,16 @@
}
@Override
+ public Instruction peekPrevious() {
+ // Reset current since listIterator.remove() changes based on whether next() or previous() was
+ // last called.
+ // E.g.: previous() -> current=B
+ // peekPrevious(): previous() -> current=A, next() -> current=A
+ current = null;
+ return IteratorUtils.peekPrevious(listIterator);
+ }
+
+ @Override
public boolean hasInsertionPosition() {
return position != null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index 4874229..325cea8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -23,7 +23,7 @@
public class IRCodeInstructionListIterator implements InstructionListIterator {
- private final ListIterator<BasicBlock> blockIterator;
+ private final BasicBlockIterator blockIterator;
private InstructionListIterator instructionIterator;
private final IRCode code;
@@ -174,6 +174,16 @@
}
@Override
+ public Instruction peekNext() {
+ // Default impl calls next() / previous(), which affects what remove() does.
+ Instruction next = instructionIterator.peekNext();
+ if (next == null && blockIterator.hasNext()) {
+ next = blockIterator.peekNext().entry();
+ }
+ return next;
+ }
+
+ @Override
public boolean hasPrevious() {
return instructionIterator.hasPrevious() || blockIterator.hasPrevious();
}
@@ -193,6 +203,16 @@
}
@Override
+ public Instruction peekPrevious() {
+ // Default impl calls next() / previous(), which affects what remove() does.
+ Instruction previous = instructionIterator.peekPrevious();
+ if (previous == null && blockIterator.hasPrevious()) {
+ previous = blockIterator.peekPrevious().exit();
+ }
+ return previous;
+ }
+
+ @Override
public int nextIndex() {
throw new UnsupportedOperationException();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 24e7793..21ab96c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -249,6 +249,27 @@
return currentBlockIterator.next();
}
+ @Override
+ public Instruction peekNext() {
+ // Default impl calls next() / previous(), which will alter this.seen.
+ Instruction current = currentBlockIterator.peekNext();
+ if (!current.isGoto()) {
+ return current;
+ }
+ BasicBlock target = current.asGoto().getTarget();
+ if (!isLinearEdge(currentBlock, target)) {
+ return current;
+ }
+ while (target.isTrivialGoto()) {
+ BasicBlock candidate = target.exit().asGoto().getTarget();
+ if (!isLinearEdge(target, candidate)) {
+ break;
+ }
+ target = candidate;
+ }
+ return target.entry();
+ }
+
private BasicBlock getBeginningOfTrivialLinearGotoChain(BasicBlock block) {
if (block.getPredecessors().size() != 1
|| !isLinearEdge(block.getPredecessors().get(0), block)) {
@@ -290,6 +311,20 @@
}
@Override
+ public Instruction peekPrevious() {
+ // Default impl calls next() / previous(), which will alter this.seen.
+ Instruction ret = currentBlockIterator.peekPrevious();
+ if (ret != null) {
+ return ret;
+ }
+ BasicBlock target = getBeginningOfTrivialLinearGotoChain(currentBlock);
+ if (target == null || target.size() < 2) {
+ return null;
+ }
+ return target.getInstructions().get(target.size() - 2);
+ }
+
+ @Override
public int nextIndex() {
throw new UnsupportedOperationException();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
index 740c180..e4845ca 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/DexConstantOptimizer.java
@@ -489,7 +489,8 @@
addConstantInBlock
.computeIfAbsent(dominator, k -> new LinkedHashMap<>())
.put(copy.outValue(), copy);
- assert iterator.peekPrevious() == instruction;
+ // Using peekPrevious() would disable remove().
+ assert iterator.previous() == instruction && iterator.next() == instruction;
iterator.removeOrReplaceByDebugLocalRead();
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
index 506d1e3b..6730a23 100644
--- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -88,13 +88,24 @@
return null;
}
+ /**
+ * Returns the previous element or null if !hasPrevious(). A subsequent call to iterator.remove()
+ * will remove the peeked element.
+ */
public static <T> T peekPrevious(ListIterator<T> iterator) {
- T previous = iterator.previous();
- T next = iterator.next();
- assert previous == next;
- return previous;
+ if (iterator.hasPrevious()) {
+ T previous = iterator.previous();
+ T next = iterator.next();
+ assert previous == next;
+ return previous;
+ }
+ return null;
}
+ /**
+ * Returns the next element or null if !hasNext(). A subsequent call to iterator.remove() will
+ * remove the peeked element.
+ */
public static <T> T peekNext(ListIterator<T> iterator) {
if (iterator.hasNext()) {
T next = iterator.next();