Report resolution target for library modelled methods

Change-Id: I9e6038f6fe91230e6b974ef078c2327761475538
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 9484c21..9ed88a6 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -19,6 +19,7 @@
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
 import com.android.tools.r8.ir.optimize.library.LibraryMethodOptimizer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.LibraryModeledPredicate;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OptionalBool;
@@ -32,7 +33,7 @@
 import java.util.function.Function;
 import java.util.function.Predicate;
 
-public class AppView<T extends AppInfo> implements DexDefinitionSupplier {
+public class AppView<T extends AppInfo> implements DexDefinitionSupplier, LibraryModeledPredicate {
 
   private enum WholeProgramOptimizations {
     ON,
@@ -108,6 +109,11 @@
     }
   }
 
+  @Override
+  public boolean isModeled(DexType type) {
+    return libraryMethodOptimizer.isModeled(type);
+  }
+
   public static <T extends AppInfo> AppView<T> createForD8(T appInfo, InternalOptions options) {
     return new AppView<>(appInfo, WholeProgramOptimizations.OFF, options);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 6c9e84b..58b768d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -965,8 +965,6 @@
         createMethod(npeType, createProto(voidType), constructorMethodName);
     public final DexMethod initWithMessage =
         createMethod(npeType, createProto(voidType, stringType), constructorMethodName);
-
-    private NullPointerExceptionMethods() {}
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 8b139ca..e9f3063 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -95,6 +95,7 @@
               getInvokedMethod(),
               invocationContext,
               true,
+              appView,
               TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
               getReceiver().getDynamicLowerBoundType(appViewWithLiveness));
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 0499be1..1ec8873 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -99,6 +99,7 @@
               getInvokedMethod(),
               invocationContext,
               false,
+              appView,
               TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this),
               getReceiver().getDynamicLowerBoundType(appViewWithLiveness));
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index ca95080..25ddb6a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -174,7 +174,7 @@
         }
       } else {
         DexEncodedMethod singleTarget =
-            appView.appInfo().lookupSingleTarget(type, method, context.holder);
+            appView.appInfo().lookupSingleTarget(type, method, context.holder, appView);
         if (singleTarget != null) {
           assert !source.accessFlags.isBridge() || singleTarget != currentMethod.method;
           DexProgramClass clazz =
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 87e317e..44367e0 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1087,7 +1087,10 @@
   }
 
   public DexEncodedMethod lookupSingleTarget(
-      Type type, DexMethod target, DexType invocationContext) {
+      Type type,
+      DexMethod target,
+      DexType invocationContext,
+      LibraryModeledPredicate modeledPredicate) {
     assert checkIfObsolete();
     DexType holder = target.holder;
     if (!holder.isClassType()) {
@@ -1095,9 +1098,9 @@
     }
     switch (type) {
       case VIRTUAL:
-        return lookupSingleVirtualTarget(target, invocationContext, false);
+        return lookupSingleVirtualTarget(target, invocationContext, false, modeledPredicate);
       case INTERFACE:
-        return lookupSingleVirtualTarget(target, invocationContext, true);
+        return lookupSingleVirtualTarget(target, invocationContext, true, modeledPredicate);
       case DIRECT:
         return lookupDirectTarget(target, invocationContext);
       case STATIC:
@@ -1138,13 +1141,26 @@
   public DexEncodedMethod lookupSingleVirtualTarget(
       DexMethod method, DexType invocationContext, boolean isInterface) {
     assert checkIfObsolete();
-    return lookupSingleVirtualTarget(method, invocationContext, isInterface, method.holder, null);
+    return lookupSingleVirtualTarget(
+        method, invocationContext, isInterface, type -> false, method.holder, null);
+  }
+
+  /** For mapping invoke virtual instruction to single target method. */
+  public DexEncodedMethod lookupSingleVirtualTarget(
+      DexMethod method,
+      DexType invocationContext,
+      boolean isInterface,
+      LibraryModeledPredicate modeledPredicate) {
+    assert checkIfObsolete();
+    return lookupSingleVirtualTarget(
+        method, invocationContext, isInterface, modeledPredicate, method.holder, null);
   }
 
   public DexEncodedMethod lookupSingleVirtualTarget(
       DexMethod method,
       DexType invocationContext,
       boolean isInterface,
+      LibraryModeledPredicate modeledPredicate,
       DexType refinedReceiverType,
       ClassTypeLatticeElement receiverLowerBoundType) {
     assert checkIfObsolete();
@@ -1169,6 +1185,14 @@
         || !resolution.isAccessibleForVirtualDispatchFrom(invocationClass, this)) {
       return null;
     }
+    // If the method is modeled, return the resolution.
+    if (modeledPredicate.isModeled(resolution.getResolvedHolder().type)) {
+      if (resolution.getResolvedHolder().isFinal()
+          || (resolution.getResolvedMethod().isFinal()
+              && resolution.getResolvedMethod().accessFlags.isPublic())) {
+        return resolution.getResolvedMethod();
+      }
+    }
     // If the lower-bound on the receiver type is the same as the upper-bound, then we have exact
     // runtime type information. In this case, the invoke will dispatch to the resolution result
     // from the runtime type of the receiver.
diff --git a/src/main/java/com/android/tools/r8/shaking/LibraryModeledPredicate.java b/src/main/java/com/android/tools/r8/shaking/LibraryModeledPredicate.java
new file mode 100644
index 0000000..39ee67c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/LibraryModeledPredicate.java
@@ -0,0 +1,13 @@
+// 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.shaking;
+
+import com.android.tools.r8.graph.DexType;
+
+@FunctionalInterface
+public interface LibraryModeledPredicate {
+
+  boolean isModeled(DexType type);
+}