Revert "Extend the range of assume instructions, part 3."
This reverts commit 2980a52d09f0d8806a77dd3d043c2b279c10b218.
Reason for revert: b/141517313
Change-Id: I858b167eb1297c5dba01371a7c587959c9f5bcb4
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 18ba962..c4dfbc4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -153,10 +153,6 @@
return self;
}
- public boolean mayAffectStaticType() {
- return isAssumeNonNull();
- }
-
@Override
public boolean couldIntroduceAnAlias(AppView<?> appView, Value root) {
assert root != null && root.getTypeLattice().isReference();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 6648777..2881de7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
@@ -425,22 +424,6 @@
return users.getFirst();
}
- public Set<Instruction> aliasedUsers() {
- Set<Instruction> users = SetUtils.newIdentityHashSet(uniqueUsers());
- collectAliasedUsersViaAssume(uniqueUsers(), users);
- return users;
- }
-
- private static void collectAliasedUsersViaAssume(
- Set<Instruction> usersToTest, Set<Instruction> collectedUsers) {
- for (Instruction user : usersToTest) {
- if (user.isAssume()) {
- collectedUsers.addAll(user.outValue().uniqueUsers());
- collectAliasedUsersViaAssume(user.outValue().uniqueUsers(), collectedUsers);
- }
- }
- }
-
public Phi firstPhiUser() {
assert !phiUsers.isEmpty();
return phiUsers.getFirst();
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 1ae7240..e370192 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
@@ -1249,6 +1249,26 @@
assert code.verifyTypes(appView);
+ if (nonNullTracker != null) {
+ // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
+ // this may not be the right place to collect call site optimization info.
+ // Collecting call-site optimization info depends on the existence of non-null IRs.
+ // Arguments can be changed during the debug mode.
+ if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
+ appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
+ }
+ // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
+ nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
+ }
+ if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
+ codeRewriter.removeAssumeInstructions(code);
+ assert code.isConsistentSSA();
+ }
+ // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
+ assert code.verifyNoNullabilityBottomTypes();
+
+ assert code.verifyTypes(appView);
+
previous = printMethod(code, "IR before class inlining (SSA)", previous);
if (classInliner != null) {
@@ -1310,26 +1330,6 @@
assert code.isConsistentSSA();
}
- if (nonNullTracker != null) {
- // TODO(b/139246447): Once we extend this optimization to, e.g., constants of primitive args,
- // this may not be the right place to collect call site optimization info.
- // Collecting call-site optimization info depends on the existence of non-null IRs.
- // Arguments can be changed during the debug mode.
- if (!isDebugMode && appView.callSiteOptimizationInfoPropagator() != null) {
- appView.callSiteOptimizationInfoPropagator().collectCallSiteOptimizationInfo(code);
- }
- // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
- nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
- }
- if (aliasIntroducer != null || nonNullTracker != null || dynamicTypeOptimization != null) {
- codeRewriter.removeAssumeInstructions(code);
- assert code.isConsistentSSA();
- }
- // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
- assert code.verifyNoNullabilityBottomTypes();
-
- assert code.verifyTypes(appView);
-
previous = printMethod(code, "IR after lambda merger (SSA)", previous);
if (options.outline.enabled) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 33855c9..b656678 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -34,14 +34,8 @@
NON_CLASS_TYPE,
UNKNOWN_TYPE,
- // Used by isClassEligible
- NON_PROGRAM_CLASS,
- ABSTRACT_OR_INTERFACE,
- NEVER_CLASS_INLINE,
- HAS_FINALIZER,
- TRIGGER_CLINIT,
-
// Used by InlineCandidateProcessor#isClassAndUsageEligible
+ INELIGIBLE_CLASS,
HAS_CLINIT,
HAS_INSTANCE_FIELDS,
NON_FINAL_TYPE,
@@ -52,8 +46,7 @@
}
private final LambdaRewriter lambdaRewriter;
- private final ConcurrentHashMap<DexClass, EligibilityStatus> knownClasses =
- new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<DexClass, Boolean> knownClasses = new ConcurrentHashMap<>();
public ClassInliner(LambdaRewriter lambdaRewriter) {
this.lambdaRewriter = lambdaRewriter;
@@ -63,15 +56,7 @@
DexEncodedMethod context, Instruction root, EligibilityStatus status) {
if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
Log.info(getClass(), "At %s,", context.toSourceString());
- Log.info(getClass(), "ClassInlining eligibility of `%s`: %s.", root, status);
- }
- }
-
- private void logIneligibleUser(
- DexEncodedMethod context, Instruction root, InstructionOrPhi ineligibleUser) {
- if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
- Log.info(getClass(), "At %s,", context.toSourceString());
- Log.info(getClass(), "Ineligible user of `%s`: `%s`.", root, ineligibleUser);
+ Log.info(getClass(), "ClassInlining eligibility of %s: %s,", root, status);
}
}
@@ -148,7 +133,7 @@
// return 1;
// }
// static int method3() {
- // return 123;
+ // return "F::getX";
// }
// }
//
@@ -210,7 +195,6 @@
InstructionOrPhi ineligibleUser = processor.areInstanceUsersEligible(defaultOracle);
if (ineligibleUser != null) {
// This root may succeed if users change in future.
- logIneligibleUser(code.method, root, ineligibleUser);
continue;
}
@@ -249,11 +233,11 @@
}
}
- private EligibilityStatus isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
- EligibilityStatus eligible = knownClasses.get(clazz);
+ private boolean isClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+ Boolean eligible = knownClasses.get(clazz);
if (eligible == null) {
- EligibilityStatus computed = computeClassEligible(appView, clazz);
- EligibilityStatus existing = knownClasses.putIfAbsent(clazz, computed);
+ boolean computed = computeClassEligible(appView, clazz);
+ Boolean existing = knownClasses.putIfAbsent(clazz, computed);
assert existing == null || existing == computed;
eligible = existing == null ? computed : existing;
}
@@ -264,19 +248,13 @@
// - is not an abstract class or interface
// - does not declare finalizer
// - does not trigger any static initializers except for its own
- private EligibilityStatus computeClassEligible(
- AppView<AppInfoWithLiveness> appView, DexClass clazz) {
- if (clazz == null) {
- return EligibilityStatus.UNKNOWN_TYPE;
- }
- if (clazz.isNotProgramClass()) {
- return EligibilityStatus.NON_PROGRAM_CLASS;
- }
- if (clazz.isAbstract() || clazz.isInterface()) {
- return EligibilityStatus.ABSTRACT_OR_INTERFACE;
- }
- if (appView.appInfo().neverClassInline.contains(clazz.type)) {
- return EligibilityStatus.NEVER_CLASS_INLINE;
+ private boolean computeClassEligible(AppView<AppInfoWithLiveness> appView, DexClass clazz) {
+ if (clazz == null
+ || clazz.isNotProgramClass()
+ || clazz.accessFlags.isAbstract()
+ || clazz.accessFlags.isInterface()
+ || appView.appInfo().neverClassInline.contains(clazz.type)) {
+ return false;
}
// Class must not define finalizer.
@@ -284,14 +262,11 @@
for (DexEncodedMethod method : clazz.virtualMethods()) {
if (method.method.name == dexItemFactory.finalizeMethodName
&& method.method.proto == dexItemFactory.objectMethods.finalize.proto) {
- return EligibilityStatus.HAS_FINALIZER;
+ return false;
}
}
// Check for static initializers in this class or any of interfaces it implements.
- if (clazz.initializationOfParentTypesMayHaveSideEffects(appView)) {
- return EligibilityStatus.TRIGGER_CLINIT;
- }
- return EligibilityStatus.ELIGIBLE;
+ return !clazz.initializationOfParentTypesMayHaveSideEffects(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
index d06b3f9..a16e9a3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/FieldValueHelper.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.classinliner;
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import com.android.tools.r8.graph.AppView;
@@ -15,7 +14,6 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Phi.RegisterReadType;
import com.android.tools.r8.ir.code.Value;
@@ -41,9 +39,6 @@
this.code = code;
this.root = root;
this.appView = appView;
- // Verify that `root` is not aliased.
- assert root.hasOutValue();
- assert root.outValue() == root.outValue().getAliasedValue();
}
void replaceValue(Value oldValue, Value newValue) {
@@ -127,10 +122,10 @@
Instruction instruction = iterator.previous();
assert instruction != null;
- if (instruction == root
- || (instruction.isInstancePut()
- && instruction.asInstancePut().getField() == field
- && instruction.asInstancePut().object().getAliasedValue() == root.outValue())) {
+ if (instruction == root ||
+ (instruction.isInstancePut() &&
+ instruction.asInstancePut().getField() == field &&
+ instruction.asInstancePut().object() == root.outValue())) {
valueProducingInsn = instruction;
break;
}
@@ -145,17 +140,12 @@
assert root == valueProducingInsn;
if (defaultValue == null) {
- InstructionListIterator it = block.listIterator(code, root);
// If we met newInstance it means that default value is supposed to be used.
- if (field.type.isPrimitiveType()) {
- defaultValue = code.createValue(
- TypeLatticeElement.fromDexType(field.type, definitelyNotNull(), appView));
- ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
- defaultValueInsn.setPosition(root.getPosition());
- it.add(defaultValueInsn);
- } else {
- defaultValue = it.insertConstNullInstruction(code, appView.options());
- }
+ defaultValue =
+ code.createValue(TypeLatticeElement.fromDexType(field.type, maybeNull(), appView));
+ ConstNumber defaultValueInsn = new ConstNumber(defaultValue, 0);
+ defaultValueInsn.setPosition(root.getPosition());
+ block.listIterator(code, root).add(defaultValueInsn);
}
return defaultValue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index de444e5..a2e9358 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
-import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.IRCode;
@@ -41,18 +40,16 @@
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -63,7 +60,7 @@
private final AppView<AppInfoWithLiveness> appView;
private final LambdaRewriter lambdaRewriter;
private final Inliner inliner;
- private final Function<DexClass, EligibilityStatus> isClassEligible;
+ private final Predicate<DexClass> isClassEligible;
private final Predicate<DexEncodedMethod> isProcessedConcurrently;
private final DexEncodedMethod method;
private final Instruction root;
@@ -86,7 +83,7 @@
AppView<AppInfoWithLiveness> appView,
LambdaRewriter lambdaRewriter,
Inliner inliner,
- Function<DexClass, EligibilityStatus> isClassEligible,
+ Predicate<DexClass> isClassEligible,
Predicate<DexEncodedMethod> isProcessedConcurrently,
DexEncodedMethod method,
Instruction root) {
@@ -143,9 +140,8 @@
// * class has class initializer marked as TrivialClassInitializer, and
// class initializer initializes the field we are reading here.
EligibilityStatus isClassAndUsageEligible() {
- EligibilityStatus status = isClassEligible.apply(eligibleClassDefinition);
- if (status != EligibilityStatus.ELIGIBLE) {
- return status;
+ if (!isClassEligible.test(eligibleClassDefinition)) {
+ return EligibilityStatus.INELIGIBLE_CLASS;
}
if (root.isNewInstance()) {
@@ -255,7 +251,7 @@
*
* @return null if all users are eligible, or the first ineligible user.
*/
- InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
+ protected InstructionOrPhi areInstanceUsersEligible(Supplier<InliningOracle> defaultOracle) {
// No Phi users.
if (eligibleInstance.numberOfPhiUsers() > 0) {
return eligibleInstance.firstPhiUser(); // Not eligible.
@@ -263,19 +259,11 @@
Set<Instruction> currentUsers = eligibleInstance.uniqueUsers();
while (!currentUsers.isEmpty()) {
- Set<Instruction> indirectUsers = Sets.newIdentityHashSet();
+ Set<Instruction> indirectUsers = new HashSet<>();
for (Instruction user : currentUsers) {
- if (user.isAssume()) {
- if (user.outValue().numberOfPhiUsers() > 0) {
- return user.outValue().firstPhiUser(); // Not eligible.
- }
- indirectUsers.addAll(user.outValue().uniqueUsers());
- continue;
- }
// Field read/write.
if (user.isInstanceGet()
- || (user.isInstancePut()
- && user.asInstancePut().value().getAliasedValue() != eligibleInstance)) {
+ || (user.isInstancePut() && user.asInstancePut().value() != eligibleInstance)) {
DexField field = user.asFieldInstruction().getField();
if (field.holder == eligibleClass
&& eligibleClassDefinition.lookupInstanceField(field) != null) {
@@ -300,7 +288,7 @@
boolean isCorrespondingConstructorCall =
root.isNewInstance()
&& !invoke.inValues().isEmpty()
- && root.outValue() == invoke.getReceiver();
+ && root.outValue() == invoke.inValues().get(0);
if (isCorrespondingConstructorCall) {
InliningInfo inliningInfo =
isEligibleConstructorCall(user.asInvokeDirect(), singleTarget, defaultOracle);
@@ -355,7 +343,6 @@
// Process inlining, includes the following steps:
//
- // * remove linked assume instructions if any so that users of the eligible field are up-to-date.
// * replace unused instance usages as arguments which are never used
// * inline extra methods if any, collect new direct method calls
// * inline direct methods if any
@@ -365,9 +352,6 @@
//
// Returns `true` if at least one method was inlined.
boolean processInlining(IRCode code, Supplier<InliningOracle> defaultOracle) {
- // Verify that `eligibleInstance` is not aliased.
- assert eligibleInstance == eligibleInstance.getAliasedValue();
-
replaceUsagesAsUnusedArgument(code);
boolean anyInlinedMethods = forceInlineExtraMethodInvocations(code);
@@ -390,14 +374,11 @@
// methods that need to be inlined anyway.
return true;
}
- assert extraMethodCalls.isEmpty()
- : "Remaining extra method calls: " + StringUtils.join(extraMethodCalls.entrySet(), ", ");
- assert unusedArguments.isEmpty()
- : "Remaining unused arg: " + StringUtils.join(unusedArguments, ", ");
+ assert extraMethodCalls.isEmpty();
+ assert unusedArguments.isEmpty();
}
anyInlinedMethods |= forceInlineDirectMethodInvocations(code);
- removeAssumeInstructionsLinkedToEligibleInstance();
removeMiscUsages(code);
removeFieldReads(code);
removeFieldWrites();
@@ -438,22 +419,6 @@
return true;
}
- private void removeAssumeInstructionsLinkedToEligibleInstance() {
- for (Instruction user : eligibleInstance.aliasedUsers()) {
- if (!user.isAssume()) {
- continue;
- }
- Assume<?> assumeInstruction = user.asAssume();
- Value src = assumeInstruction.src();
- Value dest = assumeInstruction.outValue();
- assert dest.numberOfPhiUsers() == 0;
- dest.replaceUsers(src);
- removeInstruction(user);
- }
- // Verify that no more assume instructions are left as users.
- assert eligibleInstance.aliasedUsers().stream().noneMatch(Instruction::isAssume);
- }
-
// Remove miscellaneous users before handling field reads.
private void removeMiscUsages(IRCode code) {
boolean needToRemoveUnreachableBlocks = false;
@@ -530,8 +495,8 @@
}
}
- private void replaceFieldRead(
- IRCode code, InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
+ private void replaceFieldRead(IRCode code,
+ InstanceGet fieldRead, Map<DexField, FieldValueHelper> fieldHelpers) {
Value value = fieldRead.outValue();
if (value != null) {
FieldValueHelper helper =
@@ -574,8 +539,7 @@
assert isEligibleSingleTarget(singleTarget);
// Must be a constructor called on the receiver.
- if (ListUtils.lastIndexMatching(
- invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) != 0) {
+ if (invoke.inValues().lastIndexOf(eligibleInstance) != 0) {
return null;
}
@@ -630,7 +594,7 @@
: null;
}
- // An invoke is eligible for inlining in the following cases:
+ // An invoke is eligible for inlinining in the following cases:
//
// - if it does not return the receiver
// - if there are no uses of the out value
@@ -681,8 +645,7 @@
DexEncodedMethod singleTarget,
Set<Instruction> indirectUsers) {
assert isEligibleSingleTarget(singleTarget);
- if (ListUtils.lastIndexMatching(
- invoke.inValues(), v -> v.getAliasedValue() == eligibleInstance) > 0) {
+ if (invoke.inValues().lastIndexOf(eligibleInstance) > 0) {
return null; // Instance passed as an argument.
}
return isEligibleVirtualMethodCall(
@@ -751,8 +714,7 @@
return false;
}
if (invoke.isInvokeMethodWithReceiver()
- && invoke.asInvokeMethodWithReceiver().getReceiver().getAliasedValue()
- == eligibleInstance) {
+ && invoke.asInvokeMethodWithReceiver().getReceiver() == eligibleInstance) {
return false;
}
if (invoke.isInvokeSuper()) {
@@ -793,7 +755,7 @@
// If we got here with invocation on receiver the user is ineligible.
if (invoke.isInvokeMethodWithReceiver()) {
- if (arguments.get(0).getAliasedValue() == eligibleInstance) {
+ if (arguments.get(0) == eligibleInstance) {
return false;
}
@@ -813,7 +775,7 @@
}
for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
- Value argument = arguments.get(argIndex).getAliasedValue();
+ Value argument = arguments.get(argIndex);
if (argument == eligibleInstance && optimizationInfo.getParameterUsages(argIndex).notUsed()) {
// Reference can be removed since it's not used.
unusedArguments.add(new Pair<>(invoke, argIndex));
@@ -834,7 +796,7 @@
Supplier<InliningOracle> defaultOracle) {
// Go through all arguments, see if all usages of eligibleInstance are good.
for (int argIndex = 0; argIndex < arguments.size(); argIndex++) {
- Value argument = arguments.get(argIndex).getAliasedValue();
+ Value argument = arguments.get(argIndex);
if (argument != eligibleInstance) {
continue; // Nothing to worry about.
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 370d5e2..a9c3f98 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -8,19 +8,9 @@
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
-import java.util.function.Predicate;
public class ListUtils {
- public static <T> int lastIndexMatching(List<T> list, Predicate<T> tester) {
- for (int i = list.size() - 1; i >= 0; i--) {
- if (tester.test(list.get(i))) {
- return i;
- }
- }
- return -1;
- }
-
public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
List<T> result = new ArrayList<>(list.size());
for (S element : list) {
diff --git a/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java b/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
deleted file mode 100644
index 3a63555..0000000
--- a/src/test/java/com/android/tools/r8/utils/ListUtilsTest.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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 static org.junit.Assert.assertEquals;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Predicate;
-import org.junit.Test;
-
-public class ListUtilsTest {
-
- private List<Integer> createInputData(int size) {
- List<Integer> input = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- input.add(i);
- }
- return input;
- }
-
- @Test
- public void lastIndexOf_outOfRange() {
- List<Integer> input = createInputData(3);
- Predicate<Integer> tester = x -> x * x == -1;
- assertEquals(-1, ListUtils.lastIndexMatching(input, tester));
- }
-
- @Test
- public void lastIndexOf_first() {
- List<Integer> input = createInputData(3);
- Predicate<Integer> tester = x -> x * x == 0;
- assertEquals(0, ListUtils.lastIndexMatching(input, tester));
- }
-
- @Test
- public void lastIndexOf_middle() {
- List<Integer> input = createInputData(4);
- Predicate<Integer> tester = x -> x * x == 4;
- assertEquals(2, ListUtils.lastIndexMatching(input, tester));
- }
-
- @Test
- public void lastIndexOf_last() {
- List<Integer> input = createInputData(2);
- Predicate<Integer> tester = x -> x * x == 1;
- assertEquals(1, ListUtils.lastIndexMatching(input, tester));
- }
-}