Introduce DexClassAndMethod result object

Change-Id: Ie8c7c52a6d5efbcf341f12af330359cccdd99aa4
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
new file mode 100644
index 0000000..9d07cc6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMethod.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2020, 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.graph;
+
+import com.android.tools.r8.errors.Unreachable;
+
+public class DexClassAndMethod<C extends DexClass> {
+
+  private static final DexClassAndMethod<?> NO_RESULT = new DexClassAndMethod<>(null, null);
+
+  public final C holder;
+  public final DexEncodedMethod method;
+
+  protected DexClassAndMethod(C holder, DexEncodedMethod method) {
+    assert (holder == null && method == null)
+        || (holder != null && method != null && holder.type == method.method.holder);
+    this.holder = holder;
+    this.method = method;
+  }
+
+  public static <C extends DexClass> DexClassAndMethod<C> createResult(
+      C c, DexEncodedMethod method) {
+    assert c != null;
+    assert method != null;
+    assert c.type == method.method.holder;
+    return new DexClassAndMethod<C>(c, method);
+  }
+
+  public static DexClassAndMethod<?> createNoResult() {
+    return NO_RESULT;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    throw new Unreachable("Unsupported attempt at comparing Class and DexClassAndMethod");
+  }
+
+  @Override
+  public int hashCode() {
+    throw new Unreachable("Unsupported attempt at computing the hashcode of DexClassAndMethod");
+  }
+
+  public boolean hasResult() {
+    return this != NO_RESULT;
+  }
+
+  public boolean hasNoResult() {
+    return this == NO_RESULT;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 1e33e66..8a68ee5 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -3,26 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.graph;
 
-import com.android.tools.r8.errors.Unreachable;
-
 /** Type representing a method definition in the programs compilation unit and its holder. */
-public class ProgramMethod {
-  public final DexProgramClass holder;
-  public final DexEncodedMethod method;
+public final class ProgramMethod extends DexClassAndMethod<DexProgramClass> {
 
-  public ProgramMethod(DexProgramClass holder,  DexEncodedMethod method) {
-    assert holder.type == method.method.holder;
-    this.holder = holder;
-    this.method = method;
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    throw new Unreachable("Unsupported attempt at comparing ProgramMethod");
-  }
-
-  @Override
-  public int hashCode() {
-    throw new Unreachable("Unsupported attempt at computing the hashcode of ProgramMethod");
+  public ProgramMethod(DexProgramClass holder, DexEncodedMethod method) {
+    super(holder, method);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index 0941f92..1f5c91b 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -86,7 +86,7 @@
     return lookupVirtualDispatchTargets(context, appView, appView.appInfo());
   }
 
-  public abstract DexEncodedMethod lookupVirtualDispatchTarget(
+  public abstract DexClassAndMethod<?> lookupVirtualDispatchTarget(
       DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView);
 
   /** Result for a resolution that succeeds with a known declaration/definition. */
@@ -356,11 +356,11 @@
       instantiatedInfo.forEachInstantiatedSubType(
           resolvedHolder.type,
           subClass -> {
-            DexEncodedMethod lookupTarget = lookupVirtualDispatchTarget(subClass, appView);
-            if (lookupTarget == null) {
-              return;
+            DexClassAndMethod<?> dexClassAndMethod = lookupVirtualDispatchTarget(subClass, appView);
+            if (dexClassAndMethod.hasResult()) {
+              addVirtualDispatchTarget(
+                  dexClassAndMethod.method, resolvedHolder.isInterface(), result);
             }
-            addVirtualDispatchTarget(lookupTarget, resolvedHolder.isInterface(), result);
           },
           dexCallSite -> {
             // TODO(b/148769279): We need to look at the call site to see if it overrides
@@ -416,7 +416,7 @@
      * we have an object ref on the stack.
      */
     @Override
-    public DexEncodedMethod lookupVirtualDispatchTarget(
+    public DexClassAndMethod<?> lookupVirtualDispatchTarget(
         DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
       // TODO(b/148591377): Enable this assertion.
       // The dynamic type cannot be an interface.
@@ -434,16 +434,21 @@
         if (candidate == null || candidate == DexEncodedMethod.SENTINEL) {
           // We cannot find a target above the resolved method.
           if (current.type == overrideTarget.method.holder) {
-            return null;
+            return DexClassAndMethod.createNoResult();
           }
           current = current.superType == null ? null : appView.definitionFor(current.superType);
           continue;
         }
-        return candidate;
+        return DexClassAndMethod.createResult(current, candidate);
       }
       // TODO(b/149557233): Enable assertion.
       // assert resolvedHolder.isInterface();
-      return lookupMaximallySpecificDispatchTarget(dynamicInstance, appView);
+      DexEncodedMethod maximalSpecific =
+          lookupMaximallySpecificDispatchTarget(dynamicInstance, appView);
+      return maximalSpecific == null
+          ? DexClassAndMethod.createNoResult()
+          : DexClassAndMethod.createResult(
+              appView.definitionFor(maximalSpecific.method.holder), maximalSpecific);
     }
 
     private DexEncodedMethod lookupMaximallySpecificDispatchTarget(
@@ -550,9 +555,9 @@
     }
 
     @Override
-    public DexEncodedMethod lookupVirtualDispatchTarget(
+    public DexClassAndMethod<?> lookupVirtualDispatchTarget(
         DexProgramClass dynamicInstance, AppView<? extends AppInfoWithClassHierarchy> appView) {
-      return null;
+      return DexClassAndMethod.createNoResult();
     }
   }