Include holder of startup method rules in startup class checks

Bug: b/274591979
Change-Id: I6a68b889640dfb09681551e2863b2dc527663438
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 270fcf2..d163330 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -1335,7 +1335,7 @@
 
       private static Predicate<DexProgramClass> getStartupClassPredicate(
           StartupProfile startupProfile) {
-        return clazz -> startupProfile.containsClassRule(clazz.getType());
+        return clazz -> startupProfile.isStartupClass(clazz.getType());
       }
 
       public List<DexProgramClass> getStartupClasses() {
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
index 7f2a248..a91c273 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupProfile.java
@@ -59,6 +59,11 @@
   }
 
   @Override
+  public boolean isStartupClass(DexType type) {
+    return false;
+  }
+
+  @Override
   public EmptyStartupProfile rewrittenWithLens(GraphLens graphLens) {
     return this;
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
index b85dbaa..0689d7c 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.origin.Origin;
@@ -130,6 +131,8 @@
     return StartupProfile.merge(startupProfiles);
   }
 
+  public abstract boolean isStartupClass(DexType type);
+
   public abstract Collection<StartupProfileRule> getRules();
 
   public abstract boolean isEmpty();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
index ab251a8..4b35a4b 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/NonEmptyStartupProfile.java
@@ -15,28 +15,33 @@
 import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import java.util.Collection;
 import java.util.LinkedHashMap;
+import java.util.Set;
 import java.util.function.BiConsumer;
 
 public class NonEmptyStartupProfile extends StartupProfile {
 
-  private final LinkedHashMap<DexReference, StartupProfileRule> startupItems;
+  private final Set<DexType> startupClasses;
+  private final LinkedHashMap<DexReference, StartupProfileRule> startupRules;
 
-  public NonEmptyStartupProfile(LinkedHashMap<DexReference, StartupProfileRule> startupItems) {
-    assert !startupItems.isEmpty();
-    this.startupItems = startupItems;
-  }
-
-  @Override
-  public boolean containsMethodRule(DexMethod method) {
-    return startupItems.containsKey(method);
+  public NonEmptyStartupProfile(LinkedHashMap<DexReference, StartupProfileRule> startupRules) {
+    assert !startupRules.isEmpty();
+    this.startupClasses =
+        SetUtils.mapIdentityHashSet(startupRules.keySet(), DexReference::getContextType);
+    this.startupRules = startupRules;
   }
 
   @Override
   public boolean containsClassRule(DexType type) {
-    return startupItems.containsKey(type);
+    return startupRules.containsKey(type);
+  }
+
+  @Override
+  public boolean containsMethodRule(DexMethod method) {
+    return startupRules.containsKey(method);
   }
 
   @Override
@@ -51,17 +56,17 @@
 
   @Override
   public StartupProfileClassRule getClassRule(DexType type) {
-    return (StartupProfileClassRule) startupItems.get(type);
+    return (StartupProfileClassRule) startupRules.get(type);
   }
 
   @Override
   public StartupProfileMethodRule getMethodRule(DexMethod method) {
-    return (StartupProfileMethodRule) startupItems.get(method);
+    return (StartupProfileMethodRule) startupRules.get(method);
   }
 
   @Override
   public Collection<StartupProfileRule> getRules() {
-    return startupItems.values();
+    return startupRules.values();
   }
 
   @Override
@@ -70,6 +75,11 @@
   }
 
   @Override
+  public boolean isStartupClass(DexType type) {
+    return startupClasses.contains(type);
+  }
+
+  @Override
   public StartupProfile rewrittenWithLens(GraphLens graphLens) {
     return transform(
         (classRule, builder) ->
@@ -85,7 +95,7 @@
   }
 
   public int size() {
-    return startupItems.size();
+    return startupRules.size();
   }
 
   /**
@@ -176,7 +186,7 @@
   private StartupProfile transform(
       BiConsumer<StartupProfileClassRule, Builder> classRuleTransformer,
       BiConsumer<StartupProfileMethodRule, Builder> methodRuleTransformer) {
-    Builder builder = builderWithCapacity(startupItems.size());
+    Builder builder = builderWithCapacity(startupRules.size());
     forEachRule(
         classRule -> classRuleTransformer.accept(classRule, builder),
         methodRule -> methodRuleTransformer.accept(methodRule, builder));
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index dc33db7..88bfb4f 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -175,7 +175,7 @@
       feature = classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
     }
     if (feature.isBase()) {
-      return !startupProfile.containsClassRule(type)
+      return !startupProfile.isStartupClass(type)
               || options.getStartupOptions().isStartupBoundaryOptimizationsEnabled()
           ? FeatureSplit.BASE
           : FeatureSplit.BASE_STARTUP;
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
index 37f1494..4c3c809 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
@@ -77,8 +77,8 @@
     } else if (callerIsStartupMethod.isFalse()) {
       // If the caller is not a startup method, then only allow inlining if the caller is not a
       // startup class or the callee is a startup class.
-      if (startupProfile.containsClassRule(caller.getHolderType())
-          && !startupProfile.containsClassRule(callee.getHolderType())) {
+      if (startupProfile.isStartupClass(caller.getHolderType())
+          && !startupProfile.isStartupClass(callee.getHolderType())) {
         return false;
       }
     }
@@ -114,8 +114,8 @@
     // If the source class is a startup class then require that the target class is also a startup
     // class.
     StartupProfile startupProfile = appView.getStartupProfile();
-    if (startupProfile.containsClassRule(sourceClass.getType())
-        && !startupProfile.containsClassRule(targetClass.getType())) {
+    if (startupProfile.isStartupClass(sourceClass.getType())
+        && !startupProfile.isStartupClass(targetClass.getType())) {
       return false;
     }
     return true;
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
index dcd0191..6034910 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexFromStartupMethodRuleTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.startup;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -58,17 +57,11 @@
             primaryDexInspector -> {
               ClassSubject mainClassSubject = primaryDexInspector.clazz(Main.class);
               assertThat(mainClassSubject, isPresent());
-
-              // TODO(b/274591979): Should be in classes2.dex.
-              ClassSubject postStartupClassSubject =
-                  primaryDexInspector.clazz(PostStartupClass.class);
-              assertThat(postStartupClassSubject, isPresent());
             },
             secondaryDexInspector -> {
-              // TODO(b/274591979): Should be present.
               ClassSubject postStartupClassSubject =
                   secondaryDexInspector.clazz(PostStartupClass.class);
-              assertThat(postStartupClassSubject, isAbsent());
+              assertThat(postStartupClassSubject, isPresent());
             })
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("Hello, world!");