Materialize wildcards with captures to apply -if rules in parallel.

Bug: 79486261
Change-Id: Ie0d197bb7365fe7dad76b75fe4fe88deee4c5daf
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index b007f83..568ad3d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -83,6 +83,10 @@
     return nameList == null ? Collections::emptyIterator : nameList.getWildcards();
   }
 
+  protected ProguardClassNameList materialize() {
+    return this;
+  }
+
   public abstract void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer);
 
   private static class EmptyClassNameList extends ProguardClassNameList {
@@ -149,6 +153,11 @@
     }
 
     @Override
+    protected SingleClassNameList materialize() {
+      return new SingleClassNameList(className.materialize());
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       consumer.accept(className);
     }
@@ -202,6 +211,12 @@
     }
 
     @Override
+    protected PositiveClassNameList materialize() {
+      return new PositiveClassNameList(
+          classNames.stream().map(ProguardTypeMatcher::materialize).collect(Collectors.toList()));
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       classNames.forEach(consumer);
     }
@@ -260,6 +275,13 @@
     }
 
     @Override
+    protected ProguardClassNameList materialize() {
+      Builder builder = builder();
+      classNames.forEach((m, negated) -> builder.addClassName(negated, m.materialize()));
+      return builder.build();
+    }
+
+    @Override
     public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
       classNames.object2BooleanEntrySet().forEach(entry -> consumer.accept(entry.getKey()));
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 2e8cccc..47f26ed 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -5,6 +5,7 @@
 
 import com.google.common.collect.Iterables;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class ProguardIfRule extends ProguardKeepRule {
 
@@ -51,6 +52,24 @@
   }
 
   @Override
