Add extend support for embedded rules in library providers

Read embedded rules from library dependencies from -libraryjars
in configuration files as well.

Also remove the fixpoint which was not needed.

Bug: b/289087274
Fixes: b/370665838
Change-Id: I7c613724f2ef5d107ce11cf5182b3560a6344328
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 6a7475f..b6c9ae4 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -63,19 +63,19 @@
 import java.io.InputStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Deque;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 /**
  * Immutable command structure for an invocation of the {@link R8} compiler.
@@ -875,43 +875,48 @@
                 }
                 return compilerVersion;
               });
-      // TODO(b/370665838): Remove the fixpoint, as -injars are not supported in embedded keep
-      // rules. The comment below is not true.
-      // Since -injars can itself reference archives with rules and that in turn have -injars the
-      // completion of amending rules and providers must run in a fixed point. The fixed point is
-      // reached once the injars set is stable.
-      Set<FilteredClassPath> seenInjars = SetUtils.newIdentityHashSet();
-      Deque<ProgramResourceProvider> providers =
-          new ArrayDeque<>(getAppBuilder().getProgramResourceProviders());
-      while (true) {
-        for (FilteredClassPath injar : parser.getConfigurationBuilder().getInjars()) {
-          if (seenInjars.add(injar)) {
-            ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(injar);
+      Set<FilteredClassPath> seen = SetUtils.newIdentityHashSet();
+      // Find resources in program providers. Both from API and added through legacy -injars in
+      // configuration files.
+      List<DataResourceProvider> providers =
+          new ArrayList<>(
+              getAppBuilder().getProgramResourceProviders().stream()
+                  .map(ProgramResourceProvider::getDataResourceProvider)
+                  .filter(Objects::nonNull)
+                  .collect(Collectors.toList()));
+      for (FilteredClassPath injar : parser.getConfigurationBuilder().getInjars()) {
+        if (seen.add(injar)) {
+          ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(injar);
+          if (provider != null) {
+            providers.add(provider);
+          }
+        }
+      }
+
+      if (readEmbeddedRulesFromClasspathAndLibrary) {
+        // Find resources in classpath providers. No legacy configuration file option for classpath.
+        getAppBuilder().getClasspathResourceProviders().stream()
+            .map(ClassFileResourceProvider::getDataResourceProvider)
+            .filter(Objects::nonNull)
+            .forEach(providers::add);
+        // Find resources in library providers. Both from API and added through legacy -libraryjars
+        // in configuration files.
+        getAppBuilder().getLibraryResourceProviders().stream()
+            .map(ClassFileResourceProvider::getDataResourceProvider)
+            .filter(Objects::nonNull)
+            .forEach(providers::add);
+        for (FilteredClassPath libraryjar :
+            parser.getConfigurationBuilder().build().getLibraryjars()) {
+          if (seen.add(libraryjar)) {
+            ArchiveResourceProvider provider = getAppBuilder().createAndAddProvider(libraryjar);
             if (provider != null) {
               providers.add(provider);
             }
           }
         }
-        if (providers.isEmpty()) {
-          break;
-        }
-
-        while (!providers.isEmpty()) {
-          DataResourceProvider dataResourceProvider = providers.pop().getDataResourceProvider();
-          parseEmbeddedRules(reporter, parser, semanticVersionSupplier, dataResourceProvider);
-        }
-        // TODO(b/370665838): Remove the fixpoint. For now assert that it is not needed.
-        assert seenInjars.containsAll(parser.getConfigurationBuilder().getInjars());
       }
-      if (readEmbeddedRulesFromClasspathAndLibrary) {
-        for (ClassFileResourceProvider provider : getAppBuilder().getClasspathResourceProviders()) {
-          parseEmbeddedRules(
-              reporter, parser, semanticVersionSupplier, provider.getDataResourceProvider());
-        }
-        for (ClassFileResourceProvider provider : getAppBuilder().getLibraryResourceProviders()) {
-          parseEmbeddedRules(
-              reporter, parser, semanticVersionSupplier, provider.getDataResourceProvider());
-        }
+      for (DataResourceProvider provider : providers) {
+        parseEmbeddedRules(reporter, parser, semanticVersionSupplier, provider);
       }
     }
 
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesFromClasspathOrLibraryTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesFromClasspathOrLibraryTest.java
index d9631f1..552e81d 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesFromClasspathOrLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesFromClasspathOrLibraryTest.java
@@ -16,6 +16,7 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
 import com.android.tools.r8.ClassFileResourceProvider;
@@ -23,13 +24,13 @@
 import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.origin.ArchiveEntryOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
@@ -53,6 +54,12 @@
 
   public interface Interface {}
 
+  private enum HowToAdd {
+    API_CLASSPATH,
+    API_LIBRARY,
+    RULES_LIBRARYJARS
+  }
+
   @Parameter(0)
   public TestParameters parameters;
 
@@ -60,30 +67,36 @@
   public LibraryType libraryType;
 
   @Parameter(2)
-  public boolean isClasspath;
+  public HowToAdd howToAdd;
 
-  @Parameters(name = "{0}, libraryType: {1}, isClasspath {2}")
+  @Parameters(name = "{0}, libraryType: {1}, howToAdd: {2}")
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(),
         LibraryType.values(),
-        BooleanUtils.values());
+        HowToAdd.values());
   }
 
   private Path buildLibrary(List<String> rules) throws Exception {
     return buildLibrary(libraryType, ImmutableList.of(Interface.class, Keep.class), rules);
   }
 
+  private void addClasspathOrLibrary(Path library, R8FullTestBuilder builder) {
+    builder.applyIf(
+        howToAdd == HowToAdd.API_CLASSPATH,
+        b -> b.addClasspathFiles(library),
+        howToAdd == HowToAdd.API_LIBRARY,
+        b ->
+            b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+                .addLibraryFiles(library),
+        b -> b.addKeepRules("-libraryjars " + library.toAbsolutePath()));
+  }
+
   private CodeInspector runTest(List<String> rules) throws Exception {
     Path library = buildLibrary(rules);
     return testForR8(parameters.getBackend())
         .addProgramClasses(A.class, B.class)
-        .applyIf(
-            isClasspath,
-            b -> b.addClasspathFiles(library),
-            b ->
-                b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
-                    .addLibraryFiles(library))
+        .apply(b -> addClasspathOrLibrary(library, b))
         .setMinApi(parameters)
         .apply(b -> ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(b.getBuilder(), true))
         .compile()
@@ -155,12 +168,7 @@
         () -> {
           Path library = buildLibrary(ImmutableList.of("-injars some.jar"));
           testForR8(parameters.getBackend())
-              .applyIf(
-                  isClasspath,
-                  b -> b.addClasspathFiles(library),
-                  b ->
-                      b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
-                          .addLibraryFiles(library))
+              .apply(b -> addClasspathOrLibrary(library, b))
               .setMinApi(parameters)
               .apply(
                   b -> ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(b.getBuilder(), true))
@@ -181,12 +189,7 @@
         () -> {
           Path library = buildLibrary(ImmutableList.of("-include other.rules"));
           testForR8(parameters.getBackend())
-              .applyIf(
-                  isClasspath,
-                  b -> b.addClasspathFiles(library),
-                  b ->
-                      b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
-                          .addLibraryFiles(library))
+              .apply(b -> addClasspathOrLibrary(library, b))
               .setMinApi(parameters)
               .apply(
                   b -> ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(b.getBuilder(), true))
@@ -233,6 +236,7 @@
 
   @Test
   public void throwingDataResourceProvider() {
+    assumeFalse(howToAdd == HowToAdd.RULES_LIBRARYJARS);
     // TODO(b/228319861): Read Proguard rules from AAR's.
     assumeTrue(!libraryType.isAar());
     assertThrows(
@@ -240,7 +244,7 @@
         () ->
             testForR8(parameters.getBackend())
                 .applyIf(
-                    isClasspath,
+                    howToAdd == HowToAdd.API_CLASSPATH,
                     b -> b.addClasspathResourceProviders(new TestProvider()),
                     b ->
                         b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))