blob: 2f81351e16565d4170851168add0d535b0089c74 [file] [log] [blame]
// 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.codeinspector;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
public class CodeMatchers {
public static Matcher<MethodSubject> accessesField(FieldSubject targetSubject) {
if (!targetSubject.isPresent()) {
throw new IllegalArgumentException();
}
DexField target = targetSubject.getField().field;
return new TypeSafeMatcher<MethodSubject>() {
@Override
protected boolean matchesSafely(MethodSubject subject) {
if (!subject.isPresent()) {
return false;
}
if (!subject.getMethod().hasCode()) {
return false;
}
return subject.streamInstructions().anyMatch(isFieldAccessWithTarget(target));
}
@Override
public void describeTo(Description description) {
description.appendText("accesses field `" + target.toSourceString() + "`");
}
@Override
public void describeMismatchSafely(final MethodSubject subject, Description description) {
description.appendText("method did not");
}
};
}
public static Matcher<MethodSubject> invokesMethod(MethodSubject targetSubject) {
if (!targetSubject.isPresent()) {
throw new IllegalArgumentException();
}
DexMethod target = targetSubject.getMethod().method;
return new TypeSafeMatcher<MethodSubject>() {
@Override
protected boolean matchesSafely(MethodSubject subject) {
if (!subject.isPresent()) {
return false;
}
if (!subject.getMethod().hasCode()) {
return false;
}
return subject.streamInstructions().anyMatch(isInvokeWithTarget(target));
}
@Override
public void describeTo(Description description) {
description.appendText("invokes method `" + target.toSourceString() + "`");
}
@Override
public void describeMismatchSafely(final MethodSubject subject, Description description) {
description.appendText("method did not");
}
};
}
public static Matcher<MethodSubject> invokesMethod(
String returnType, String holderType, String methodName, List<String> parameterTypes) {
return new TypeSafeMatcher<MethodSubject>() {
@Override
protected boolean matchesSafely(MethodSubject subject) {
if (!subject.isPresent()) {
return false;
}
if (!subject.getMethod().hasCode()) {
return false;
}
return subject
.streamInstructions()
.anyMatch(isInvokeWithTarget(returnType, holderType, methodName, parameterTypes));
}
@Override
public void describeTo(Description description) {
StringBuilder text =
new StringBuilder("invokes method `")
.append(returnType != null ? returnType : "*")
.append(" ")
.append(holderType != null ? holderType : "*")
.append(".")
.append(methodName != null ? methodName : "*")
.append("(");
if (parameterTypes != null) {
text.append(
parameterTypes.stream()
.map(parameterType -> parameterType != null ? parameterType : "*")
.collect(Collectors.joining(", ")));
} else {
text.append("...");
}
text.append(")`");
description.appendText(text.toString());
}
@Override
public void describeMismatchSafely(final MethodSubject subject, Description description) {
description.appendText("method did not");
}
};
}
public static Matcher<MethodSubject> invokesMethodWithHolderAndName(
String holderType, String name) {
return invokesMethod(null, holderType, name, null);
}
public static Matcher<MethodSubject> invokesMethodWithName(String name) {
return invokesMethod(null, null, name, null);
}
public static Predicate<InstructionSubject> isInvokeWithTarget(DexMethod target) {
return instruction -> instruction.isInvoke() && instruction.getMethod() == target;
}
public static Predicate<InstructionSubject> isInvokeWithTarget(
String returnType, String holderType, String methodName, List<String> parameterTypes) {
return instruction -> {
if (!instruction.isInvoke()) {
return false;
}
DexMethod invokedMethod = instruction.getMethod();
if (returnType != null
&& !invokedMethod.getReturnType().toSourceString().equals(returnType)) {
return false;
}
if (holderType != null
&& !invokedMethod.getHolderType().toSourceString().equals(holderType)) {
return false;
}
if (methodName != null && !invokedMethod.getName().toSourceString().equals(methodName)) {
return false;
}
if (parameterTypes != null) {
if (parameterTypes.size() != invokedMethod.getArity()) {
return false;
}
for (int i = 0; i < parameterTypes.size(); i++) {
String parameterType = parameterTypes.get(i);
if (parameterType != null
&& !invokedMethod.getParameters().get(i).toSourceString().equals(parameterType)) {
return false;
}
}
}
return true;
};
}
public static Predicate<InstructionSubject> isFieldAccessWithTarget(DexField target) {
return instruction -> instruction.isFieldAccess() && instruction.getField() == target;
}
}