Add matcher helpers to 2.2 branch

Fixed: 187090274
Change-Id: I382a64d6c9270f3c97335c59407fc7577570c7cb
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
index 86a21c1..2f81351 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
@@ -6,7 +6,9 @@
 
 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;
@@ -71,10 +73,98 @@
     };
   }
 
+  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;
   }