Introduce AppInfoWithClassHierarchy

- rewrite lookupStaticTarget to use resolution.

Bug:147471606
Change-Id: I2bad5097e0429e8f4d94e6ba883bda15c18895e7
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index cf5e6c8..bd49aa1 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -155,7 +156,8 @@
       DexApplication app = new ApplicationReader(inputApp, options, timing).read(executor);
       PrefixRewritingMapper rewritePrefix =
           options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options.itemFactory);
-      AppInfo appInfo = new AppInfo(app);
+      AppInfo appInfo =
+          options.enableDesugaring ? new AppInfoWithClassHierarchy(app) : new AppInfo(app);
 
       final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
 
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index a692e1d..45cb778 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.dex.ApplicationReader;
 import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.GraphLense;
@@ -116,7 +117,7 @@
           options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options.itemFactory);
 
       app = new L8TreePruner(options).prune(app, rewritePrefix);
-      AppInfo appInfo = new AppInfo(app);
+      AppInfo appInfo = new AppInfoWithClassHierarchy(app);
 
       AppView<?> appView = AppView.createForL8(appInfo, options, rewritePrefix);
       IRConverter converter = new IRConverter(appView, timing);
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 3b4d52f..d775cb3 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -22,7 +22,7 @@
       DexEncodedMethod method,
       DexClass holder,
       DexProgramClass context,
-      AppInfoWithSubtyping appInfo) {
+      AppInfoWithClassHierarchy appInfo) {
     return isMemberAccessible(method.accessFlags, holder, context, appInfo);
   }
 
@@ -38,7 +38,7 @@
       AccessFlags<?> memberFlags,
       DexClass holder,
       DexProgramClass context,