+  protected ProguardIfRule materialize() {
+    return new ProguardIfRule(
+        getClassAnnotation(),
+        getClassAccessFlags(),
+        getNegatedClassAccessFlags(),
+        getClassTypeNegated(),
+        getClassType(),
+        getClassNames().materialize(),
+        getInheritanceAnnotation() == null ? null : getInheritanceAnnotation().materialize(),
+        getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(),
+        getInheritanceIsExtends(),
+        getMemberRules() == null ? null :
+            getMemberRules().stream()
+                .map(ProguardMemberRule::materialize).collect(Collectors.toList()),
+        subsequentRule.materialize());
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardIfRule)) {
       return false;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 8756f1a..6651dae 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -5,6 +5,7 @@
 
 import java.util.List;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 public class ProguardKeepRule extends ProguardConfigurationRule {
 
@@ -68,6 +69,24 @@
     return modifiers;
   }
 
+  protected ProguardKeepRule materialize() {
+    return new ProguardKeepRule(
+        getClassAnnotation(),
+        getClassAccessFlags(),
+        getNegatedClassAccessFlags(),
+        getClassTypeNegated(),
+        getClassType(),
+        getClassNames() == null ? null : getClassNames().materialize(),
+        getInheritanceAnnotation() == null ? null : getInheritanceAnnotation().materialize(),
+        getInheritanceClassName() == null ? null : getInheritanceClassName().materialize(),
+        getInheritanceIsExtends(),
+        getMemberRules() == null ? null :
+            getMemberRules().stream()
+                .map(ProguardMemberRule::materialize).collect(Collectors.toList()),
+        getType(),
+        getModifiers());
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardKeepRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index c3454a7..39737ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -12,6 +12,7 @@
 import com.google.common.collect.Iterables;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 
 public class ProguardMemberRule {
@@ -278,6 +279,20 @@
     );
   }
 
+  ProguardMemberRule materialize() {
+    return new ProguardMemberRule(
+        getAnnotation() == null ? null : getAnnotation().materialize(),
+        getAccessFlags(),
+        getNegatedAccessFlags(),
+        getRuleType(),
+        getType() == null ? null : getType().materialize(),
+        getName() == null ? null : getName().materialize(),
+        getArguments() == null ? null :
+            getArguments().stream()
+                .map(ProguardTypeMatcher::materialize).collect(Collectors.toList()),
+        getReturnValue());
+  }
+
   @Override
   public boolean equals(Object o) {
     if (!(o instanceof ProguardMemberRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
index 4adb948..c217d45 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -9,6 +9,7 @@
 import com.google.common.collect.ImmutableList;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public abstract class ProguardNameMatcher {
 
@@ -96,11 +97,19 @@
     return nameMatcher == null ? Collections::emptyIterator : nameMatcher.getWildcards();
   }
 
+  protected ProguardNameMatcher materialize() {
+    return this;
+  }
+
   private static class MatchAllNames extends ProguardNameMatcher {
     private final ProguardWildcard wildcard;
 
     MatchAllNames() {
-      this.wildcard = new Pattern("*");
+      this(new Pattern("*"));
+    }
+
+    private MatchAllNames(ProguardWildcard wildcard) {
+      this.wildcard = wildcard;
     }
 
     @Override
@@ -115,6 +124,11 @@
     }
 
     @Override
+    protected MatchAllNames materialize() {
+      return new MatchAllNames(wildcard.materialize());
+    }
+
+    @Override
     public String toString() {
       return "*";
     }
@@ -145,6 +159,15 @@
     }
 
     @Override
+    protected MatchNamePattern materialize() {
+      List<ProguardWildcard> materializedWildcards =
+          wildcards.stream().map(ProguardWildcard::materialize).collect(Collectors.toList());
+      IdentifierPatternWithWildcards identifierPatternWithMaterializedWildcards =
+          new IdentifierPatternWithWildcards(pattern, materializedWildcards);
+      return new MatchNamePattern(identifierPatternWithMaterializedWildcards);
+    }
+
+    @Override
     public String toString() {
       return pattern;
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index 0d5016b..34ee554 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -13,6 +13,7 @@
 import com.google.common.collect.ImmutableList;
 import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public abstract class ProguardTypeMatcher {
 
@@ -40,6 +41,10 @@
     return typeMatcher == null ? Collections::emptyIterator : typeMatcher.getWildcards();
   }
 
+  protected ProguardTypeMatcher materialize() {
+    return this;
+  }
+
   @Override
   public abstract String toString();
 
@@ -99,7 +104,11 @@
     private final ProguardWildcard wildcard;
 
     MatchAllTypes() {
-      this.wildcard = new Pattern(MATCH_ALL_PATTERN);
+      this(new Pattern(MATCH_ALL_PATTERN));
+    }
+
+    private MatchAllTypes(ProguardWildcard wildcard) {
+      this.wildcard = wildcard;
     }
 
     @Override
@@ -114,6 +123,11 @@
     }
 
     @Override
+    protected MatchAllTypes materialize() {
+      return new MatchAllTypes(wildcard.materialize());
+    }
+
+    @Override
     public String toString() {
       return MATCH_ALL_PATTERN;
     }
@@ -170,9 +184,13 @@
     private final ProguardWildcard wildcard;
 
     private MatchClassTypes(String pattern) {
+      this(pattern, new Pattern(pattern));
+    }
+
+    private MatchClassTypes(String pattern, ProguardWildcard wildcard) {
       assert pattern.equals(LEGACY_MATCH_CLASS_PATTERN) || pattern.equals(MATCH_CLASS_PATTERN);
       this.pattern = pattern;
-      this.wildcard = new Pattern(pattern);
+      this.wildcard = wildcard;
     }
 
     @Override
@@ -190,6 +208,11 @@
     }
 
     @Override
+    protected MatchClassTypes materialize() {
+      return new MatchClassTypes(pattern, wildcard.materialize());
+    }
+
+    @Override
     public String toString() {
       return pattern;
     }
@@ -212,7 +235,11 @@
     private final ProguardWildcard wildcard;
 
     MatchBasicTypes() {
-      this.wildcard = new Pattern(MATCH_BASIC_PATTERN);
+      this(new Pattern(MATCH_BASIC_PATTERN));
+    }
+
+    private MatchBasicTypes(ProguardWildcard wildcard) {
+      this.wildcard = wildcard;
     }
 
     @Override
@@ -230,6 +257,11 @@
     }
 
     @Override
+    protected MatchBasicTypes materialize() {
+      return new MatchBasicTypes(wildcard.materialize());
+    }
+
+    @Override
     public String toString() {
       return MATCH_BASIC_PATTERN;
     }
@@ -311,6 +343,15 @@
       return wildcards;
     }
 