-      AppInfoWithSubtyping appInfo) {
+      AppInfoWithClassHierarchy appInfo) {
     if (!isClassAccessible(holder, context)) {
       return false;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index ad19ed4..9bf667b 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -201,15 +201,20 @@
     definitions.remove(type);
   }
 
+  // TODO(b/147578480): RemoveDeprecation Use AppInfoWithClassHierarchy and
+  // lookupXX(DexMethod, DexProgramClass). The following 3 methods should either be removed or
+  // return null.
+
   /**
    * Lookup static method following the super chain from the holder of {@code method}.
-   * <p>
-   * This method will resolve the method on the holder of {@code method} and only return a non-null
-   * value if the result of resolution was a static, non-abstract method.
+   *
+   * <p>This method will resolve the method on the holder of {@code method} and only return a
+   * non-null value if the result of resolution was a static, non-abstract method.
    *
    * @param method the method to lookup
    * @return The actual target for {@code method} or {@code null} if none found.
    */
+  @Deprecated // TODO(b/147578480): Remove
   public DexEncodedMethod lookupStaticTarget(DexMethod method) {
     assert checkIfObsolete();
     ResolutionResult resolutionResult = resolveMethod(method.holder, method);
@@ -227,6 +232,7 @@
    * @param invocationContext the class the invoke is contained in, i.e., the holder of the caller.
    * @return The actual target for {@code method} or {@code null} if none found.
    */
+  @Deprecated // TODO(b/147578480): Remove
   public DexEncodedMethod lookupSuperTarget(DexMethod method, DexType invocationContext) {
     assert checkIfObsolete();
     assert invocationContext.isClassType();
@@ -234,6 +240,7 @@
     return context == null ? null : lookupSuperTarget(method, context);
   }
 
+  @Deprecated // TODO(b/147578480): Remove
   public DexEncodedMethod lookupSuperTarget(DexMethod method, DexClass invocationContext) {
     assert checkIfObsolete();
     return resolveMethod(method.holder, method).lookupInvokeSuperTarget(invocationContext, this);
@@ -596,6 +603,16 @@
     return null;
   }
 
+  public boolean hasClassHierarchy() {
+    assert checkIfObsolete();
+    return false;
+  }
+
+  public AppInfoWithClassHierarchy withClassHierarchy() {
+    assert checkIfObsolete();
+    return null;
+  }
+
   public boolean hasSubtyping() {
     assert checkIfObsolete();
     return false;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
new file mode 100644
index 0000000..6963898
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -0,0 +1,119 @@
+// 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 java.util.LinkedList;
+
+/* Specific subclass of AppInfo designed to support desugaring in D8. Desugaring requires a
+ * minimal amount of knowledge in the overall program, provided through classpath. Basic
+ * features are present, such as static and super look-ups, or isSubtype.
+ */
+public class AppInfoWithClassHierarchy extends AppInfo {
+
+  public AppInfoWithClassHierarchy(DexApplication application) {
+    super(application);
+  }
+
+  public AppInfoWithClassHierarchy(AppInfo previous) {
+    super(previous);
+  }
+
+  @Override
+  public boolean hasClassHierarchy() {
+    assert checkIfObsolete();
+    return true;
+  }
+
+  @Override
+  public AppInfoWithClassHierarchy withClassHierarchy() {
+    assert checkIfObsolete();
+    return this;
+  }
+
+  public boolean isSubtype(DexType subtype, DexType supertype) {
+    LinkedList<DexType> workList = new LinkedList<>();
+    workList.add(subtype);
+    while (!workList.isEmpty()) {
+      DexClass subtypeClass = definitionFor(subtype);
+      if (subtypeClass == null) {
+        // What to do here? Report missing type?
+        continue;
+      }
+      if (subtypeClass.superType == supertype) {
+        return true;
+      } else {
+        workList.add(subtypeClass.superType);
+      }
+      for (DexType itf : subtypeClass.interfaces.values) {
+        if (itf == supertype) {
+          return true;
+        } else {
+          workList.add(itf);
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Lookup static method following the super chain from the holder of {@code method}.
+   *
+   * <p>This method will resolve the method on the holder of {@code method} and only return a
+   * non-null value if the result of resolution was a static, non-abstract method.
+   *
+   * @param method the method to lookup
+   * @return The actual target for {@code method} or {@code null} if none found.
+   */
+
+  @Deprecated // TODO(b/147578480): Remove
+  public DexEncodedMethod lookupStaticTarget(DexMethod method, DexType invocationContext) {
+    assert checkIfObsolete();
+    assert invocationContext.isClassType();
+    DexClass context = definitionFor(invocationContext);
+    if (context == null) {
+      return null;
+    }
+    assert context.isProgramClass();
+    return lookupStaticTarget(method, context.asProgramClass());
+  }
+
+  public final DexEncodedMethod lookupStaticTarget(
+      DexMethod method, DexProgramClass invocationContext) {
+    assert checkIfObsolete();
+    return resolveMethod(method.holder, method).lookupInvokeStaticTarget(invocationContext, this);
+  }
+
+  /**
+   * Lookup super method following the super chain from the holder of {@code method}.
+   *
+   * <p>This method will resolve the method on the holder of {@code method} and only return a
+   * non-null value if the result of resolution was an instance (i.e. non-static) method.
+   *
+   * @param method the method to lookup
+   * @param invocationContext the class the invoke is contained in, i.e., the holder of the caller.
+   * @return The actual target for {@code method} or {@code null} if none found.
+   */
+  @Deprecated // TODO(b/147578480): Remove
+  public DexEncodedMethod lookupSuperTarget(DexMethod method, DexType invocationContext) {
+    assert checkIfObsolete();
+    assert invocationContext.isClassType();
+    DexClass context = definitionFor(invocationContext);
+    if (context == null) {
+      return null;
+    }
+    if (context.isProgramClass()){
+      return lookupSuperTarget(method, context.asProgramClass());
+    }
+    // TODO(clement): find out when this happens and remove it,
+    //  Should be assert context.isProgramClass().
+    return lookupSuperTarget(method, context);
+  }
+
+  public final DexEncodedMethod lookupSuperTarget(
+      DexMethod method, DexProgramClass invocationContext) {
+    assert checkIfObsolete();
+    return resolveMethod(method.holder, method).lookupInvokeSuperTarget(invocationContext, this);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index a24212f..71b2235 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -26,7 +26,7 @@
 import java.util.function.Consumer;
 import java.util.function.Function;
 
-public class AppInfoWithSubtyping extends AppInfo implements ClassHierarchy {
+public class AppInfoWithSubtyping extends AppInfoWithClassHierarchy {
 
   private static final int ROOT_LEVEL = 0;
   private static final int UNKNOWN_LEVEL = -1;
@@ -463,7 +463,6 @@
     return getTypeInfo(type).isInterface();
   }
 
-  @Override
   public boolean hasSubtypes(DexType type) {
     return !getTypeInfo(type).directSubtypes.isEmpty();
   }
@@ -651,13 +650,6 @@
     }
   }
 
-  @Override
-  public boolean isDirectSubtype(DexType subtype, DexType supertype) {
-    TypeInfo info = getTypeInfo(supertype);
-    assert info.hierarchyLevel != UNKNOWN_LEVEL;
-    return info.directSubtypes.contains(subtype);
-  }
-
   // TODO(b/130636783): inconsistent location
   public DexType computeLeastUpperBoundOfClasses(DexType subtype, DexType other) {
     if (subtype == other) {
diff --git a/src/main/java/com/android/tools/r8/graph/ClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/ClassHierarchy.java
deleted file mode 100644
index bcf56bb..0000000
--- a/src/main/java/com/android/tools/r8/graph/ClassHierarchy.java
+++ /dev/null
@@ -1,12 +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.graph;
-
-public interface ClassHierarchy extends DexDefinitionSupplier {
-  boolean hasSubtypes(DexType type);
-
-  boolean isDirectSubtype(DexType subtype, DexType supertype);
-
-  boolean isSubtype(DexType subtype, DexType supertype);
-}
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 4854741..2b70ca2 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -49,10 +49,11 @@
     return isSingleResolution() ? asSingleResolution().getResolvedMethod() : null;
   }
 
-  public abstract boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo);
+  public abstract boolean isAccessibleFrom(
+      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
 
   public abstract boolean isAccessibleForVirtualDispatchFrom(
-      DexProgramClass context, AppInfoWithSubtyping appInfo);
+      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
 
   // TODO(b/145187573): Remove this and use proper access checks.
   @Deprecated
@@ -60,15 +61,19 @@
 
   /** Lookup the single target of an invoke-special on this resolution result if possible. */
   public abstract DexEncodedMethod lookupInvokeSpecialTarget(
-      DexProgramClass context, AppInfoWithSubtyping appInfo);
+      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
 
   /** Lookup the single target of an invoke-super on this resolution result if possible. */
   public abstract DexEncodedMethod lookupInvokeSuperTarget(
-      DexProgramClass context, AppInfoWithSubtyping appInfo);
+      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
 
   @Deprecated
   public abstract DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo);
 
+  /** Lookup the single target of an invoke-static on this resolution result if possible. */
+  public abstract DexEncodedMethod lookupInvokeStaticTarget(
+      DexProgramClass context, AppInfoWithClassHierarchy appInfo);
+
   public final Set<DexEncodedMethod> lookupVirtualDispatchTargets(
       boolean isInterface, AppInfoWithSubtyping appInfo) {
     return isInterface ? lookupInterfaceTargets(appInfo) : lookupVirtualTargets(appInfo);
@@ -116,14 +121,14 @@
     }
 
     @Override
-    public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo) {
+    public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return AccessControl.isMethodAccessible(
           resolvedMethod, initialResolutionHolder, context, appInfo);
     }
 
     @Override
     public boolean isAccessibleForVirtualDispatchFrom(
-        DexProgramClass context, AppInfoWithSubtyping appInfo) {
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return resolvedMethod.isVirtualMethod() && isAccessibleFrom(context, appInfo);
     }
 
@@ -140,7 +145,7 @@
      */
     @Override
     public DexEncodedMethod lookupInvokeSpecialTarget(
-        DexProgramClass context, AppInfoWithSubtyping appInfo) {
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       // If the resolution is non-accessible then no target exists.
       if (!isAccessibleFrom(context, appInfo)) {
         return null;
@@ -176,7 +181,7 @@
      */
     @Override
     public DexEncodedMethod lookupInvokeSuperTarget(
-        DexProgramClass context, AppInfoWithSubtyping appInfo) {
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       if (!isAccessibleFrom(context, appInfo)) {
         return null;
       }
@@ -192,6 +197,28 @@
       return target;
     }
 
+    /**
+     * Lookup the target of an invoke-static.
+     *
+     * <p>This method will resolve the method on the holder and only return a non-null value if the
+     * result of resolution was a static, non-abstract method.
+     *
+     * @param context Class the invoke is contained in, i.e., the holder of the caller.
+     *      * @param appInfo Application info.
+     * @return The actual target or {@code null} if none found.
+     */
+    @Override
+    public DexEncodedMethod lookupInvokeStaticTarget(DexProgramClass context,
+        AppInfoWithClassHierarchy appInfo) {
+      if (!isAccessibleFrom(context, appInfo)) {
+        return null;
+      }
+      if (resolvedMethod.isStatic()) {
+        return resolvedMethod;
+      }
+      return null;
+    }
+
     @Override
     public DexEncodedMethod lookupInvokeSuperTarget(DexClass context, AppInfo appInfo) {
       assert context != null;
@@ -276,7 +303,8 @@
       return target;
     }
 
-    private static boolean isSuperclass(DexClass sup, DexClass sub, AppInfoWithSubtyping appInfo) {
+    private static boolean isSuperclass(
+        DexClass sup, DexClass sub, AppInfoWithClassHierarchy appInfo) {
       return sup != sub && appInfo.isSubtype(sub.type, sup.type);
     }
 
@@ -391,13 +419,13 @@
 
     @Override
     public final DexEncodedMethod lookupInvokeSpecialTarget(
-        DexProgramClass context, AppInfoWithSubtyping appInfo) {
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return null;
     }
 
     @Override
     public DexEncodedMethod lookupInvokeSuperTarget(
-        DexProgramClass context, AppInfoWithSubtyping appInfo) {
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return null;
     }
 
@@ -407,6 +435,12 @@
     }
 
     @Override
+    public DexEncodedMethod lookupInvokeStaticTarget(DexProgramClass context,
+        AppInfoWithClassHierarchy appInfo) {
+      return null;
+    }
+
+    @Override
     public final Set<DexEncodedMethod> lookupVirtualTargets(AppInfoWithSubtyping appInfo) {
       return null;
     }
@@ -427,13 +461,13 @@
     }
 
     @Override
-    public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo) {
+    public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return true;
     }
 
     @Override
     public boolean isAccessibleForVirtualDispatchFrom(
-        DexProgramClass context, AppInfoWithSubtyping appInfo) {
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return true;
     }
 
@@ -461,13 +495,13 @@
     }
 
     @Override
-    public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithSubtyping appInfo) {
+    public boolean isAccessibleFrom(DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return false;
     }
 
     @Override
     public boolean isAccessibleForVirtualDispatchFrom(
-        DexProgramClass context, AppInfoWithSubtyping appInfo) {
+        DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
       return false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 0c4671e..6893f5f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -105,7 +105,7 @@
     DexMethod invokedMethod = getInvokedMethod();
     if (appView.appInfo().hasLiveness()) {
       AppInfoWithLiveness appInfo = appView.appInfo().withLiveness();
-      return appInfo.lookupStaticTarget(invokedMethod);
+      return appInfo.lookupStaticTarget(invokedMethod, invocationContext);
     }
     // In D8, we can treat invoke-static instructions as having a single target if the invoke is
     // targeting a method in the enclosing class.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index b95f69e..fd4b0ca 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -183,7 +183,7 @@
   public ConstraintWithTarget forInvokeStatic(DexMethod method, DexType invocationContext) {
     DexMethod lookup = graphLense.lookupMethod(method);
     return forSingleTargetInvoke(
-        lookup, appView.appInfo().lookupStaticTarget(lookup), invocationContext);
+        lookup, appView.appInfo().lookupStaticTarget(lookup, invocationContext), invocationContext);
   }
 
   public ConstraintWithTarget forInvokeSuper(DexMethod method, DexType invocationContext) {
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 9b5029f..bba68bf 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -968,7 +968,7 @@
       case DIRECT:
         return lookupDirectTarget(target);
       case STATIC:
-        return lookupStaticTarget(target);
+        return lookupStaticTarget(target, invocationContext);
       case SUPER:
         return lookupSuperTarget(target, invocationContext);
       default:
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index b4dc614..c55b3a5 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -293,8 +293,7 @@
         "  invokespecial SubClass/<init>()V",
         "  invokespecial SubClass/aMethod()V",
         "  return");
-    ensureExceptionOrCompilerError(builder, IllegalAccessError.class,
-        compiler -> compiler.equals(CompilerUnderTest.R8));
+    ensureIAE(builder);
   }
 
   @Test