+    @Override
+    protected MatchTypePattern materialize() {
+      List<ProguardWildcard> materializedWildcards =
+          wildcards.stream().map(ProguardWildcard::materialize).collect(Collectors.toList());
+      IdentifierPatternWithWildcards identifierPatternWithMaterializedWildcards =
+          new IdentifierPatternWithWildcards(pattern, materializedWildcards);
+      return new MatchTypePattern(identifierPatternWithMaterializedWildcards, kind);
+    }
+
     private static boolean matchClassOrTypeNameImpl(
         String pattern, int patternIndex,
         String name, int nameIndex,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java b/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
index 84151e8..b6cb8e5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWildcard.java
@@ -10,6 +10,7 @@
   abstract void setCaptured(String captured);
   abstract void clearCaptured();
   abstract String getCaptured();
+  abstract ProguardWildcard materialize();
 
   boolean isPattern() {
     return false;
@@ -29,7 +30,6 @@
 
   static class Pattern extends ProguardWildcard {
     final String pattern;
-    // TODO(b/79486261): Having captured part here makes back-reference handling not thread-safe.
     private String captured = null;
 
     Pattern(String pattern) {
@@ -37,21 +37,31 @@
     }
 
     @Override
-    void setCaptured(String captured) {
+    synchronized void setCaptured(String captured) {
       this.captured = captured;
     }
 
     @Override
-    void clearCaptured() {
+    synchronized void clearCaptured() {
       captured = null;
     }
 
     @Override
-    String getCaptured() {
+    synchronized String getCaptured() {
       return captured;
     }
 
     @Override
+    Pattern materialize() {
+      if (captured == null) {
+        return this;
+      }
+      Pattern copy = new Pattern(pattern);
+      copy.setCaptured(captured);
+      return copy;
+    }
+
+    @Override
     boolean isPattern() {
       return true;
     }
@@ -96,6 +106,16 @@
     }
 
     @Override
+    BackReference materialize() {
+      if (reference == null || reference.getCaptured() == null) {
+        return this;
+      }
+      BackReference copy = new BackReference(referenceIndex);
+      copy.setReference(reference.materialize());
+      return copy;
+    }
+
+    @Override
     boolean isBackReference() {
       return true;
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 6a8adfa..700e146 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -348,10 +348,9 @@
             if (ifRule.getClassNames().matches(currentLiveType)) {
               Collection<ProguardMemberRule> memberKeepRules = ifRule.getMemberRules();
               if (memberKeepRules.isEmpty()) {
-                runPerRule(executorService, futures, ifRule.subsequentRule, ifRule);
-                // TODO(b/79486261): remove this barrier per rule.
-                ThreadUtils.awaitFutures(futures);
-                futures.clear();
+                ProguardIfRule materializedRule = ifRule.materialize();
+                runPerRule(
+                    executorService, futures, materializedRule.subsequentRule, materializedRule);
                 // No member rule to satisfy. Move on to the next live type.
                 continue;
               }
@@ -391,17 +390,15 @@
                   }
                 }
                 if (satisfied) {
-                  runPerRule(executorService, futures, ifRule.subsequentRule, ifRule);
-                  // TODO(b/79486261): remove this barrier per rule.
-                  ThreadUtils.awaitFutures(futures);
-                  futures.clear();
+                  ProguardIfRule materializedRule = ifRule.materialize();
+                  runPerRule(
+                      executorService, futures, materializedRule.subsequentRule, materializedRule);
                 }
               }
             }
           }
         }
-        // TODO(b/79486261): This is the best place to fully utilize available threads.
-        // ThreadUtils.awaitFutures(futures);
+        ThreadUtils.awaitFutures(futures);
       }
     } finally {
       application.timing.end();