Merge commit '3fcd2bde91f2364ed0cbcdba928ff14eaab4c51c' into dev-release
diff --git a/build.gradle b/build.gradle
index abf8ddf..de83e21 100644
--- a/build.gradle
+++ b/build.gradle
@@ -67,7 +67,7 @@
 sourceSets {
     main {
         java {
-            srcDirs = ['src/main/java']
+            srcDirs = ['src/main/java', 'src/keepanno/java']
         }
         resources {
             srcDirs "third_party/api_database/api_database"
@@ -75,7 +75,7 @@
     }
     main11 {
         java {
-            srcDirs = ['src/main/java']
+            srcDirs = ['src/main/java', 'src/keepanno/java']
         }
         resources {
             srcDirs "third_party/api_database/api_database"
@@ -83,7 +83,7 @@
     }
     main17 {
         java {
-            srcDirs = ['src/main/java']
+            srcDirs = ['src/main/java', 'src/keepanno/java']
         }
         resources {
             srcDirs "third_party/api_database/api_database"
@@ -213,7 +213,7 @@
 idea {
     sourceSets.all { SourceSet sources ->
         module {
-            if (sources.name == "main" || sources.name == "keepanno") {
+            if (sources.name == "main") {
                 sourceDirs += sources.java.srcDirs
                 outputDir sources.output.classesDirs[0]
             } else {
@@ -320,8 +320,6 @@
 
     keepannoCompile group: 'org.ow2.asm', name: 'asm', version: asmVersion
     keepannoCompile "com.google.guava:guava:$guavaVersion"
-    testCompile sourceSets.keepanno.output
-    testRuntime sourceSets.keepanno.output
 }
 
 def r8LibPath = "$buildDir/libs/r8lib.jar"
@@ -1106,7 +1104,6 @@
 task testJarSources(type: Jar, dependsOn: [testClasses, buildLibraryDesugarConversions]) {
     archiveFileName = "r8testsbase.jar"
     from sourceSets.test.output
-    from sourceSets.keepanno.output
     // We only want to include tests that use R8 when generating keep rules for applymapping.
     include "com/android/tools/r8/**"
     include "android/**"
@@ -2247,6 +2244,7 @@
 
 test { task ->
 
+    dependsOn sourceSets.keepanno.output
     dependsOn buildLibraryDesugarConversions
     dependsOn getJarsFromSupportLibs
     // R8.jar is required for running bootstrap tests.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
index 8872494..37713a9 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
@@ -3,6 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.annotations;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
 /**
  * A binding of a keep item.
  *
@@ -12,6 +17,8 @@
  *
  * <p>See KeepTarget for documentation on specifying an item pattern.
  */
+@Target(ElementType.ANNOTATION_TYPE)
+@Retention(RetentionPolicy.CLASS)
 public @interface KeepBinding {
 
   /** Name with which other bindings, conditions or targets can reference the bound item pattern. */
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
index c88085a..f37c3fc 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepConstants.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.keepanno.annotations;
 
+
 /**
  * Utility class for referencing the various keep annotations and their structure.
  *
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
index f3e97fa..515becd 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -29,11 +29,66 @@
 
 public class KeepEdgeWriter implements Opcodes {
 
+  /** Annotation visitor interface to allow usage from tests without type conflicts in r8lib. */
+  public interface AnnotationVisitorInterface {
+    int version();
+
+    void visit(String name, Object value);
+
+    void visitEnum(String name, String descriptor, String value);
+
+    AnnotationVisitorInterface visitAnnotation(String name, String descriptor);
+
+    AnnotationVisitorInterface visitArray(String name);
+
+    void visitEnd();
+  }
+
+  private static AnnotationVisitor wrap(AnnotationVisitorInterface visitor) {
+    if (visitor == null) {
+      return null;
+    }
+    return new AnnotationVisitor(visitor.version()) {
+
+      @Override
+      public void visit(String name, Object value) {
+        visitor.visit(name, value);
+      }
+
+      @Override
+      public void visitEnum(String name, String descriptor, String value) {
+        visitor.visitEnum(name, descriptor, value);
+      }
+
+      @Override
+      public AnnotationVisitor visitAnnotation(String name, String descriptor) {
+        AnnotationVisitorInterface v = visitor.visitAnnotation(name, descriptor);
+        return v == visitor ? this : wrap(v);
+      }
+
+      @Override
+      public AnnotationVisitor visitArray(String name) {
+        AnnotationVisitorInterface v = visitor.visitArray(name);
+        return v == visitor ? this : wrap(v);
+      }
+
+      @Override
+      public void visitEnd() {
+        visitor.visitEnd();
+      }
+    };
+  }
+
   public static void writeEdge(KeepEdge edge, ClassVisitor visitor) {
-    writeEdge(edge, visitor::visitAnnotation);
+    writeEdgeInternal(edge, visitor::visitAnnotation);
   }
 
   public static void writeEdge(
+      KeepEdge edge, BiFunction<String, Boolean, AnnotationVisitorInterface> getVisitor) {
+    writeEdgeInternal(edge, (descriptor, visible) -> wrap(getVisitor.apply(descriptor, visible)));
+  }
+
+  public static void writeEdgeInternal(
       KeepEdge edge, BiFunction<String, Boolean, AnnotationVisitor> getVisitor) {
     new KeepEdgeWriter().writeEdge(edge, getVisitor.apply(Edge.DESCRIPTOR, false));
   }
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index e579889..9f5c949 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -15,10 +15,14 @@
 import com.android.tools.r8.inspector.Inspector;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
+import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
 import com.android.tools.r8.naming.SourceFileRewriter;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.profile.art.ArtProfileForRewriting;
+import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationParser;
 import com.android.tools.r8.shaking.ProguardConfigurationParserOptions;
@@ -30,6 +34,7 @@
 import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ArchiveResourceProvider;
 import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
 import com.android.tools.r8.utils.DumpInputFlags;
 import com.android.tools.r8.utils.ExceptionDiagnostic;
@@ -40,18 +45,22 @@
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.ProgramClassCollection;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.ImmutableList;
 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;
@@ -117,6 +126,7 @@
     private final List<FeatureSplit> featureSplits = new ArrayList<>();
     private String synthesizedClassPrefix = "";
     private boolean enableMissingLibraryApiModeling = false;
+    private boolean enableExperimentalKeepAnnotations = false;
 
     private final ProguardConfigurationParserOptions.Builder parserOptionsBuilder =
         ProguardConfigurationParserOptions.builder().readEnvironment();
@@ -442,6 +452,12 @@
       return self();
     }
 
+    @Deprecated
+    public Builder setEnableExperimentalKeepAnnotations(boolean enable) {
+      this.enableExperimentalKeepAnnotations = enable;
+      return self();
+    }
+
     @Override
     protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
         Path path,
@@ -556,47 +572,6 @@
       ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder();
       configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility);
 
-      if (proguardConfigurationConsumerForTesting != null) {
-        proguardConfigurationConsumerForTesting.accept(configurationBuilder);
-      }
-
-      // Process Proguard configurations supplied through data resources in the input.
-      DataResourceProvider.Visitor embeddedProguardConfigurationVisitor =
-          new DataResourceProvider.Visitor() {
-            @Override
-            public void visit(DataDirectoryResource directory) {
-              // Don't do anything.
-            }
-
-            @Override
-            public void visit(DataEntryResource resource) {
-              if (resource.getName().startsWith("META-INF/proguard/")) {
-                try (InputStream in = resource.getByteStream()) {
-                  ProguardConfigurationSource source =
-                      new ProguardConfigurationSourceBytes(in, resource.getOrigin());
-                  parser.parse(source);
-                } catch (ResourceException e) {
-                  reporter.error(new StringDiagnostic("Failed to open input: " + e.getMessage(),
-                      resource.getOrigin()));
-                } catch (Exception e) {
-                  reporter.error(new ExceptionDiagnostic(e, resource.getOrigin()));
-                }
-              }
-            }
-          };
-
-      getAppBuilder().getProgramResourceProviders().stream()
-          .map(ProgramResourceProvider::getDataResourceProvider)
-          .filter(Objects::nonNull)
-          .forEach(
-              dataResourceProvider -> {
-                try {
-                  dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
-                } catch (ResourceException e) {
-                  reporter.error(new ExceptionDiagnostic(e));
-                }
-              });
-
       if (getMode() == CompilationMode.DEBUG) {
         disableMinification = true;
         configurationBuilder.disableOptimization();
@@ -610,10 +585,15 @@
         configurationBuilder.disableObfuscation();
       }
 
+      if (proguardConfigurationConsumerForTesting != null) {
+        proguardConfigurationConsumerForTesting.accept(configurationBuilder);
+      }
+      amendWithRulesAndProvidersForInjarsAndMetaInf(reporter, parser);
+      // Extract out rules for keep annotations and amend the configuration.
+      // TODO(b/248408342): Remove this and parse annotations as part of R8 root-set & enqueuer.
+      extractKeepAnnotationRules(parser);
       ProguardConfiguration configuration = configurationBuilder.build();
-      getAppBuilder()
-          .addFilteredProgramArchives(configuration.getInjars())
-          .addFilteredLibraryArchives(configuration.getLibraryjars());
+      getAppBuilder().addFilteredLibraryArchives(configuration.getLibraryjars());
 
       assert getProgramConsumer() != null;
 
@@ -673,6 +653,92 @@
       return command;
     }
 
+    private void amendWithRulesAndProvidersForInjarsAndMetaInf(
+        Reporter reporter, ProguardConfigurationParser parser) {
+
+      // Process Proguard configurations supplied through data resources in the input.
+      DataResourceProvider.Visitor embeddedProguardConfigurationVisitor =
+          new DataResourceProvider.Visitor() {
+            @Override
+            public void visit(DataDirectoryResource directory) {
+              // Don't do anything.
+            }
+
+            @Override
+            public void visit(DataEntryResource resource) {
+              if (resource.getName().startsWith("META-INF/proguard/")) {
+                try (InputStream in = resource.getByteStream()) {
+                  ProguardConfigurationSource source =
+                      new ProguardConfigurationSourceBytes(in, resource.getOrigin());
+                  parser.parse(source);
+                } catch (ResourceException e) {
+                  reporter.error(
+                      new StringDiagnostic(
+                          "Failed to open input: " + e.getMessage(), resource.getOrigin()));
+                } catch (Exception e) {
+                  reporter.error(new ExceptionDiagnostic(e, resource.getOrigin()));
+                }
+              }
+            }
+          };
+
+      // 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);
+            if (provider != null) {
+              providers.add(provider);
+            }
+          }
+        }
+        if (providers.isEmpty()) {
+          return;
+        }
+        while (!providers.isEmpty()) {
+          DataResourceProvider dataResourceProvider = providers.pop().getDataResourceProvider();
+          if (dataResourceProvider != null) {
+            try {
+              dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
+            } catch (ResourceException e) {
+              reporter.error(new ExceptionDiagnostic(e));
+            }
+          }
+        }
+      }
+    }
+
+    private void extractKeepAnnotationRules(ProguardConfigurationParser parser) {
+      if (!enableExperimentalKeepAnnotations) {
+        return;
+      }
+      try {
+        for (ProgramResourceProvider provider : getAppBuilder().getProgramResourceProviders()) {
+          for (ProgramResource resource : provider.getProgramResources()) {
+            if (resource.getKind() == Kind.CF) {
+              Set<KeepEdge> edges = KeepEdgeReader.readKeepEdges(resource.getBytes());
+              KeepRuleExtractor extractor =
+                  new KeepRuleExtractor(
+                      rule -> {
+                        ProguardConfigurationSourceStrings source =
+                            new ProguardConfigurationSourceStrings(
+                                Collections.singletonList(rule), null, resource.getOrigin());
+                        parser.parse(source);
+                      });
+              edges.forEach(extractor::extract);
+            }
+          }
+        }
+      } catch (ResourceException e) {
+        throw getAppBuilder().getReporter().fatalError(new ExceptionDiagnostic(e));
+      }
+    }
+
     // Internal for-testing method to add post-processors of the proguard configuration.
     void addProguardConfigurationConsumerForTesting(Consumer<ProguardConfiguration.Builder> c) {
       Consumer<ProguardConfiguration.Builder> oldConsumer = proguardConfigurationConsumerForTesting;
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 761298d..d7a47a1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1224,12 +1224,11 @@
   }
 
   public static DexEncodedMethod createDesugaringForwardingMethod(
-      DexEncodedMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
-    DexMethod method = target.getReference();
+      DexClassAndMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
     assert forwardMethod != null;
     // New method will have the same name, proto, and also all the flags of the
     // default method, including bridge flag.
-    DexMethod newMethod = factory.createMethod(clazz.type, method.proto, method.name);
+    DexMethod newMethod = target.getReference().withHolder(clazz, factory);
     MethodAccessFlags newFlags = target.getAccessFlags().copy();
     // Some debuggers (like IntelliJ) automatically skip synthetic methods on single step.
     newFlags.setSynthetic();
@@ -1246,8 +1245,8 @@
                 .setNonStaticSource(newMethod)
                 .setStaticTarget(forwardMethod, isInterfaceMethodReference)
                 .build())
-        .setApiLevelForDefinition(target.getApiLevelForDefinition())
-        .setApiLevelForCode(target.getApiLevelForCode())
+        .setApiLevelForDefinition(target.getDefinition().getApiLevelForDefinition())
+        .setApiLevelForCode(target.getDefinition().getApiLevelForCode())
         .build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 1673079..be7bf1c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -296,10 +296,8 @@
         new NoDifferentApiReferenceLevel(appView),
         new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
         new NoWeakerAccessPrivileges(appView, immediateSubtypingInfo),
-        new PreventClassMethodAndDefaultMethodCollisions(appView, immediateSubtypingInfo));
-    if (appView.options().canHaveIssueWithInlinedMonitors()) {
-      builder.add(new NotTwoInitsWithMonitors());
-    }
+        new PreventClassMethodAndDefaultMethodCollisions(appView, immediateSubtypingInfo),
+        new NotTwoInitsWithMonitors(appView));
   }
 
   private static void addMultiClassPoliciesForMergingNonSyntheticClasses(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotTwoInitsWithMonitors.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotTwoInitsWithMonitors.java
index d5cebe2..3b7e6c7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotTwoInitsWithMonitors.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotTwoInitsWithMonitors.java
@@ -4,12 +4,19 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
+import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 
 public class NotTwoInitsWithMonitors extends AtMostOneClassThatMatchesPolicy {
 
+  private final AppView<?> appView;
+
+  public NotTwoInitsWithMonitors(AppView<?> appView) {
+    this.appView = appView;
+  }
+
   @Override
   public boolean atMostOneOf(DexProgramClass clazz) {
     for (ProgramMethod initializer : clazz.programInstanceInitializers()) {
@@ -25,4 +32,9 @@
   public String getName() {
     return "NotTwoInitsWithMonitors";
   }
+
+  @Override
+  public boolean shouldSkipPolicy() {
+    return !appView.options().canHaveIssueWithInlinedMonitors();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 1ccb991..c1bd17b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -125,7 +125,11 @@
               .build();
     }
 
-    converter.prepareDesugaringForD8(executorService);
+    CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForPrepareStep =
+        CfInstructionDesugaringEventConsumer.createForD8(appView, resultBuilder, methodProcessor);
+    converter.prepareDesugaringForD8(
+        instructionDesugaringEventConsumerForPrepareStep, executorService);
+    assert instructionDesugaringEventConsumerForPrepareStep.verifyNothingToFinalize();
 
     // When adding nest members to the wave we must do so deterministically.
     Deque<List<DexProgramClass>> nestProcessingWaves = getDeterministicNestWaves(classes);
@@ -144,19 +148,22 @@
         methodProcessor.addScheduled(clazz);
       }
 
-      CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer =
+      CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForWave =
           CfInstructionDesugaringEventConsumer.createForD8(appView, resultBuilder, methodProcessor);
 
       // Process the wave and wait for all IR processing to complete.
       methodProcessor.newWave();
       checkWaveDeterminism(wave);
       ThreadUtils.processItems(
-          wave, clazz -> convertClass(clazz, instructionDesugaringEventConsumer), executorService);
+          wave,
+          clazz -> convertClass(clazz, instructionDesugaringEventConsumerForWave),
+          executorService);
       methodProcessor.awaitMethodProcessing();
 
       // Finalize the desugaring of the processed classes. This may require processing (and
       // reprocessing) of some methods.
-      List<ProgramMethod> needsProcessing = instructionDesugaringEventConsumer.finalizeDesugaring();
+      List<ProgramMethod> needsProcessing =
+          instructionDesugaringEventConsumerForWave.finalizeDesugaring();
       if (!needsProcessing.isEmpty()) {
         // Create a new processor context to ensure unique method processing contexts.
         methodProcessor.newWave();
@@ -170,16 +177,16 @@
               if (definition.isProcessed()) {
                 definition.markNotProcessed();
               }
-              methodProcessor.processMethod(method, instructionDesugaringEventConsumer);
+              methodProcessor.processMethod(method, instructionDesugaringEventConsumerForWave);
               if (interfaceProcessor != null) {
-                interfaceProcessor.processMethod(method, instructionDesugaringEventConsumer);
+                interfaceProcessor.processMethod(method, instructionDesugaringEventConsumerForWave);
               }
             },
             executorService);
 
         // Verify there is nothing to finalize once method processing finishes.
         methodProcessor.awaitMethodProcessing();
-        assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
+        assert instructionDesugaringEventConsumerForWave.verifyNothingToFinalize();
       }
 
       if (!nestProcessingWaves.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 547fef4..ccb3126 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -471,7 +471,9 @@
         DesugaredLibraryAPIConverter::generateTrackingWarnings);
   }
 
-  public void prepareDesugaringForD8(ExecutorService executorService) throws ExecutionException {
+  public void prepareDesugaringForD8(
+      CfInstructionDesugaringEventConsumer desugaringEventConsumer, ExecutorService executorService)
+      throws ExecutionException {
     // Prepare desugaring by collecting all the synthetic methods required on program classes.
     ProgramAdditions programAdditions = new ProgramAdditions();
     ThreadUtils.processItems(
@@ -479,7 +481,8 @@
         clazz -> {
           clazz.forEachProgramMethodMatching(
               method -> method.hasCode() && method.getCode().isCfCode(),
-              method -> instructionDesugaring.prepare(method, programAdditions));
+              method ->
+                  instructionDesugaring.prepare(method, desugaringEventConsumer, programAdditions));
         },
         executorService);
     programAdditions.apply(executorService);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
index 8f1ddeb..0d454d6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaring.java
@@ -26,7 +26,10 @@
    * synthetic items can be added and the instruction stream can be altered, but program methods
    * cannot be added.
    */
-  default void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+  default void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     // Default prepare is to do nothing.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index c457ace..90eda8a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -44,7 +44,10 @@
     return EmptyCfInstructionDesugaringCollection.getInstance();
   }
 
-  public abstract void prepare(ProgramMethod method, ProgramAdditions programAdditions);
+  public abstract void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions);
 
   public abstract void scan(
       ProgramMethod method, CfInstructionDesugaringEventConsumer eventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index edead3e..312eda4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
@@ -125,7 +126,19 @@
     }
 
     @Override
-    public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+    public void acceptDefaultAsCompanionMethod(
+        ProgramMethod method, ProgramMethod companionMethod) {
+      // Intentionally empty. Methods are moved when processing the interface definition.
+    }
+
+    @Override
+    public void acceptPrivateAsCompanionMethod(
+        ProgramMethod method, ProgramMethod companionMethod) {
+      // Intentionally empty. Methods are moved when processing the interface definition.
+    }
+
+    @Override
+    public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
       // Intentionally empty. Methods are moved when processing the interface definition.
     }
 
@@ -202,18 +215,30 @@
     }
 
     @Override
-    public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
-      assert false;
+    public void acceptNestConstructorBridge(
+        ProgramMethod target,
+        ProgramMethod bridge,
+        DexProgramClass argumentClass,
+        DexClassAndMethod context) {
+      // Intentionally empty.
     }
 
     @Override
-    public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
-      assert false;
+    public void acceptNestFieldGetBridge(
+        ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+      // Intentionally empty.
     }
 
     @Override
-    public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
-      assert false;
+    public void acceptNestFieldPutBridge(
+        ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void acceptNestMethodBridge(
+        ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
+      // Intentionally empty.
     }
 
     @Override
@@ -363,7 +388,19 @@
     }
 
     @Override
-    public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+    public void acceptDefaultAsCompanionMethod(
+        ProgramMethod method, ProgramMethod companionMethod) {
+      onCompanionMethodCallback.accept(method, companionMethod);
+    }
+
+    @Override
+    public void acceptPrivateAsCompanionMethod(
+        ProgramMethod method, ProgramMethod companionMethod) {
+      onCompanionMethodCallback.accept(method, companionMethod);
+    }
+
+    @Override
+    public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
       onCompanionMethodCallback.accept(method, companionMethod);
     }
 
@@ -478,18 +515,30 @@
     }
 
     @Override
-    public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
-      assert false;
+    public void acceptNestConstructorBridge(
+        ProgramMethod target,
+        ProgramMethod bridge,
+        DexProgramClass argumentClass,
+        DexClassAndMethod context) {
+      // Intentionally empty.
     }
 
     @Override
-    public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
-      assert false;
+    public void acceptNestFieldGetBridge(
+        ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+      // Intentionally empty.
     }
 
     @Override
-    public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
-      assert false;
+    public void acceptNestFieldPutBridge(
+        ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void acceptNestMethodBridge(
+        ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
+      // Intentionally empty.
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index f96680f..0d37795 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -13,6 +14,8 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessingDesugaringEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCfPostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import java.util.concurrent.ExecutionException;
@@ -33,11 +36,15 @@
     return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, instructionDesugaring);
   }
 
-  public static R8PostProcessingDesugaringEventConsumer createForR8(
+  public static CfPostProcessingDesugaringEventConsumer createForR8(
       SyntheticAdditions additions,
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
       CfInstructionDesugaringCollection desugaring,
       BiConsumer<DexProgramClass, DexType> missingClassConsumer) {
-    return new R8PostProcessingDesugaringEventConsumer(additions, desugaring, missingClassConsumer);
+    CfPostProcessingDesugaringEventConsumer eventConsumer =
+        new R8PostProcessingDesugaringEventConsumer(additions, desugaring, missingClassConsumer);
+    return ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.attach(
+        artProfileCollectionAdditions, eventConsumer);
   }
 
   public abstract void finalizeDesugaring() throws ExecutionException;
@@ -92,7 +99,18 @@
     }
 
     @Override
-    public void acceptForwardingMethod(ProgramMethod method) {
+    public void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method) {
+      addMethodToReprocess(method);
+    }
+
+    @Override
+    public void acceptInterfaceMethodDesugaringForwardingMethod(
+        ProgramMethod method, DexClassAndMethod baseMethod) {
+      addMethodToReprocess(method);
+    }
+
+    @Override
+    public void acceptThrowingMethod(ProgramMethod method, DexType errorType) {
       addMethodToReprocess(method);
     }
 
@@ -180,7 +198,18 @@
     }
 
     @Override
-    public void acceptForwardingMethod(ProgramMethod method) {
+    public void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method) {
+      additions.addLiveMethod(method);
+    }
+
+    @Override
+    public void acceptInterfaceMethodDesugaringForwardingMethod(
+        ProgramMethod method, DexClassAndMethod baseMethod) {
+      additions.addLiveMethod(method);
+    }
+
+    @Override
+    public void acceptThrowingMethod(ProgramMethod method, DexType errorType) {
       additions.addLiveMethod(method);
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index e7a1587..64bc96e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -30,7 +30,10 @@
   }
 
   @Override
-  public void prepare(ProgramMethod method, ProgramAdditions additionalProgramMethods) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions additionalProgramMethods) {
     // Intentionally empty.
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 20e5a46..97216e8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -187,9 +187,12 @@
   }
 
   @Override
-  public void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     ensureCfCode(method);
-    desugarings.forEach(d -> d.prepare(method, programAdditions));
+    desugarings.forEach(d -> d.prepare(method, eventConsumer, programAdditions));
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java b/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
index cf57453..932ea7c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ProgramAdditions.java
@@ -5,48 +5,47 @@
 package com.android.tools.r8.ir.desugar;
 
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.utils.ThreadUtils;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
 import java.util.function.Supplier;
 
-public class ProgramAdditions implements BiConsumer<DexMember<?, ?>, Supplier<ProgramMethod>> {
-  private final Set<DexReference> added = Sets.newConcurrentHashSet();
-  private final Map<DexProgramClass, List<DexEncodedMethod>> additions = new ConcurrentHashMap<>();
+public class ProgramAdditions {
 
-  @Override
-  public synchronized void accept(
-      DexMember<?, ?> reference, Supplier<ProgramMethod> programMethodSupplier) {
-    if (added.add(reference)) {
-      ProgramMethod method = programMethodSupplier.get();
-      List<DexEncodedMethod> methods =
-          additions.computeIfAbsent(method.getHolder(), k -> new ArrayList<>());
-      synchronized (methods) {
-        assert !methods.contains(method.getDefinition());
-        assert method.getHolder().lookupProgramMethod(method.getReference()) == null;
-        methods.add(method.getDefinition());
-      }
-    }
+  private final Map<DexType, Map<DexMethod, ProgramMethod>> additions = new ConcurrentHashMap<>();
+
+  public ProgramMethod ensureMethod(
+      DexMethod methodReference, Supplier<ProgramMethod> programMethodSupplier) {
+    Map<DexMethod, ProgramMethod> classAdditions =
+        additions.computeIfAbsent(
+            methodReference.getHolderType(), key -> new ConcurrentHashMap<>());
+    return classAdditions.computeIfAbsent(
+        methodReference,
+        key -> {
+          ProgramMethod method = programMethodSupplier.get();
+          assert method.getHolder().lookupProgramMethod(method.getReference()) == null;
+          return method;
+        });
   }
 
   public void apply(ExecutorService executorService) throws ExecutionException {
     ThreadUtils.processMap(
         additions,
-        (clazz, methods) -> {
-          methods.sort(Comparator.comparing(DexEncodedMethod::getReference));
-          clazz.getMethodCollection().addDirectMethods(methods);
+        (holderType, methodMap) -> {
+          DexProgramClass holder = methodMap.values().iterator().next().getHolder();
+          List<DexEncodedMethod> newDirectMethods = new ArrayList<>();
+          methodMap.values().forEach(method -> newDirectMethods.add(method.getDefinition()));
+          newDirectMethods.sort(Comparator.comparing(DexEncodedMethod::getReference));
+          holder.getMethodCollection().addDirectMethods(newDirectMethods);
         },
         executorService);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index 53b8207..cc5abfd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
@@ -136,7 +137,8 @@
       if (clazz.lookupVirtualMethod(method) == null) {
         DexEncodedMethod newMethod = createForwardingMethod(itfMethod, descriptor, clazz);
         clazz.addVirtualMethod(newMethod);
-        eventConsumer.acceptForwardingMethod(new ProgramMethod(clazz, newMethod));
+        eventConsumer.acceptDesugaredLibraryRetargeterForwardingMethod(
+            new ProgramMethod(clazz, newMethod));
       }
     }
   }
@@ -148,8 +150,8 @@
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
     DexMethod forwardMethod = syntheticHelper.forwardingMethod(descriptor);
     assert forwardMethod != null && forwardMethod != target;
-    DexEncodedMethod resolvedMethod =
-        appView.appInfoForDesugaring().resolveMethodLegacy(target, true).getResolvedMethod();
+    DexClassAndMethod resolvedMethod =
+        appView.appInfoForDesugaring().resolveMethodLegacy(target, true).getResolutionPair();
     assert resolvedMethod != null;
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java
index 7e86c5b..63dcba2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSynthesizerEventConsumer.java
@@ -25,6 +25,6 @@
       extends DesugaredLibraryRetargeterInstructionEventConsumer {
     void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface);
 
-    void acceptForwardingMethod(ProgramMethod method);
+    void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 42f22a6..0c5ed70 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexLibraryClass;
@@ -39,8 +40,8 @@
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
 import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import com.google.common.base.Equivalence.Wrapper;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
@@ -55,6 +56,8 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -71,6 +74,74 @@
  */
 final class ClassProcessor {
 
+  private abstract static class SyntheticMethodInfo {
+
+    private final ProgramMethod method;
+
+    SyntheticMethodInfo(ProgramMethod method) {
+      this.method = method;
+    }
+
+    ProgramMethod getMethod() {
+      return method;
+    }
+
+    boolean isForwardingMethodInfo() {
+      return false;
+    }
+
+    SyntheticForwardingMethodInfo asForwardingMethodInfo() {
+      return null;
+    }
+
+    SyntheticThrowingMethodInfo asThrowingMethodInfo() {
+      return null;
+    }
+  }
+
+  private static class SyntheticForwardingMethodInfo extends SyntheticMethodInfo {
+
+    private final DexClassAndMethod baseMethod;
+
+    SyntheticForwardingMethodInfo(ProgramMethod method, DexClassAndMethod baseMethod) {
+      super(method);
+      this.baseMethod = baseMethod;
+    }
+
+    DexClassAndMethod getBaseMethod() {
+      return baseMethod;
+    }
+
+    @Override
+    boolean isForwardingMethodInfo() {
+      return true;
+    }
+
+    @Override
+    SyntheticForwardingMethodInfo asForwardingMethodInfo() {
+      return this;
+    }
+  }
+
+  private static class SyntheticThrowingMethodInfo extends SyntheticMethodInfo {
+
+    private final DexType errorType;
+
+    SyntheticThrowingMethodInfo(ProgramMethod method, DexType errorType) {
+      super(method);
+      this.errorType = errorType;
+    }
+
+    DexType getErrorType() {
+      return errorType;
+    }
+
+    @Override
+    SyntheticThrowingMethodInfo asThrowingMethodInfo() {
+      return this;
+    }
+  }
+
   // Collection for method signatures that may cause forwarding methods to be created.
   private static class MethodSignatures {
 
@@ -370,7 +441,7 @@
   private final Map<DexClass, SignaturesInfo> interfaceInfo = new ConcurrentHashMap<>();
 
   // Mapping from actual program classes to the synthesized forwarding methods to be created.
-  private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods =
+  private final Map<DexProgramClass, Map<DexMethod, SyntheticMethodInfo>> newSyntheticMethods =
       new ConcurrentHashMap<>();
 
   // Mapping from actual program classes to the extra interfaces needed for emulated dispatch.
@@ -415,22 +486,37 @@
           new ReportingContext(
               clazz,
               clazz,
-              (context, missing) -> eventConsumer.warnMissingInterface(context, missing, helper)));
+              (context, missing) -> eventConsumer.warnMissingInterface(context, missing, helper)),
+          eventConsumer);
     }
   }
 
   // We introduce forwarding methods only once all desugaring has been performed to avoid
   // confusing the look-up with inserted forwarding methods.
-  public void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) {
-    newSyntheticMethods.forEach(
-        (clazz, newForwardingMethods) -> {
-          List<ProgramMethod> sorted = new ArrayList<>(newForwardingMethods.toCollection());
-          sorted.sort(Comparator.comparing(ProgramMethod::getReference));
-          for (ProgramMethod method : sorted) {
-            clazz.addVirtualMethod(method.getDefinition());
-            eventConsumer.acceptForwardingMethod(method);
+  public void finalizeProcessing(
+      InterfaceProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService)
+      throws ExecutionException {
+    ThreadUtils.processMap(
+        newSyntheticMethods,
+        (clazz, infos) -> {
+          List<DexEncodedMethod> sortedDefinitions = new ArrayList<>(infos.size());
+          for (SyntheticMethodInfo info : infos.values()) {
+            sortedDefinitions.add(info.getMethod().getDefinition());
           }
-        });
+          sortedDefinitions.sort(Comparator.comparing(DexEncodedMember::getReference));
+          clazz.addVirtualMethods(sortedDefinitions);
+          for (DexEncodedMethod definition : sortedDefinitions) {
+            SyntheticMethodInfo info = infos.get(definition.getReference());
+            if (info.isForwardingMethodInfo()) {
+              eventConsumer.acceptInterfaceMethodDesugaringForwardingMethod(
+                  info.getMethod(), info.asForwardingMethodInfo().getBaseMethod());
+            } else {
+              eventConsumer.acceptThrowingMethod(
+                  info.getMethod(), info.asThrowingMethodInfo().getErrorType());
+            }
+          }
+        },
+        executorService);
     newExtraInterfaceSignatures.forEach(
         (clazz, extraInterfaceSignatures) -> {
           if (!extraInterfaceSignatures.isEmpty()) {
@@ -501,10 +587,14 @@
 
   // The computation of a class information and the insertions of forwarding methods.
   private ClassInfo computeClassInfo(
-      DexClass clazz, ClassInfo superInfo, SignaturesInfo signatureInfo) {
+      DexClass clazz,
+      ClassInfo superInfo,
+      SignaturesInfo signatureInfo,
+      InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     ImmutableList.Builder<DexClassAndMethod> additionalForwards = ImmutableList.builder();
     // First we deal with non-emulated interface desugaring.
-    resolveForwardingMethods(clazz, superInfo, signatureInfo.signatures, additionalForwards);
+    resolveForwardingMethods(
+        clazz, superInfo, signatureInfo.signatures, additionalForwards, eventConsumer);
     // Second we deal with emulated interface, if one method has override in the current class,
     // we resolve them, else we propagate the emulated interface info down.
     if (shouldResolveForwardingMethodsForEmulatedInterfaces(
@@ -513,7 +603,8 @@
           clazz,
           superInfo,
           signatureInfo.emulatedInterfaceSignaturesToForward(),
-          additionalForwards);
+          additionalForwards,
+          eventConsumer);
       duplicateEmulatedInterfaces(clazz, signatureInfo.emulatedInterfaceInfo.emulatedInterfaces);
       return ClassInfo.create(superInfo, additionalForwards.build(), EmulatedInterfaceInfo.EMPTY);
     }
@@ -709,7 +800,8 @@
       DexClass clazz,
       ClassInfo superInfo,
       MethodSignatures signatures,
-      Builder<DexClassAndMethod> additionalForwards) {
+      Builder<DexClassAndMethod> additionalForwards,
+      InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     if (clazz.isProgramClass() && appView.isAlreadyLibraryDesugared(clazz.asProgramClass())) {
       return;
     }
@@ -722,14 +814,18 @@
               additionalForwards.add(target);
               addForwardingMethod(target, forward, clazz);
             }
-          });
+          },
+          eventConsumer);
     }
   }
 
   // Looks up a method signature from the point of 'clazz', if it can dispatch to a default method
   // the 'addForward' call-back is called with the target of the forward.
   private void resolveForwardForSignature(
-      DexClass clazz, DexMethod method, BiConsumer<DexClassAndMethod, DexMethod> addForward) {
+      DexClass clazz,
+      DexMethod method,
+      BiConsumer<DexClassAndMethod, DexMethod> addForward,
+      InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
     MethodResolutionResult resolutionResult = appInfo.resolveMethodOnLegacy(clazz, method);
     if (resolutionResult.isFailedResolution()
@@ -799,7 +895,9 @@
     if (virtualDispatchTarget.isDefaultMethod()) {
       addForward.accept(
           virtualDispatchTarget,
-          helper.ensureDefaultAsMethodOfCompanionClassStub(virtualDispatchTarget).getReference());
+          helper
+              .ensureDefaultAsMethodOfCompanionClassStub(virtualDispatchTarget, eventConsumer)
+              .getReference());
       return;
     }
 
@@ -808,16 +906,25 @@
             virtualDispatchTarget.getHolder(), virtualDispatchTarget);
     if (forwardingMethod != null) {
       DexMethod concreteForwardingMethod =
-          helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod);
+          helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod, eventConsumer);
       addForward.accept(virtualDispatchTarget, concreteForwardingMethod);
     }
   }
 
-  // Construction of actual forwarding methods.
-  private void addSyntheticMethod(DexProgramClass clazz, DexEncodedMethod method) {
-    newSyntheticMethods
-        .computeIfAbsent(clazz, key -> ProgramMethodSet.create())
-        .createAndAdd(clazz, method);
+  private void addSyntheticForwardingMethod(ProgramMethod method, DexClassAndMethod baseMethod) {
+    SyntheticMethodInfo existingMethodInfo =
+        newSyntheticMethods
+            .computeIfAbsent(method.getHolder(), key -> new ConcurrentHashMap<>())
+            .put(method.getReference(), new SyntheticForwardingMethodInfo(method, baseMethod));
+    assert existingMethodInfo == null;
+  }
+
+  private void addSyntheticThrowingMethod(ProgramMethod method, DexType errorType) {
+    SyntheticMethodInfo existingMethodInfo =
+        newSyntheticMethods
+            .computeIfAbsent(method.getHolder(), key -> new ConcurrentHashMap<>())
+            .put(method.getReference(), new SyntheticThrowingMethodInfo(method, errorType));
+    assert existingMethodInfo == null;
   }
 
   private void addICCEThrowingMethod(DexMethod method, DexClass clazz) {
@@ -846,7 +953,7 @@
                 createExceptionThrowingCfCode(newMethod, accessFlags, errorType, dexItemFactory))
             .disableAndroidApiLevelCheck()
             .build();
-    addSyntheticMethod(clazz.asProgramClass(), newEncodedMethod);
+    addSyntheticThrowingMethod(newEncodedMethod.asProgramMethod(clazz.asProgramClass()), errorType);
   }
 
   private static CfCode createExceptionThrowingCfCode(
@@ -894,12 +1001,12 @@
     // In desugared library, emulated interface methods can be overridden by retarget lib members.
     DexEncodedMethod desugaringForwardingMethod =
         DexEncodedMethod.createDesugaringForwardingMethod(
-            target.getDefinition(), clazz, forwardMethod, dexItemFactory);
-    if (!target.isProgramDefinition()
-        || target.getDefinition().isLibraryMethodOverride().isTrue()) {
+            target, clazz, forwardMethod, dexItemFactory);
+    if (!target.isProgramMethod() || target.getDefinition().isLibraryMethodOverride().isTrue()) {
       desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
     }
-    addSyntheticMethod(clazz.asProgramClass(), desugaringForwardingMethod);
+    addSyntheticForwardingMethod(
+        desugaringForwardingMethod.asProgramMethod(clazz.asProgramClass()), target);
   }
 
   // Topological order traversal and its helpers.
@@ -936,24 +1043,34 @@
     }
   }
 
-  private ClassInfo visitClassInfo(DexType type, ReportingContext context) {
+  private ClassInfo visitClassInfo(
+      DexType type,
+      ReportingContext context,
+      InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     DexClass clazz = definitionOrNull(type, context);
-    return clazz == null ? ClassInfo.EMPTY : visitClassInfo(clazz, context);
+    return clazz == null ? ClassInfo.EMPTY : visitClassInfo(clazz, context, eventConsumer);
   }
 
-  private ClassInfo visitClassInfo(DexClass clazz, ReportingContext context) {
+  private ClassInfo visitClassInfo(
+      DexClass clazz,
+      ReportingContext context,
+      InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     assert !clazz.isInterface();
     if (clazz.isLibraryClass()) {
       return ClassInfo.EMPTY;
     }
-    return reentrantComputeIfAbsent(classInfo, clazz, key -> visitClassInfoRaw(key, context));
+    return reentrantComputeIfAbsent(
+        classInfo, clazz, key -> visitClassInfoRaw(key, context, eventConsumer));
   }
 
-  private ClassInfo visitClassInfoRaw(DexClass clazz, ReportingContext context) {
+  private ClassInfo visitClassInfoRaw(
+      DexClass clazz,
+      ReportingContext context,
+      InterfaceProcessingDesugaringEventConsumer eventConsumer) {
     // We compute both library and class information, but one of them is empty, since a class is
     // a library class or is not, but cannot be both.
     ReportingContext thisContext = context.forClass(clazz);
-    ClassInfo superInfo = visitClassInfo(clazz.superType, thisContext);
+    ClassInfo superInfo = visitClassInfo(clazz.superType, thisContext, eventConsumer);
     SignaturesInfo signatures = visitLibraryClassInfo(clazz.superType);
     // The class may inherit emulated interface info from its program superclass if the latter
     // did not require to resolve the forwarding methods for emualted interfaces.
@@ -962,7 +1079,7 @@
     for (DexType iface : clazz.interfaces.values) {
       signatures = signatures.merge(visitInterfaceInfo(iface, thisContext));
     }
-    return computeClassInfo(clazz, superInfo, signatures);
+    return computeClassInfo(clazz, superInfo, signatures, eventConsumer);
   }
 
   private SignaturesInfo visitLibraryClassInfo(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 0fa2592..6eb7bca 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringEventConsumer.emptyInterfaceMethodDesugaringEventConsumer;
 
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.cf.code.CfInitClass;
@@ -35,11 +36,11 @@
 import com.android.tools.r8.graph.MethodResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ThrowNullCode;
-import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
 import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.ClasspathEmulatedInterfaceSynthesizerEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
 import com.android.tools.r8.synthesis.SyntheticClassBuilder;
 import com.android.tools.r8.synthesis.SyntheticItems.SyntheticKindSelector;
 import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
@@ -62,7 +63,7 @@
   // Use InterfaceDesugaringForTesting for public accesses in tests.
   static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
   static final String DEFAULT_METHOD_PREFIX = "$default$";
-  static final String PRIVATE_METHOD_PREFIX = "$private$";
+  public static final String PRIVATE_METHOD_PREFIX = "$private$";
 
   private final AppView<?> appView;
   private final Predicate<DexType> shouldIgnoreFromReportsPredicate;
@@ -274,7 +275,35 @@
     return descriptor == null ? null : descriptor.getForwardingMethod();
   }
 
-  DexMethod ensureEmulatedInterfaceForwardingMethod(DerivedMethod method) {
+  /**
+   * In the {@link ClassProcessor}, we only conditionally add the synthesized methods to the program
+   * in {@link ClassProcessor#resolveForwardingMethods}. Therefore, we do not report the synthesis
+   * to the given {@param eventConsumer} at this point, as this would lead to over-reporting.
+   */
+  // TODO(b/267144253): Avoid over-synthesizing in the ClassProcessor. Then we should be able to
+  //  always report the synthesized method to the InterfaceProcessingDesugaringEventConsumer at this
+  //  point.
+  DexMethod ensureEmulatedInterfaceForwardingMethod(
+      DerivedMethod method, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
+    return ensureEmulatedInterfaceForwardingMethod(
+        method, emptyInterfaceMethodDesugaringEventConsumer());
+  }
+
+  /**
+   * Forwarding methods synthesized by the {@link ProgramEmulatedInterfaceSynthesizer} are currently
+   * not reported to the {@link L8ProgramEmulatedInterfaceSynthesizerEventConsumer}, since we
+   * already report the entire synthetic class after all of its methods have been created to the
+   * event consumer in {@link
+   * ProgramEmulatedInterfaceSynthesizer#synthesizeProgramEmulatedInterface}.
+   */
+  DexMethod ensureEmulatedInterfaceForwardingMethod(
+      DerivedMethod method, L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
+    return ensureEmulatedInterfaceForwardingMethod(
+        method, emptyInterfaceMethodDesugaringEventConsumer());
+  }
+
+  DexMethod ensureEmulatedInterfaceForwardingMethod(
+      DerivedMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     if (method.getHolderKind(appView) == null) {
       return method.getMethod();
     }
@@ -284,7 +313,7 @@
             .appInfoForDesugaring()
             .resolveMethodLegacy(method.getMethod(), true)
             .getResolutionPair();
-    return ensureDefaultAsMethodOfCompanionClassStub(resolvedMethod).getReference();
+    return ensureDefaultAsMethodOfCompanionClassStub(resolvedMethod, eventConsumer).getReference();
   }
 
   DexClassAndMethod ensureEmulatedInterfaceDispatchMethod(
@@ -331,9 +360,25 @@
                     .setCode(ignore -> ThrowNullCode.get()));
   }
 
-  DexClassAndMethod ensureDefaultAsMethodOfCompanionClassStub(DexClassAndMethod method) {
+  /**
+   * In the {@link ClassProcessor}, we only conditionally add the synthesized methods to the program
+   * in {@link ClassProcessor#resolveForwardingMethods}. Therefore, we do not report the synthesis
+   * to the given {@param eventConsumer} at this point, as this would lead to over-reporting.
+   */
+  // TODO(b/267144253): Avoid over-synthesizing in the ClassProcessor. Then we should be able to
+  //  always report the synthesized method to the InterfaceProcessingDesugaringEventConsumer at this
+  //  point.
+  DexClassAndMethod ensureDefaultAsMethodOfCompanionClassStub(
+      DexClassAndMethod method, InterfaceProcessingDesugaringEventConsumer eventConsumer) {
+    return ensureDefaultAsMethodOfCompanionClassStub(
+        method, emptyInterfaceMethodDesugaringEventConsumer());
+  }
+
+  DexClassAndMethod ensureDefaultAsMethodOfCompanionClassStub(
+      DexClassAndMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     if (method.isProgramMethod()) {
-      return ensureDefaultAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+      return ensureDefaultAsMethodOfProgramCompanionClassStub(
+          method.asProgramMethod(), eventConsumer);
     }
     ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
     DexMethod companionMethodReference =
@@ -342,7 +387,7 @@
   }
 
   DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(
-      DexClassAndMethod method, CfInstructionDesugaringEventConsumer eventConsumer) {
+      DexClassAndMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     if (method.isProgramMethod()) {
       return ensureStaticAsMethodOfProgramCompanionClassStub(
           method.asProgramMethod(), eventConsumer);
@@ -353,7 +398,8 @@
     }
   }
 
-  ProgramMethod ensureDefaultAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+  ProgramMethod ensureDefaultAsMethodOfProgramCompanionClassStub(
+      ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     DexEncodedMethod virtual = method.getDefinition();
     DexMethod companionMethod =
         defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
@@ -378,10 +424,11 @@
                   virtual.getParameterAnnotations().withFakeThisParameter())
               .setCode(ignored -> InvalidCode.getInstance());
         },
-        ignored -> {});
+        companion -> eventConsumer.acceptDefaultAsCompanionMethod(method, companion));
   }
 
-  ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+  ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(
+      ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     DexMethod companionMethod =
         privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
     DexEncodedMethod definition = method.getDefinition();
@@ -405,7 +452,7 @@
               .setParameterAnnotationsList(definition.getParameterAnnotations())
               .setCode(ignored -> InvalidCode.getInstance());
         },
-        ignored -> {});
+        companion -> eventConsumer.acceptPrivateAsCompanionMethod(method, companion));
   }
 
   // Represent a static interface method as a method of companion class.
@@ -490,7 +537,7 @@
               .disableAndroidApiLevelCheck()
               .setCode(ignored -> InvalidCode.getInstance());
         },
-        companion -> eventConsumer.acceptCompanionMethod(method, companion));
+        companion -> eventConsumer.acceptStaticAsCompanionMethod(method, companion));
   }
 
   public ProgramMethod ensureMethodOfProgramCompanionClassStub(
@@ -504,9 +551,9 @@
       return ensureStaticAsMethodOfProgramCompanionClassStub(method, eventConsumer);
     }
     if (definition.isPrivate()) {
-      return ensurePrivateAsMethodOfProgramCompanionClassStub(method);
+      return ensurePrivateAsMethodOfProgramCompanionClassStub(method, eventConsumer);
     }
-    return ensureDefaultAsMethodOfProgramCompanionClassStub(method);
+    return ensureDefaultAsMethodOfProgramCompanionClassStub(method, eventConsumer);
   }
 
   private void ensureCompanionClassInitializesInterface(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
index 4e1a30f..8b02f6c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
@@ -9,5 +9,9 @@
 
   void acceptCompanionClassClinit(ProgramMethod method);
 
-  void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod);
+  void acceptDefaultAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod);
+
+  void acceptPrivateAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod);
+
+  void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
index 9a65214..cea8eab 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
@@ -9,7 +9,46 @@
 public interface InterfaceMethodDesugaringEventConsumer
     extends InterfaceMethodDesugaringBaseEventConsumer {
 
-  void acceptThrowMethod(ProgramMethod method, ProgramMethod context);
-
   void acceptInvokeStaticInterfaceOutliningMethod(ProgramMethod method, ProgramMethod context);
+
+  static EmptyInterfaceMethodDesugaringEventConsumer emptyInterfaceMethodDesugaringEventConsumer() {
+    return EmptyInterfaceMethodDesugaringEventConsumer.INSTANCE;
+  }
+
+  class EmptyInterfaceMethodDesugaringEventConsumer
+      implements InterfaceMethodDesugaringEventConsumer {
+
+    static EmptyInterfaceMethodDesugaringEventConsumer INSTANCE =
+        new EmptyInterfaceMethodDesugaringEventConsumer();
+
+    private EmptyInterfaceMethodDesugaringEventConsumer() {}
+
+    @Override
+    public void acceptCompanionClassClinit(ProgramMethod method) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void acceptDefaultAsCompanionMethod(
+        ProgramMethod method, ProgramMethod companionMethod) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void acceptInvokeStaticInterfaceOutliningMethod(
+        ProgramMethod method, ProgramMethod context) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void acceptPrivateAsCompanionMethod(
+        ProgramMethod method, ProgramMethod companionMethod) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+      // Intentionally empty.
+    }
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index a5ec16a..c92c9ce 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -54,7 +54,7 @@
         Iterables.filter(programClasses, (DexProgramClass clazz) -> shouldProcess(clazz, flavour)),
         clazz -> classProcessor.process(clazz, eventConsumer),
         executorService);
-    classProcessor.finalizeProcessing(eventConsumer);
+    classProcessor.finalizeProcessing(eventConsumer, executorService);
     interfaceProcessor.finalizeProcessing();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 4310355..5de9f4a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -596,13 +596,13 @@
                 // This can be a private instance method call. Note that the referenced
                 // method is expected to be in the current class since it is private, but desugaring
                 // may move some methods or their code into other classes.
-                DexClassAndMethod companionMethodDefinition = null;
+                DexClassAndMethod companionMethodDefinition;
                 DexMethod companionMethod;
                 if (directTarget.getDefinition().isPrivateMethod()) {
                   if (directTarget.isProgramMethod()) {
                     companionMethodDefinition =
                         helper.ensurePrivateAsMethodOfProgramCompanionClassStub(
-                            directTarget.asProgramMethod());
+                            directTarget.asProgramMethod(), eventConsumer);
                     companionMethod = companionMethodDefinition.getReference();
                   } else {
                     // TODO(b/200938617): Why does this not create a stub on the class path?
@@ -610,12 +610,9 @@
                   }
                 } else {
                   companionMethodDefinition =
-                      helper.ensureDefaultAsMethodOfCompanionClassStub(directTarget);
+                      helper.ensureDefaultAsMethodOfCompanionClassStub(directTarget, eventConsumer);
                   companionMethod = companionMethodDefinition.getReference();
                 }
-                if (companionMethodDefinition != null) {
-                  acceptCompanionMethod(directTarget, companionMethodDefinition, eventConsumer);
-                }
                 return getInvokeStaticInstructions(companionMethod);
               })
           .build();
@@ -637,8 +634,8 @@
                     dexItemFactory) -> {
                   // This is a invoke-direct call to a virtual method.
                   DexClassAndMethod companionMethod =
-                      helper.ensureDefaultAsMethodOfCompanionClassStub(virtualTarget);
-                  acceptCompanionMethod(virtualTarget, companionMethod, eventConsumer);
+                      helper.ensureDefaultAsMethodOfCompanionClassStub(
+                          virtualTarget, eventConsumer);
                   return getInvokeStaticInstructions(companionMethod.getReference());
                 })
             .build();
@@ -707,16 +704,6 @@
     }
   }
 
-  private void acceptCompanionMethod(
-      DexClassAndMethod method,
-      DexClassAndMethod companion,
-      InterfaceMethodDesugaringEventConsumer eventConsumer) {
-    assert method.isProgramMethod() == companion.isProgramMethod();
-    if (method.isProgramMethod()) {
-      eventConsumer.acceptCompanionMethod(method.asProgramMethod(), companion.asProgramMethod());
-    }
-  }
-
   private DesugarDescription rewriteInvokeSuper(CfInvoke invoke, ProgramMethod context) {
     DexMethod invokedMethod = invoke.getMethod();
     DexClass clazz = appView.definitionFor(invokedMethod.holder, context);
@@ -766,10 +753,8 @@
                   if (method.isProgramMethod()) {
                     ProgramMethod companionMethodDefinition =
                         helper.ensurePrivateAsMethodOfProgramCompanionClassStub(
-                            method.asProgramMethod());
+                            method.asProgramMethod(), eventConsumer);
                     companionMethod = companionMethodDefinition.getReference();
-                    eventConsumer.acceptCompanionMethod(
-                        method.asProgramMethod(), companionMethodDefinition);
                   } else {
                     companionMethod = helper.privateAsMethodOfCompanionClass(method);
                   }
@@ -793,8 +778,7 @@
                       amendDefaultMethod(context12.getHolder(), invokedMethod);
                   assert method.getReference() == amendedMethod;
                   DexClassAndMethod companionMethod =
-                      helper.ensureDefaultAsMethodOfCompanionClassStub(method);
-                  acceptCompanionMethod(method, companionMethod, eventConsumer);
+                      helper.ensureDefaultAsMethodOfCompanionClassStub(method, eventConsumer);
                   return getInvokeStaticInstructions(companionMethod.getReference());
                 })
             .build();
@@ -847,8 +831,8 @@
                       methodProcessingContext,
                       dexItemFactory) -> {
                     DexClassAndMethod companionTarget =
-                        helper.ensureDefaultAsMethodOfCompanionClassStub(superTarget);
-                    acceptCompanionMethod(superTarget, companionTarget, eventConsumer);
+                        helper.ensureDefaultAsMethodOfCompanionClassStub(
+                            superTarget, eventConsumer);
                     return getInvokeStaticInstructions(companionTarget.getReference());
                   })
               .build();
@@ -873,7 +857,8 @@
                 methodProcessingContext,
                 dexItemFactory) ->
                 getInvokeStaticInstructions(
-                    helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod)))
+                    helper.ensureEmulatedInterfaceForwardingMethod(
+                        forwardingMethod, eventConsumer)))
         .build();
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
index 8115626..dde1382 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.desugar.itf;
 
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -11,11 +12,14 @@
 
 public interface InterfaceProcessingDesugaringEventConsumer {
 
-  void acceptForwardingMethod(ProgramMethod method);
+  void acceptInterfaceMethodDesugaringForwardingMethod(
+      ProgramMethod method, DexClassAndMethod baseMethod);
 
   void acceptEmulatedInterfaceMarkerInterface(
       DexProgramClass clazz, DexClasspathClass newInterface);
 
+  void acceptThrowingMethod(ProgramMethod method, DexType errorType);
+
   void warnMissingInterface(
       DexProgramClass context, DexType missing, InterfaceDesugaringSyntheticHelper helper);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index adf23fa..a7562ee 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -90,21 +90,22 @@
       return;
     }
     if (desugaringMode == EMULATED_INTERFACE_ONLY) {
-      processEmulatedInterfaceOnly(method);
+      processEmulatedInterfaceOnly(method, eventConsumer);
       return;
     }
     if (method.getDefinition().belongsToDirectPool()) {
       processDirectInterfaceMethod(method, eventConsumer);
     } else {
       assert method.getDefinition().belongsToVirtualPool();
-      processVirtualInterfaceMethod(method);
+      processVirtualInterfaceMethod(method, eventConsumer);
       if (!interfaceMethodRemovalChangesApi(method)) {
         getPostProcessingInterfaceInfo(method.getHolder()).setHasBridgesToRemove();
       }
     }
   }
 
-  private void processEmulatedInterfaceOnly(ProgramMethod method) {
+  private void processEmulatedInterfaceOnly(
+      ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     if (!appView.options().isDesugaredLibraryCompilation()) {
       return;
     }
@@ -115,7 +116,7 @@
       EmulatedDispatchMethodDescriptor emulatedDispatchDescriptor =
           helper.getEmulatedDispatchDescriptor(method.getHolder(), method);
       if (emulatedDispatchDescriptor != null) {
-        processVirtualInterfaceMethod(method);
+        processVirtualInterfaceMethod(method, eventConsumer);
         if (!interfaceMethodRemovalChangesApi(method)) {
           getPostProcessingInterfaceInfo(method.getHolder()).setHasBridgesToRemove();
         }
@@ -150,10 +151,12 @@
             newMethodCallback);
   }
 
-  private void processVirtualInterfaceMethod(ProgramMethod method) {
+  private void processVirtualInterfaceMethod(
+      ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) {
     if (helper.isCompatibleDefaultMethod(method.getDefinition())) {
       // Create a new method in a companion class to represent default method implementation.
-      ProgramMethod companion = helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
+      ProgramMethod companion =
+          helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method, eventConsumer);
       finalizeMoveToCompanionMethod(method, companion);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 6a57dae..e941dd6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -40,11 +40,11 @@
     helper = new InterfaceDesugaringSyntheticHelper(appView);
   }
 
-  DexProgramClass synthesizeProgramEmulatedInterface(
+  private void synthesizeProgramEmulatedInterface(
       DexProgramClass emulatedInterface,
       EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
       L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
-    return appView
+    appView
         .getSyntheticItems()
         .ensureFixedClass(
             kinds -> kinds.EMULATED_INTERFACE_CLASS,
@@ -52,14 +52,15 @@
             appView,
             builder ->
                 synthesizeEmulateInterfaceMethods(
-                    emulatedInterface, emulatedInterfaceDescriptor, builder),
+                    emulatedInterface, emulatedInterfaceDescriptor, builder, eventConsumer),
             eventConsumer::acceptProgramEmulatedInterface);
   }
 
   private void synthesizeEmulateInterfaceMethods(
       DexProgramClass emulatedInterface,
       EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
-      SyntheticProgramClassBuilder builder) {
+      SyntheticProgramClassBuilder builder,
+      L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
     emulatedInterface.forEachProgramVirtualMethodMatching(
         m -> emulatedInterfaceDescriptor.getEmulatedMethods().containsKey(m.getReference()),
         method ->
@@ -69,22 +70,26 @@
                         method,
                         emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference()),
                         builder.getType(),
-                        methodBuilder)));
+                        methodBuilder,
+                        eventConsumer)));
   }
 
   private void synthesizeEmulatedInterfaceMethod(
       ProgramMethod method,
       EmulatedDispatchMethodDescriptor descriptor,
       DexType dispatchType,
-      SyntheticMethodBuilder methodBuilder) {
+      SyntheticMethodBuilder methodBuilder,
+      L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
     assert !method.getDefinition().isStatic();
     DexMethod emulatedMethod =
         helper.emulatedInterfaceDispatchMethod(
             descriptor.getEmulatedDispatchMethod(), dispatchType);
     DexMethod itfMethod = helper.emulatedInterfaceInterfaceMethod(descriptor.getInterfaceMethod());
     DexMethod companionMethod =
-        helper.ensureEmulatedInterfaceForwardingMethod(descriptor.getForwardingMethod());
-    LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
+        helper.ensureEmulatedInterfaceForwardingMethod(
+            descriptor.getForwardingMethod(), eventConsumer);
+    LinkedHashMap<DexType, DexMethod> extraDispatchCases =
+        resolveDispatchCases(descriptor, eventConsumer);
     methodBuilder
         .setName(emulatedMethod.getName())
         .setProto(emulatedMethod.getProto())
@@ -101,14 +106,16 @@
   }
 
   private LinkedHashMap<DexType, DexMethod> resolveDispatchCases(
-      EmulatedDispatchMethodDescriptor descriptor) {
+      EmulatedDispatchMethodDescriptor descriptor,
+      L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
     LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
     descriptor
         .getDispatchCases()
         .forEach(
             (type, derivedMethod) ->
                 extraDispatchCases.put(
-                    type, helper.ensureEmulatedInterfaceForwardingMethod(derivedMethod)));
+                    type,
+                    helper.ensureEmulatedInterfaceForwardingMethod(derivedMethod, eventConsumer)));
     return extraDispatchCases;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
index bde95b8..34324ff 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -94,17 +95,29 @@
         new NestBasedAccessDesugaringEventConsumer() {
 
           @Override
-          public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
+          public void acceptNestConstructorBridge(
+              ProgramMethod target,
+              ProgramMethod bridge,
+              DexProgramClass argumentClass,
+              DexClassAndMethod context) {
             methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
           }
 
           @Override
-          public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
+          public void acceptNestFieldGetBridge(
+              ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
             methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
           }
 
           @Override
-          public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
+          public void acceptNestFieldPutBridge(
+              ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+            methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
+          }
+
+          @Override
+          public void acceptNestMethodBridge(
+              ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
             methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
           }
         };
@@ -132,68 +145,151 @@
       this.eventConsumer = eventConsumer;
     }
 
-    private void registerFieldAccess(DexField reference, boolean isGet) {
+    private void registerFieldAccessFromClasspath(DexField reference, boolean isGet) {
       DexClassAndField field =
           reference.lookupMemberOnClass(appView.definitionForHolder(reference));
       if (field != null && needsDesugaring(field, getContext())) {
-        ensureFieldAccessBridge(field, isGet, eventConsumer);
+        ensureFieldAccessBridgeFromClasspathAccess(field, isGet, eventConsumer);
       }
     }
 
-    private void registerInvoke(DexMethod reference) {
+    private void ensureFieldAccessBridgeFromClasspathAccess(
+        DexClassAndField field,
+        boolean isGet,
+        NestBasedAccessDesugaringEventConsumer eventConsumer) {
+      if (field.isProgramField()) {
+        ensureFieldAccessBridgeFromClasspathAccess(field.asProgramField(), isGet, eventConsumer);
+      } else if (field.isClasspathField()) {
+        // Intentionally empty.
+      } else {
+        assert field.isLibraryField();
+        throw reportIncompleteNest(field.asLibraryField());
+      }
+    }
+
+    private void ensureFieldAccessBridgeFromClasspathAccess(
+        ProgramField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+      DexMethod bridgeReference = getFieldAccessBridgeReference(field, isGet);
+      synchronized (field.getHolder().getMethodCollection()) {
+        if (field.getHolder().lookupMethod(bridgeReference) == null) {
+          ProgramMethod bridge =
+              AccessBridgeFactory.createFieldAccessorBridge(bridgeReference, field, isGet);
+          bridge.getHolder().addDirectMethod(bridge.getDefinition());
+          if (isGet) {
+            eventConsumer.acceptNestFieldGetBridge(field, bridge, getContext());
+          } else {
+            eventConsumer.acceptNestFieldPutBridge(field, bridge, getContext());
+          }
+        }
+      }
+    }
+
+    private void registerInvokeFromClasspath(DexMethod reference) {
       if (!reference.getHolderType().isClassType()) {
         return;
       }
       DexClassAndMethod method =
           reference.lookupMemberOnClass(appView.definitionForHolder(reference));
       if (method != null && needsDesugaring(method, getContext())) {
-        ensureMethodBridge(method, eventConsumer);
+        ensureConstructorOrMethodBridgeFromClasspathAccess(method, eventConsumer);
+      }
+    }
+
+    // This is only used for generating bridge methods for class path references.
+    private void ensureConstructorOrMethodBridgeFromClasspathAccess(
+        DexClassAndMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+      if (method.isProgramMethod()) {
+        if (method.getDefinition().isInstanceInitializer()) {
+          ensureConstructorBridgeFromClasspathAccess(method.asProgramMethod(), eventConsumer);
+        } else {
+          ensureMethodBridgeFromClasspathAccess(method.asProgramMethod(), eventConsumer);
+        }
+      } else if (method.isClasspathMethod()) {
+        if (method.getDefinition().isInstanceInitializer()) {
+          ensureConstructorArgumentClass(method);
+        }
+      } else {
+        assert method.isLibraryMethod();
+        throw reportIncompleteNest(method.asLibraryMethod());
+      }
+    }
+
+    private void ensureConstructorBridgeFromClasspathAccess(
+        ProgramMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+      assert method.getDefinition().isInstanceInitializer();
+      DexProgramClass constructorArgumentClass =
+          ensureConstructorArgumentClass(method).asProgramClass();
+      DexMethod bridgeReference = getConstructorBridgeReference(method, constructorArgumentClass);
+      synchronized (method.getHolder().getMethodCollection()) {
+        if (method.getHolder().lookupMethod(bridgeReference) == null) {
+          ProgramMethod bridge =
+              AccessBridgeFactory.createInitializerAccessorBridge(
+                  bridgeReference, method, dexItemFactory);
+          bridge.getHolder().addDirectMethod(bridge.getDefinition());
+          eventConsumer.acceptNestConstructorBridge(
+              method, bridge, constructorArgumentClass, getContext());
+        }
+      }
+    }
+
+    private void ensureMethodBridgeFromClasspathAccess(
+        ProgramMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
+      assert !method.getDefinition().isInstanceInitializer();
+      DexMethod bridgeReference = getMethodBridgeReference(method);
+      synchronized (method.getHolder().getMethodCollection()) {
+        if (method.getHolder().lookupMethod(bridgeReference) == null) {
+          ProgramMethod bridge =
+              AccessBridgeFactory.createMethodAccessorBridge(
+                  bridgeReference, method, dexItemFactory);
+          bridge.getHolder().addDirectMethod(bridge.getDefinition());
+          eventConsumer.acceptNestMethodBridge(method, bridge, getContext());
+        }
       }
     }
 
     @Override
     public void registerInvokeDirect(DexMethod method) {
-      registerInvoke(method);
+      registerInvokeFromClasspath(method);
     }
 
     @Override
     public void registerInvokeInterface(DexMethod method) {
-      registerInvoke(method);
+      registerInvokeFromClasspath(method);
     }
 
     @Override
     public void registerInvokeStatic(DexMethod method) {
-      registerInvoke(method);
+      registerInvokeFromClasspath(method);
     }
 
     @Override
     public void registerInvokeSuper(DexMethod method) {
-      registerInvoke(method);
+      registerInvokeFromClasspath(method);
     }
 
     @Override
     public void registerInvokeVirtual(DexMethod method) {
-      registerInvoke(method);
+      registerInvokeFromClasspath(method);
     }
 
     @Override
     public void registerInstanceFieldWrite(DexField field) {
-      registerFieldAccess(field, false);
+      registerFieldAccessFromClasspath(field, false);
     }
 
     @Override
     public void registerInstanceFieldRead(DexField field) {
-      registerFieldAccess(field, true);
+      registerFieldAccessFromClasspath(field, true);
     }
 
     @Override
     public void registerStaticFieldRead(DexField field) {
-      registerFieldAccess(field, true);
+      registerFieldAccessFromClasspath(field, true);
     }
 
     @Override
     public void registerStaticFieldWrite(DexField field) {
-      registerFieldAccess(field, false);
+      registerFieldAccessFromClasspath(field, false);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index ad59051..c9c2c54 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.graph.DexClassAndField;
 import com.android.tools.r8.graph.DexClassAndMember;
 import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMember;
@@ -48,6 +47,7 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
+import java.util.function.Function;
 import org.objectweb.asm.Opcodes;
 
 // NestBasedAccessDesugaring contains common code between the two subclasses
@@ -66,8 +66,8 @@
       NEST_ACCESS_NAME_PREFIX + "sfput";
 
   protected final AppView<?> appView;
-  private final DexItemFactory dexItemFactory;
-  private final Map<DexType, DexType> syntheticNestConstructorTypes = new ConcurrentHashMap<>();
+  protected final DexItemFactory dexItemFactory;
+  private final Map<DexType, DexClass> syntheticNestConstructorTypes = new ConcurrentHashMap<>();
 
   NestBasedAccessDesugaring(AppView<?> appView) {
     this.appView = appView;
@@ -140,7 +140,10 @@
   }
 
   @Override
-  public void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     method
         .getDefinition()
         .getCode()
@@ -155,12 +158,14 @@
                       field,
                       instruction.asFieldInstruction().isFieldGet(),
                       method,
+                      eventConsumer,
                       programAdditions);
                 }
               } else if (instruction.isInvoke()) {
                 DexMethod invokedMethod = instruction.asInvoke().getMethod();
                 if (needsDesugaring(invokedMethod, method)) {
-                  prepareDesugarMethodInstruction(invokedMethod, method, programAdditions);
+                  prepareDesugarMethodInstruction(
+                      invokedMethod, method, eventConsumer, programAdditions);
                 }
               } else if (instruction.isInvokeDynamic()) {
                 // Starting from Java 17, lambda can use nest based access. We need to generate
@@ -173,7 +178,8 @@
                   DexMember<?, ?> member = lambdaDescriptor.implHandle.member;
                   if (needsDesugaring(member, method)) {
                     assert member.isDexMethod();
-                    prepareDesugarMethodInstruction(member.asDexMethod(), method, programAdditions);
+                    prepareDesugarMethodInstruction(
+                        member.asDexMethod(), method, eventConsumer, programAdditions);
                   }
                 }
               }
@@ -181,43 +187,63 @@
   }
 
   private void prepareDesugarFieldInstruction(
-      DexField field, boolean isGet, ProgramMethod context, ProgramAdditions programAdditions) {
+      DexField field,
+      boolean isGet,
+      ProgramMethod context,
+      NestBasedAccessDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     BridgeAndTarget<DexClassAndField> bridgeAndTarget =
         bridgeAndTargetForDesugaring(field, isGet, context);
     if (bridgeAndTarget == null || !bridgeAndTarget.shouldAddBridge()) {
       return;
     }
 
-    programAdditions.accept(
-        bridgeAndTarget.getBridge(),
-        () ->
-            AccessBridgeFactory.createFieldAccessorBridge(
-                bridgeAndTarget.getBridge(), bridgeAndTarget.getTarget().asProgramField(), isGet));
+    ProgramField targetField = bridgeAndTarget.getTarget().asProgramField();
+    ProgramMethod bridgeMethod =
+        programAdditions.ensureMethod(
+            bridgeAndTarget.getBridge(),
+            () ->
+                AccessBridgeFactory.createFieldAccessorBridge(
+                    bridgeAndTarget.getBridge(), targetField, isGet));
+    if (isGet) {
+      eventConsumer.acceptNestFieldGetBridge(targetField, bridgeMethod, context);
+    } else {
+      eventConsumer.acceptNestFieldPutBridge(targetField, bridgeMethod, context);
+    }
   }
 
   private void prepareDesugarMethodInstruction(
-      DexMethod method, ProgramMethod context, ProgramAdditions programAdditions) {
+      DexMethod method,
+      ProgramMethod context,
+      NestBasedAccessDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     BridgeAndTarget<DexClassAndMethod> bridgeAndTarget =
-        bridgeAndTargetForDesugaring(method, context);
+        bridgeAndTargetForDesugaring(method, context, this::ensureConstructorArgumentClass);
     if (bridgeAndTarget == null || !bridgeAndTarget.shouldAddBridge()) {
       return;
     }
-    programAdditions.accept(
-        bridgeAndTarget.getBridge(),
-        () ->
-            bridgeAndTarget.getTarget().getDefinition().isInstanceInitializer()
-                ? AccessBridgeFactory.createInitializerAccessorBridge(
-                    bridgeAndTarget.getBridge(),
-                    bridgeAndTarget.getTarget().asProgramMethod(),
-                    dexItemFactory)
-                : AccessBridgeFactory.createMethodAccessorBridge(
-                    bridgeAndTarget.getBridge(),
-                    bridgeAndTarget.getTarget().asProgramMethod(),
-                    dexItemFactory));
+    ProgramMethod targetMethod = bridgeAndTarget.getTarget().asProgramMethod();
+    ProgramMethod bridgeMethod =
+        programAdditions.ensureMethod(
+            bridgeAndTarget.getBridge(),
+            () ->
+                targetMethod.getDefinition().isInstanceInitializer()
+                    ? AccessBridgeFactory.createInitializerAccessorBridge(
+                        bridgeAndTarget.getBridge(), targetMethod, dexItemFactory)
+                    : AccessBridgeFactory.createMethodAccessorBridge(
+                        bridgeAndTarget.getBridge(), targetMethod, dexItemFactory));
+    if (targetMethod.getDefinition().isInstanceInitializer()) {
+      DexProgramClass argumentClass = getConstructorArgumentClass(targetMethod).asProgramClass();
+      eventConsumer.acceptNestConstructorBridge(targetMethod, bridgeMethod, argumentClass, context);
+    } else {
+      eventConsumer.acceptNestMethodBridge(targetMethod, bridgeMethod, context);
+    }
   }
 
   private BridgeAndTarget<DexClassAndMethod> bridgeAndTargetForDesugaring(
-      DexMethod method, ProgramMethod context) {
+      DexMethod method,
+      ProgramMethod context,
+      Function<DexClassAndMethod, DexClass> constructorArgumentClassProvider) {
     if (!method.getHolderType().isClassType()) {
       return null;
     }
@@ -229,7 +255,14 @@
     if (target == null || !needsDesugaring(target, context)) {
       return null;
     }
-    return new BridgeAndTarget<>(getMethodBridgeReference(target), target);
+    DexMethod bridgeReference;
+    if (target.getDefinition().isInstanceInitializer()) {
+      DexClass constructorArgumentClass = constructorArgumentClassProvider.apply(target);
+      bridgeReference = getConstructorBridgeReference(target, constructorArgumentClass);
+    } else {
+      bridgeReference = getMethodBridgeReference(target);
+    }
+    return new BridgeAndTarget<>(bridgeReference, target);
   }
 
   private BridgeAndTarget<DexClassAndField> bridgeAndTargetForDesugaring(
@@ -301,20 +334,16 @@
       CfInstructionDesugaringCollection desugaringCollection,
       DexItemFactory dexItemFactory) {
     if (instruction.isFieldInstruction()) {
-      return desugarFieldInstruction(instruction.asFieldInstruction(), context, eventConsumer);
+      return desugarFieldInstruction(instruction.asFieldInstruction(), context);
     }
     if (instruction.isInvoke()) {
-      return desugarInvokeInstruction(
-          instruction.asInvoke(), localStackAllocator, context, eventConsumer);
+      return desugarInvokeInstruction(instruction.asInvoke(), localStackAllocator, context);
     }
     return null;
   }
 
   private List<CfInstruction> desugarFieldInstruction(
-      CfFieldInstruction instruction,
-      ProgramMethod context,
-      NestBasedAccessDesugaringEventConsumer eventConsumer) {
-
+      CfFieldInstruction instruction, ProgramMethod context) {
     BridgeAndTarget<DexClassAndField> bridgeAndTarget =
         bridgeAndTargetForDesugaring(instruction.getField(), instruction.isFieldGet(), context);
     if (bridgeAndTarget == null) {
@@ -332,14 +361,10 @@
   }
 
   private List<CfInstruction> desugarInvokeInstruction(
-      CfInvoke invoke,
-      LocalStackAllocator localStackAllocator,
-      ProgramMethod context,
-      NestBasedAccessDesugaringEventConsumer eventConsumer) {
+      CfInvoke invoke, LocalStackAllocator localStackAllocator, ProgramMethod context) {
     DexMethod invokedMethod = invoke.getMethod();
-
     BridgeAndTarget<DexClassAndMethod> bridgeAndTarget =
-        bridgeAndTargetForDesugaring(invokedMethod, context);
+        bridgeAndTargetForDesugaring(invokedMethod, context, this::getConstructorArgumentClass);
     if (bridgeAndTarget == null) {
       return null;
     }
@@ -360,45 +385,13 @@
         new CfInvoke(Opcodes.INVOKESTATIC, bridgeAndTarget.getBridge(), invoke.isInterface()));
   }
 
-  private RuntimeException reportIncompleteNest(LibraryMember<?, ?> member) {
+  RuntimeException reportIncompleteNest(LibraryMember<?, ?> member) {
     Nest nest = Nest.create(appView, member.getHolder());
     assert nest != null : "Should be a compilation error if missing nest host on library class.";
     throw appView.options().errorMissingNestMember(nest);
   }
 
-  DexMethod ensureFieldAccessBridge(
-      DexClassAndField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
-    if (field.isProgramField()) {
-      return ensureFieldAccessBridge(field.asProgramField(), isGet, eventConsumer);
-    }
-    if (field.isClasspathField()) {
-      return getFieldAccessBridgeReference(field, isGet);
-    }
-    assert field.isLibraryField();
-    throw reportIncompleteNest(field.asLibraryField());
-  }
-
-  private DexMethod ensureFieldAccessBridge(
-      ProgramField field, boolean isGet, NestBasedAccessDesugaringEventConsumer eventConsumer) {
-    DexMethod bridgeReference = getFieldAccessBridgeReference(field, isGet);
-    synchronized (field.getHolder().getMethodCollection()) {
-      ProgramMethod bridge = field.getHolder().lookupProgramMethod(bridgeReference);
-      if (bridge == null) {
-        bridge = AccessBridgeFactory.createFieldAccessorBridge(bridgeReference, field, isGet);
-        bridge.getHolder().addDirectMethod(bridge.getDefinition());
-        if (eventConsumer != null) {
-          if (isGet) {
-            eventConsumer.acceptNestFieldGetBridge(field, bridge);
-          } else {
-            eventConsumer.acceptNestFieldPutBridge(field, bridge);
-          }
-        }
-      }
-      return bridge.getReference();
-    }
-  }
-
-  private DexMethod getFieldAccessBridgeReference(DexClassAndField field, boolean isGet) {
+  DexMethod getFieldAccessBridgeReference(DexClassAndField field, boolean isGet) {
     int bridgeParameterCount =
         BooleanUtils.intValue(!field.getAccessFlags().isStatic()) + BooleanUtils.intValue(!isGet);
     DexType[] parameters = new DexType[bridgeParameterCount];
@@ -428,72 +421,47 @@
     return dexItemFactory.createString(prefix + field.getName().toString());
   }
 
-  // This is only used for generating bridge methods for class path references.
-  DexMethod ensureMethodBridge(
-      DexClassAndMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
-    if (method.isProgramMethod()) {
-      return ensureMethodBridge(method.asProgramMethod(), eventConsumer);
-    }
-    if (method.isClasspathMethod()) {
-      return getMethodBridgeReference(method);
-    }
-    assert method.isLibraryMethod();
-    throw reportIncompleteNest(method.asLibraryMethod());
+  private DexClass getConstructorArgumentClass(DexClassAndMethod constructor) {
+    return syntheticNestConstructorTypes.get(constructor.getHolderType());
   }
 
-  private DexMethod ensureMethodBridge(
-      ProgramMethod method, NestBasedAccessDesugaringEventConsumer eventConsumer) {
-    DexMethod bridgeReference = getMethodBridgeReference(method);
-    synchronized (method.getHolder().getMethodCollection()) {
-      ProgramMethod bridge = method.getHolder().lookupProgramMethod(bridgeReference);
-      if (bridge == null) {
-        DexEncodedMethod definition = method.getDefinition();
-        bridge =
-            definition.isInstanceInitializer()
-                ? AccessBridgeFactory.createInitializerAccessorBridge(
-                    bridgeReference, method, dexItemFactory)
-                : AccessBridgeFactory.createMethodAccessorBridge(
-                    bridgeReference, method, dexItemFactory);
-        bridge.getHolder().addDirectMethod(bridge.getDefinition());
-        if (eventConsumer != null) {
-          eventConsumer.acceptNestMethodBridge(method, bridge);
-        }
-      }
-    }
-    return bridgeReference;
+  DexClass ensureConstructorArgumentClass(DexClassAndMethod constructor) {
+    assert constructor.getDefinition().isInstanceInitializer();
+    return syntheticNestConstructorTypes.computeIfAbsent(
+        constructor.getHolderType(),
+        holder -> {
+          if (constructor.isProgramMethod()) {
+            return appView
+                .getSyntheticItems()
+                .createFixedClass(
+                    kinds -> kinds.INIT_TYPE_ARGUMENT,
+                    constructor.asProgramMethod().getHolder(),
+                    appView,
+                    builder -> {});
+          } else {
+            assert constructor.isClasspathMethod();
+            return appView
+                .getSyntheticItems()
+                .ensureFixedClasspathClass(
+                    kinds -> kinds.INIT_TYPE_ARGUMENT,
+                    constructor.asClasspathMethod().getHolder(),
+                    appView,
+                    ignored -> {},
+                    ignored -> {});
+          }
+        });
   }
 
-  private DexMethod getMethodBridgeReference(DexClassAndMethod method) {
-    if (method.getDefinition().isInstanceInitializer()) {
-      DexType nestConstructorType =
-          syntheticNestConstructorTypes.computeIfAbsent(
-              method.getHolderType(),
-              holder -> {
-                if (method.isProgramMethod()) {
-                  return appView
-                      .getSyntheticItems()
-                      .createFixedClass(
-                          kinds -> kinds.INIT_TYPE_ARGUMENT,
-                          method.asProgramMethod().getHolder(),
-                          appView,
-                          builder -> {})
-                      .getType();
-                } else {
-                  assert method.isClasspathMethod();
-                  return appView
-                      .getSyntheticItems()
-                      .ensureFixedClasspathClass(
-                          kinds -> kinds.INIT_TYPE_ARGUMENT,
-                          method.asClasspathMethod().getHolder(),
-                          appView,
-                          ignored -> {},
-                          ignored -> {})
-                      .getType();
-                }
-              });
-      DexProto newProto = dexItemFactory.appendTypeToProto(method.getProto(), nestConstructorType);
-      return method.getReference().withProto(newProto, dexItemFactory);
-    }
+  DexMethod getConstructorBridgeReference(
+      DexClassAndMethod method, DexClass constructorArgumentClass) {
+    assert method.getDefinition().isInstanceInitializer();
+    DexProto newProto =
+        dexItemFactory.appendTypeToProto(method.getProto(), constructorArgumentClass.getType());
+    return method.getReference().withProto(newProto, dexItemFactory);
+  }
+
+  DexMethod getMethodBridgeReference(DexClassAndMethod method) {
+    assert !method.getDefinition().isInstanceInitializer();
     DexProto proto =
         method.getAccessFlags().isStatic()
             ? method.getProto()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
index b5afce1..dac3668 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaringEventConsumer.java
@@ -4,14 +4,25 @@
 
 package com.android.tools.r8.ir.desugar.nest;
 
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 
 public interface NestBasedAccessDesugaringEventConsumer {
 
-  void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge);
+  void acceptNestConstructorBridge(
+      ProgramMethod target,
+      ProgramMethod bridge,
+      DexProgramClass argumentClass,
+      DexClassAndMethod context);
 
-  void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge);
+  void acceptNestFieldGetBridge(
+      ProgramField target, ProgramMethod bridge, DexClassAndMethod context);
 
-  void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge);
+  void acceptNestFieldPutBridge(
+      ProgramField target, ProgramMethod bridge, DexClassAndMethod context);
+
+  void acceptNestMethodBridge(
+      ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context);
 }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index 81736ab..afae01d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -96,7 +96,10 @@
   }
 
   @Override
-  public void prepare(ProgramMethod method, ProgramAdditions programAdditions) {
+  public void prepare(
+      ProgramMethod method,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramAdditions programAdditions) {
     CfCode cfCode = method.getDefinition().getCode().asCfCode();
     for (CfInstruction instruction : cfCode.getInstructions()) {
       if (instruction.isInvokeDynamic() && needsDesugaring(instruction, method)) {
@@ -253,7 +256,7 @@
     DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
     DexMethod method = equalsRecordMethod(clazz.type);
     assert clazz.lookupProgramMethod(method) == null;
-    programAdditions.accept(
+    programAdditions.ensureMethod(
         method, () -> synthesizeEqualsRecordMethod(clazz, getFieldsAsObjects, method));
     return method;
   }
@@ -263,7 +266,7 @@
     DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
     DexMethod method = getFieldsAsObjectsMethod(clazz.type);
     assert clazz.lookupProgramMethod(method) == null;
-    programAdditions.accept(
+    programAdditions.ensureMethod(
         method,
         () -> synthesizeGetFieldsAsObjectsMethod(clazz, recordInvokeDynamic.getFields(), method));
     return method;
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
index 297e443..428f970 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.profile.art.rewriting;
 
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
@@ -32,7 +33,7 @@
     this.artProfile = artProfile;
   }
 
-  void addRulesIfContextIsInProfile(ProgramMethod context, ProgramDefinition... definitions) {
+  void addRulesIfContextIsInProfile(DexClassAndMethod context, ProgramDefinition... definitions) {
     ArtProfileMethodRule contextMethodRule = artProfile.getMethodRule(context.getReference());
     if (contextMethodRule != null) {
       for (ProgramDefinition definition : definitions) {
@@ -42,7 +43,7 @@
   }
 
   // Specialization of the above method to avoid redundant varargs array creation.
-  void addRulesIfContextIsInProfile(ProgramMethod context, ProgramDefinition definition) {
+  void addRulesIfContextIsInProfile(DexClassAndMethod context, ProgramDefinition definition) {
     ArtProfileMethodRule contextMethodRule = artProfile.getMethodRule(context.getReference());
     if (contextMethodRule != null) {
       addRuleFromContext(definition, contextMethodRule, MethodRuleAdditionConfig.getDefault());
@@ -82,9 +83,11 @@
             methodReference -> ArtProfileMethodRule.builder().setMethod(method.getReference()));
 
     // Setup the rule.
-    methodRuleBuilder.acceptMethodRuleInfoBuilder(
-        methodRuleInfoBuilder ->
-            config.configureMethodRuleInfo(methodRuleInfoBuilder, contextMethodRule));
+    synchronized (methodRuleBuilder) {
+      methodRuleBuilder.acceptMethodRuleInfoBuilder(
+          methodRuleInfoBuilder ->
+              config.configureMethodRuleInfo(methodRuleInfoBuilder, contextMethodRule));
+    }
   }
 
   ArtProfile createNewArtProfile() {
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
index 7e44875..3f04e4c 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.profile.art.rewriting;
 
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramField;
@@ -70,11 +71,6 @@
   }
 
   @Override
-  public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
-    parent.acceptCompanionMethod(method, companionMethod);
-  }
-
-  @Override
   public void acceptConstantDynamicClass(ConstantDynamicClass lambdaClass, ProgramMethod context) {
     parent.acceptConstantDynamicClass(lambdaClass, context);
   }
@@ -85,6 +81,13 @@
   }
 
   @Override
+  public void acceptDefaultAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+    additionsCollection.addRulesIfContextIsInProfile(
+        method, companionMethod, companionMethod.getHolder());
+    parent.acceptDefaultAsCompanionMethod(method, companionMethod);
+  }
+
+  @Override
   public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
     parent.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
   }
@@ -118,18 +121,39 @@
   }
 
   @Override
-  public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
-    parent.acceptNestFieldGetBridge(target, bridge);
+  public void acceptNestConstructorBridge(
+      ProgramMethod target,
+      ProgramMethod bridge,
+      DexProgramClass argumentClass,
+      DexClassAndMethod context) {
+    assert context.isProgramMethod();
+    additionsCollection.addRulesIfContextIsInProfile(
+        context.asProgramMethod(), argumentClass, bridge);
+    parent.acceptNestConstructorBridge(target, bridge, argumentClass, context);
   }
 
   @Override
-  public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
-    parent.acceptNestFieldPutBridge(target, bridge);
+  public void acceptNestFieldGetBridge(
+      ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+    assert context.isProgramMethod();
+    additionsCollection.addRulesIfContextIsInProfile(context.asProgramMethod(), bridge);
+    parent.acceptNestFieldGetBridge(target, bridge, context);
   }
 
   @Override
-  public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
-    parent.acceptNestMethodBridge(target, bridge);
+  public void acceptNestFieldPutBridge(
+      ProgramField target, ProgramMethod bridge, DexClassAndMethod context) {
+    assert context.isProgramMethod();
+    additionsCollection.addRulesIfContextIsInProfile(context.asProgramMethod(), bridge);
+    parent.acceptNestFieldPutBridge(target, bridge, context);
+  }
+
+  @Override
+  public void acceptNestMethodBridge(
+      ProgramMethod target, ProgramMethod bridge, DexClassAndMethod context) {
+    assert context.isProgramMethod();
+    additionsCollection.addRulesIfContextIsInProfile(context.asProgramMethod(), bridge);
+    parent.acceptNestMethodBridge(target, bridge, context);
   }
 
   @Override
@@ -140,6 +164,13 @@
   }
 
   @Override
+  public void acceptPrivateAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+    additionsCollection.addRulesIfContextIsInProfile(
+        method, companionMethod, companionMethod.getHolder());
+    parent.acceptPrivateAsCompanionMethod(method, companionMethod);
+  }
+
+  @Override
   public void acceptRecordClass(DexProgramClass recordClass) {
     parent.acceptRecordClass(recordClass);
   }
@@ -150,12 +181,20 @@
   }
 
   @Override
+  public void acceptStaticAsCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) {
+    additionsCollection.addRulesIfContextIsInProfile(
+        method, companionMethod, companionMethod.getHolder());
+    parent.acceptStaticAsCompanionMethod(method, companionMethod);
+  }
+
+  @Override
   public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
     parent.acceptThrowMethod(method, context);
   }
 
   @Override
   public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
+    additionsCollection.addRulesIfContextIsInProfile(context, closeMethod, closeMethod.getHolder());
     parent.acceptTwrCloseResourceMethod(closeMethod, context);
   }
 
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
new file mode 100644
index 0000000..cc234c8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2023, 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.profile.art.rewriting;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
+import java.util.concurrent.ExecutionException;
+
+public class ArtProfileRewritingCfPostProcessingDesugaringEventConsumer
+    extends CfPostProcessingDesugaringEventConsumer {
+
+  private final ConcreteArtProfileCollectionAdditions additionsCollection;
+  private final CfPostProcessingDesugaringEventConsumer parent;
+
+  private ArtProfileRewritingCfPostProcessingDesugaringEventConsumer(
+      ConcreteArtProfileCollectionAdditions additionsCollection,
+      CfPostProcessingDesugaringEventConsumer parent) {
+    this.additionsCollection = additionsCollection;
+    this.parent = parent;
+  }
+
+  public static CfPostProcessingDesugaringEventConsumer attach(
+      ArtProfileCollectionAdditions artProfileCollectionAdditions,
+      CfPostProcessingDesugaringEventConsumer eventConsumer) {
+    if (artProfileCollectionAdditions.isNop()) {
+      return eventConsumer;
+    }
+    return new ArtProfileRewritingCfPostProcessingDesugaringEventConsumer(
+        artProfileCollectionAdditions.asConcrete(), eventConsumer);
+  }
+
+  @Override
+  public void acceptAPIConversionCallback(ProgramMethod method) {
+    parent.acceptAPIConversionCallback(method);
+  }
+
+  @Override
+  public void acceptCollectionConversion(ProgramMethod arrayConversion) {
+    parent.acceptCollectionConversion(arrayConversion);
+  }
+
+  @Override
+  public void acceptCovariantRetargetMethod(ProgramMethod method) {
+    parent.acceptCovariantRetargetMethod(method);
+  }
+
+  @Override
+  public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+    parent.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+  }
+
+  @Override
+  public void acceptDesugaredLibraryRetargeterForwardingMethod(ProgramMethod method) {
+    parent.acceptDesugaredLibraryRetargeterForwardingMethod(method);
+  }
+
+  @Override
+  public void acceptEmulatedInterfaceMarkerInterface(
+      DexProgramClass clazz, DexClasspathClass newInterface) {
+    parent.acceptEmulatedInterfaceMarkerInterface(clazz, newInterface);
+  }
+
+  @Override
+  public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
+    parent.acceptEnumConversionClasspathClass(clazz);
+  }
+
+  @Override
+  public void acceptGenericApiConversionStub(DexClasspathClass dexClasspathClass) {
+    parent.acceptGenericApiConversionStub(dexClasspathClass);
+  }
+
+  @Override
+  public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
+    parent.acceptInterfaceInjection(clazz, newInterface);
+  }
+
+  @Override
+  public void acceptInterfaceMethodDesugaringForwardingMethod(
+      ProgramMethod method, DexClassAndMethod baseMethod) {
+    additionsCollection.addRulesIfContextIsInProfile(baseMethod, method);
+    parent.acceptInterfaceMethodDesugaringForwardingMethod(method, baseMethod);
+  }
+
+  @Override
+  public void acceptThrowingMethod(ProgramMethod method, DexType errorType) {
+    parent.acceptThrowingMethod(method, errorType);
+  }
+
+  @Override
+  public void acceptWrapperClasspathClass(DexClasspathClass clazz) {
+    parent.acceptWrapperClasspathClass(clazz);
+  }
+
+  @Override
+  public void finalizeDesugaring() throws ExecutionException {
+    parent.finalizeDesugaring();
+  }
+
+  @Override
+  public void warnMissingInterface(
+      DexProgramClass context, DexType missing, InterfaceDesugaringSyntheticHelper helper) {
+    parent.warnMissingInterface(context, missing, helper);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
index b66c5f1..1d0c199 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
@@ -5,8 +5,8 @@
 package com.android.tools.r8.profile.art.rewriting;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.profile.art.ArtProfile;
 import com.android.tools.r8.profile.art.ArtProfileCollection;
 import com.android.tools.r8.profile.art.NonEmptyArtProfileCollection;
@@ -25,14 +25,14 @@
     assert !additionsCollection.isEmpty();
   }
 
-  void addRulesIfContextIsInProfile(ProgramMethod context, ProgramDefinition... definitions) {
+  void addRulesIfContextIsInProfile(DexClassAndMethod context, ProgramDefinition... definitions) {
     for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
       artProfileAdditions.addRulesIfContextIsInProfile(context, definitions);
     }
   }
 
   // Specialization of the above method to avoid redundant varargs array creation.
-  void addRulesIfContextIsInProfile(ProgramMethod context, ProgramDefinition definition) {
+  void addRulesIfContextIsInProfile(DexClassAndMethod context, ProgramDefinition definition) {
     for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
       artProfileAdditions.addRulesIfContextIsInProfile(context, definition);
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 11a9fb6..06db3b7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -113,7 +113,6 @@
 import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection;
 import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer.R8PostProcessingDesugaringEventConsumer;
 import com.android.tools.r8.ir.desugar.LambdaClass;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.ir.desugar.ProgramAdditions;
@@ -4108,7 +4107,7 @@
     ProgramAdditions programAdditions = new ProgramAdditions();
     ThreadUtils.processItems(
         pendingCodeDesugaring,
-        method -> desugaring.prepare(method, programAdditions),
+        method -> desugaring.prepare(method, eventConsumer, programAdditions),
         executorService);
     programAdditions.apply(executorService);
 
@@ -4562,9 +4561,10 @@
 
     assert workList.isEmpty();
 
-    R8PostProcessingDesugaringEventConsumer eventConsumer =
+    CfPostProcessingDesugaringEventConsumer eventConsumer =
         CfPostProcessingDesugaringEventConsumer.createForR8(
             syntheticAdditions,
+            artProfileCollectionAdditions,
             desugaring,
             (context, missing) ->
                 missingClassesBuilder.addNewMissingClassWithDesugarDiagnostic(
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 99edc77..955069f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -76,6 +76,10 @@
       this.reporter = reporter;
     }
 
+    public List<FilteredClassPath> getInjars() {
+      return injars;
+    }
+
     public void addParsedConfiguration(String source) {
       parsedConfiguration.add(source);
     }
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 bd09578..8e19b25 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -48,7 +48,9 @@
   }
 
   public void addInlinableFieldMatchingPrecondition(DexField field) {
-    inlinableFieldsInPrecondition.put(field, field);
+    if (inlinableFieldsInPrecondition != null) {
+      inlinableFieldsInPrecondition.put(field, field);
+    }
   }
 
   public Set<DexField> getAndClearInlinableFieldsMatchingPrecondition() {
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 5717ccf..149201d 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1584,13 +1584,27 @@
             interfaceDesugaringSyntheticHelper.ensureMethodOfProgramCompanionClassStub(
                 method,
                 new InterfaceMethodDesugaringBaseEventConsumer() {
+
                   @Override
                   public void acceptCompanionClassClinit(ProgramMethod method) {
                     // No processing of synthesized CC.<clinit>. They will be picked up by tracing.
                   }
 
                   @Override
-                  public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) {
+                  public void acceptDefaultAsCompanionMethod(
+                      ProgramMethod method, ProgramMethod companionMethod) {
+                    // The move will be included in the pending-inverse map below.
+                  }
+
+                  @Override
+                  public void acceptPrivateAsCompanionMethod(
+                      ProgramMethod method, ProgramMethod companion) {
+                    // The move will be included in the pending-inverse map below.
+                  }
+
+                  @Override
+                  public void acceptStaticAsCompanionMethod(
+                      ProgramMethod method, ProgramMethod companion) {
                     // The move will be included in the pending-inverse map below.
                   }
                 });
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index a72a237..9f8c41b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -992,20 +992,25 @@
       return this;
     }
 
+    public ArchiveResourceProvider createAndAddProvider(FilteredClassPath archive) {
+      if (isArchive(archive.getPath())) {
+        ArchiveResourceProvider archiveResourceProvider =
+            new ArchiveResourceProvider(archive, ignoreDexInArchive);
+        addProgramResourceProvider(archiveResourceProvider);
+        return archiveResourceProvider;
+      }
+      reporter.error(
+          new StringDiagnostic(
+              "Unexpected input type. Only archive types are supported, e.g., .jar, .zip, etc.",
+              archive.getOrigin(),
+              archive.getPosition()));
+      return null;
+    }
+
     /** Add filtered archives of program resources. */
     public Builder addFilteredProgramArchives(Collection<FilteredClassPath> filteredArchives) {
       for (FilteredClassPath archive : filteredArchives) {
-        if (isArchive(archive.getPath())) {
-          ArchiveResourceProvider archiveResourceProvider =
-              new ArchiveResourceProvider(archive, ignoreDexInArchive);
-          addProgramResourceProvider(archiveResourceProvider);
-        } else {
-          reporter.error(
-              new StringDiagnostic(
-                  "Unexpected input type. Only archive types are supported, e.g., .jar, .zip, etc.",
-                  archive.getOrigin(),
-                  archive.getPosition()));
-        }
+        createAndAddProvider(archive);
       }
       return this;
     }
diff --git a/src/main/keep.txt b/src/main/keep.txt
index 2213ce9..89105b4 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -51,3 +51,7 @@
 }
 # Test in this class is using the class name to fing the original .java file.
 -keep class com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringMethods
+
+# Keep everything in the annotations package of keepanno.
+# These are public API as they denote the supported annotations to be interpreted by R8.
+-keep class com.android.tools.r8.keepanno.annotations.** { *; }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 34f8a0b..c4cb889 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
+import com.android.tools.r8.keepanno.KeepEdgeAnnotationsTest;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.profile.art.ArtProfileConsumer;
 import com.android.tools.r8.profile.art.ArtProfileProvider;
@@ -713,6 +714,13 @@
     return self();
   }
 
+  public T enableExperimentalKeepAnnotations() throws IOException {
+    builder.addClasspathResourceProvider(
+        DirectoryClassFileProvider.fromDirectory(KeepEdgeAnnotationsTest.KEEP_ANNO_PATH));
+    builder.setEnableExperimentalKeepAnnotations(true);
+    return self();
+  }
+
   public T enableProguardTestOptions() {
     builder.setEnableTestProguardOptions();
     return self();
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index fae6781..156f775 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -28,22 +28,24 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class TwrCloseResourceDuplicationTest extends TestBase {
 
   private static final String PKG = "twrcloseresourceduplication";
   private static final String EXAMPLE = "examplesJava9/" + PKG;
-  private final JavaExampleClassProxy MAIN =
+  protected static final JavaExampleClassProxy MAIN =
       new JavaExampleClassProxy(EXAMPLE, PKG + "/TwrCloseResourceDuplication");
-  private final JavaExampleClassProxy FOO =
+  protected static final JavaExampleClassProxy FOO =
       new JavaExampleClassProxy(EXAMPLE, PKG + "/TwrCloseResourceDuplication$Foo");
-  private final JavaExampleClassProxy BAR =
+  protected static final JavaExampleClassProxy BAR =
       new JavaExampleClassProxy(EXAMPLE, PKG + "/TwrCloseResourceDuplication$Bar");
 
   static final int INPUT_CLASSES = 3;
 
-  static final String EXPECTED =
+  protected static final String EXPECTED =
       StringUtils.lines(
           "foo opened 1",
           "foo post close 1",
@@ -56,9 +58,10 @@
           "bar caught from 2: RuntimeException",
           "bar post close 2");
 
-  private final TestParameters parameters;
+  @Parameter(0)
+  public TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{0}")
+  @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters()
         .withCfRuntimesStartingFromIncluding(CfVm.JDK9)
@@ -67,11 +70,7 @@
         .build();
   }
 
-  public TwrCloseResourceDuplicationTest(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
-  private String getZipFile() throws IOException {
+  protected String getZipFile() throws IOException {
     return ZipUtils.ZipBuilder.builder(temp.newFile("file.zip").toPath())
         // DEX VMs from 4.4 up-to 9.0 including, will fail if no entry is added.
         .addBytes("entry", new byte[1])
@@ -79,7 +78,7 @@
         .toString();
   }
 
-  private List<Path> getProgramInputs() throws Exception {
+  protected static List<Path> getProgramInputs() {
     return ImmutableList.of(JavaExampleClassProxy.examplesJar(EXAMPLE));
   }
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
index 5f2237d..3c09bff 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnotationViaSuperTest.java
@@ -22,7 +22,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -54,11 +53,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
-    System.out.println(rules);
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutKeepAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .addKeepRuntimeVisibleAnnotations()
         .setMinApi(parameters.getApiLevel())
@@ -78,19 +75,6 @@
         UnusedAnno.class);
   }
 
-  public List<byte[]> getInputClassesWithoutKeepAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(Base.class), isPresent());
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java
index c83340f..cd00198 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepBindingTest.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,10 +49,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepClassRules(A.class, B.class)
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
@@ -64,10 +62,9 @@
 
   @Test
   public void testWithRuleExtractionAndNoKeepOnClass() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
@@ -79,19 +76,6 @@
     return ImmutableList.of(TestClass.class, A.class, B.class, C.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector, boolean expectB) {
     assertThat(inspector.clazz(A.class), isPresent());
     assertThat(inspector.clazz(A.class).uniqueMethodWithOriginalName("foo"), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
index f99f3bf..f4411a6 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepEdgeAnnotationsTest.java
@@ -18,8 +18,8 @@
 import com.android.tools.r8.keepanno.annotations.KeepConstants.Edge;
 import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
 import com.android.tools.r8.keepanno.asm.KeepEdgeWriter;
+import com.android.tools.r8.keepanno.asm.KeepEdgeWriter.AnnotationVisitorInterface;
 import com.android.tools.r8.keepanno.ast.KeepEdge;
-import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
 import com.android.tools.r8.keepanno.processor.KeepEdgeProcessor;
 import com.android.tools.r8.keepanno.testsource.KeepClassAndDefaultConstructorSource;
 import com.android.tools.r8.keepanno.testsource.KeepDependentFieldSource;
@@ -42,10 +42,13 @@
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.objectweb.asm.AnnotationVisitor;
 
+@Ignore("b/248408342: These test break on r8lib builds because of src&test using ASM classes.")
 @RunWith(Parameterized.class)
 public class KeepEdgeAnnotationsTest extends TestBase {
 
@@ -64,7 +67,7 @@
     }
   }
 
-  private static final Path KEEP_ANNO_PATH =
+  public static final Path KEEP_ANNO_PATH =
       Paths.get(ToolHelper.BUILD_DIR, "classes", "java", "keepanno");
 
   private static List<Class<?>> getTestClasses() {
@@ -150,6 +153,46 @@
     return transformed;
   }
 
+  /** Wrapper to bridge ASM visitors when using the r8lib compiled version of the keepanno lib. */
+  private AnnotationVisitorInterface wrap(AnnotationVisitor visitor) {
+    if (visitor == null) {
+      return null;
+    }
+    return new AnnotationVisitorInterface() {
+      @Override
+      public int version() {
+        return KeepEdgeReader.ASM_VERSION;
+      }
+
+      @Override
+      public void visit(String name, Object value) {
+        visitor.visit(name, value);
+      }
+
+      @Override
+      public void visitEnum(String name, String descriptor, String value) {
+        visitor.visitEnum(name, descriptor, value);
+      }
+
+      @Override
+      public AnnotationVisitorInterface visitAnnotation(String name, String descriptor) {
+        AnnotationVisitor v = visitor.visitAnnotation(name, descriptor);
+        return v == visitor ? this : wrap(v);
+      }
+
+      @Override
+      public AnnotationVisitorInterface visitArray(String name) {
+        AnnotationVisitor v = visitor.visitArray(name);
+        return v == visitor ? this : wrap(v);
+      }
+
+      @Override
+      public void visitEnd() {
+        visitor.visitEnd();
+      }
+    };
+  }
+
   @Test
   public void testAsmReader() throws Exception {
     assumeTrue(parameters.isCfRuntime());
@@ -169,7 +212,8 @@
                   @Override
                   public void visitEnd() {
                     for (KeepEdge edge : expectedEdges) {
-                      KeepEdgeWriter.writeEdge(edge, super::visitAnnotation);
+                      KeepEdgeWriter.writeEdge(
+                          edge, (desc, visible) -> wrap(super.visitAnnotation(desc, visible)));
                     }
                     super.visitEnd();
                   }
@@ -190,25 +234,15 @@
 
   @Test
   public void testExtractAndRun() throws Exception {
-    List<String> rules = getKeepRulesForClass(source);
     testForR8(parameters.getBackend())
-        .addClasspathFiles(KEEP_ANNO_PATH)
+        .enableExperimentalKeepAnnotations()
         .addProgramClassesAndInnerClasses(source)
-        .addKeepRules(rules)
         .addKeepMainRule(source)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), source)
         .assertSuccessWithOutput(getExpected());
   }
 
-  public static List<String> getKeepRulesForClass(Class<?> clazz) throws IOException {
-    Set<KeepEdge> keepEdges = KeepEdgeReader.readKeepEdges(ToolHelper.getClassAsBytes(clazz));
-    List<String> rules = new ArrayList<>();
-    KeepRuleExtractor extractor = new KeepRuleExtractor(rules::add);
-    keepEdges.forEach(extractor::extract);
-    return rules;
-  }
-
   private void checkSynthesizedKeepEdgeClass(CodeInspector inspector, Path data)
       throws IOException {
     String synthesizedEdgesClassName =
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarAnyClassTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarAnyClassTest.java
index 74da176..82feaed 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarAnyClassTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarAnyClassTest.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,10 +47,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
@@ -63,19 +61,6 @@
     return ImmutableList.of(TestClass.class, A.class, B.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(A.class), isPresent());
     assertThat(inspector.clazz(A.class).uniqueMethodWithOriginalName("foo"), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java
index 922a3ce..f1f1f6b 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepFooIfBarSameClassTest.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,10 +49,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
@@ -64,20 +62,6 @@
   public List<Class<?>> getInputClasses() {
     return ImmutableList.of(TestClass.class, A.class, B.class);
   }
-
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(A.class), isPresent());
     assertThat(inspector.clazz(A.class).uniqueMethodWithOriginalName("foo"), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
index 6f95aa6..a9c7405 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepInvalidTargetTest.java
@@ -11,10 +11,18 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.keepanno.annotations.KeepOption;
 import com.android.tools.r8.keepanno.annotations.KeepTarget;
 import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.keepanno.asm.KeepEdgeReader;
+import com.android.tools.r8.keepanno.ast.KeepEdge;
 import com.android.tools.r8.keepanno.ast.KeepEdgeException;
+import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
 import org.hamcrest.Matcher;
 import org.junit.Test;
 import org.junit.function.ThrowingRunnable;
@@ -33,21 +41,30 @@
     parameters.assertNoneRuntime();
   }
 
+  private static List<String> extractRuleForClass(Class<?> clazz) throws IOException {
+    Set<KeepEdge> keepEdges = KeepEdgeReader.readKeepEdges(ToolHelper.getClassAsBytes(clazz));
+    List<String> rules = new ArrayList<>();
+    KeepRuleExtractor extractor = new KeepRuleExtractor(rules::add);
+    keepEdges.forEach(extractor::extract);
+    return rules;
+  }
+
   private void assertThrowsWith(ThrowingRunnable fn, Matcher<String> matcher) {
     try {
       fn.run();
-      fail("Expected run to fail");
     } catch (KeepEdgeException e) {
       assertThat(e.getMessage(), matcher);
+      return;
     } catch (Throwable e) {
-      fail("Expected run to fail with KeepEdgeException");
+      fail("Expected run to fail with KeepEdgeException, but failed with: " + e);
     }
+    fail("Expected run to fail");
   }
 
   @Test
   public void testInvalidClassDecl() throws Exception {
     assertThrowsWith(
-        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleClassDeclarations.class),
+        () -> extractRuleForClass(MultipleClassDeclarations.class),
         allOf(
             containsString("Multiple declarations"),
             containsString("className"),
@@ -65,7 +82,7 @@
   @Test
   public void testInvalidExtendsDecl() throws Exception {
     assertThrowsWith(
-        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleExtendsDeclarations.class),
+        () -> extractRuleForClass(MultipleExtendsDeclarations.class),
         allOf(
             containsString("Multiple declarations"),
             containsString("extendsClassName"),
@@ -86,7 +103,7 @@
   @Test
   public void testInvalidMemberDecl() throws Exception {
     assertThrowsWith(
-        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleMemberDeclarations.class),
+        () -> extractRuleForClass(MultipleMemberDeclarations.class),
         allOf(containsString("field"), containsString("method")));
   }
 
@@ -101,7 +118,7 @@
   @Test
   public void testInvalidOptionsDecl() throws Exception {
     assertThrowsWith(
-        () -> KeepEdgeAnnotationsTest.getKeepRulesForClass(MultipleOptionDeclarations.class),
+        () -> extractRuleForClass(MultipleOptionDeclarations.class),
         allOf(containsString("options"), containsString("allow"), containsString("disallow")));
   }
 
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java
index f4060f3..0bf08bc 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepSameMethodTest.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,10 +50,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         // The "all members" target will create an unused "all fields" rule.
@@ -68,19 +66,6 @@
     return ImmutableList.of(TestClass.class, A.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) throws Exception {
     assertThat(inspector.clazz(A.class).method(A.class.getMethod("foo")), isPresent());
     // TODO(b/265892343): The extracted rule will match all params so this is incorrectly kept.
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java
index 02e86ae..8e78dda 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepTargetClassAndMemberKindTest.java
@@ -17,7 +17,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,13 +48,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
-    for (String rule : rules) {
-      System.out.println(rule);
-    }
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .allowUnusedProguardConfigurationRules()
@@ -68,19 +63,6 @@
     return ImmutableList.of(TestClass.class, A.class, B.class, C.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(A.class), isPresent());
     assertThat(inspector.clazz(B.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
index d692791..39eea01 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationTest.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,10 +47,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .allowUnusedProguardConfigurationRules()
@@ -64,19 +62,6 @@
     return ImmutableList.of(TestClass.class, A.class, B.class, C.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(A.class), isPresent());
     assertThat(inspector.clazz(B.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
index a9dc1f7..05c742a 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionAnnotationWithAdditionalPreconditionTest.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,10 +47,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
@@ -63,19 +61,6 @@
     return ImmutableList.of(TestClass.class, A.class, B.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(B.class), isPresent());
     assertThat(inspector.clazz(B.class).uniqueMethodWithOriginalName("<init>"), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
index bfc7fb5..5177150 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionFieldAnnotationTest.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,10 +47,9 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), TestClass.class)
@@ -63,19 +61,6 @@
     return ImmutableList.of(TestClass.class, A.class, B.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(A.class), isPresent());
     assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("classNameForB"), isPresent());
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
index ee605b0..ace719a 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepUsesReflectionOnFieldTest.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -19,7 +18,6 @@
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Field;
-import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,15 +49,18 @@
 
   @Test
   public void testWithRuleExtraction() throws Exception {
-    List<String> rules = getExtractedKeepRules();
-    assertEquals(1, rules.size());
-    assertThat(rules.get(0), containsString("context: " + descriptor(A.class) + "foo()V"));
-    assertThat(rules.get(0), containsString("description: Keep the\\nstring-valued fields"));
     testForR8(parameters.getBackend())
-        .addProgramClassFileData(getInputClassesWithoutAnnotations())
-        .addKeepRules(rules)
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(getInputClasses())
         .addKeepMainRule(TestClass.class)
         .setMinApi(parameters.getApiLevel())
+        .compile()
+        .apply(
+            c -> {
+              String rules = c.getProguardConfiguration().getParsedConfiguration();
+              assertThat(rules, containsString("context: " + descriptor(A.class) + "foo()V"));
+              assertThat(rules, containsString("description: Keep the\\nstring-valued fields"));
+            })
         .run(parameters.getRuntime(), TestClass.class)
         .assertSuccessWithOutput(EXPECTED)
         .inspect(this::checkOutput);
@@ -69,19 +70,6 @@
     return ImmutableList.of(TestClass.class, A.class);
   }
 
-  public List<byte[]> getInputClassesWithoutAnnotations() throws Exception {
-    return KeepEdgeAnnotationsTest.getInputClassesWithoutKeepAnnotations(getInputClasses());
-  }
-
-  public List<String> getExtractedKeepRules() throws Exception {
-    List<Class<?>> classes = getInputClasses();
-    List<String> rules = new ArrayList<>();
-    for (Class<?> clazz : classes) {
-      rules.addAll(KeepEdgeAnnotationsTest.getKeepRulesForClass(clazz));
-    }
-    return rules;
-  }
-
   private void checkOutput(CodeInspector inspector) {
     assertThat(inspector.clazz(A.class), isPresent());
     assertThat(inspector.clazz(A.class).uniqueFieldWithOriginalName("fieldA"), isPresent());
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java
index 4e59b75..211d4bb 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java
@@ -73,6 +73,9 @@
       ClassSubject aClassSubject = inspector.clazz(A.class);
       assertThat(aClassSubject, isPresent());
 
+      ClassSubject bClassSubject = inspector.clazz(B.class);
+      assertThat(bClassSubject, isPresent());
+
       ClassSubject companionClassSubject =
           inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
       assertThat(companionClassSubject, isPresent());
@@ -80,15 +83,24 @@
       MethodSubject interfaceMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
       assertThat(interfaceMethodSubject, isPresent());
 
-      MethodSubject implementationMethodSubject =
+      MethodSubject aForwardingMethodSubject =
           aClassSubject.method(interfaceMethodSubject.getFinalReference());
-      assertThat(implementationMethodSubject, isPresent());
+      assertThat(aForwardingMethodSubject, isPresent());
+
+      MethodSubject bForwardingMethodSubject =
+          bClassSubject.method(interfaceMethodSubject.getFinalReference());
+      assertThat(bForwardingMethodSubject, isPresent());
 
       MethodSubject movedMethodSubject = companionClassSubject.uniqueMethod();
       assertThat(movedMethodSubject, isPresent());
 
-      // TODO(b/265729283): Should also include the two methods from desugaring.
-      profileInspector.assertContainsMethodRule(interfaceMethodSubject);
+      profileInspector
+          .assertContainsClassRule(companionClassSubject)
+          .assertContainsMethodRules(
+              interfaceMethodSubject,
+              aForwardingMethodSubject,
+              bForwardingMethodSubject,
+              movedMethodSubject);
     }
 
     profileInspector.assertContainsNoOtherRules();
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedPrivateInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedPrivateInterfaceMethodProfileRewritingTest.java
new file mode 100644
index 0000000..e1a429b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedPrivateInterfaceMethodProfileRewritingTest.java
@@ -0,0 +1,137 @@
+// Copyright (c) 2023, 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class MovedPrivateInterfaceMethodProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClasses(Main.class, A.class)
+        .addProgramClassFileData(getTransformedInterface())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class, A.class)
+        .addProgramClassFileData(getTransformedInterface())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .enableNoVerticalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  private byte[] getTransformedInterface() throws Exception {
+    return transformer(I.class)
+        .setPrivate(I.class.getDeclaredMethod("m"))
+        .transformMethodInsnInMethod(
+            "bridge",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              assertEquals(Opcodes.INVOKEINTERFACE, opcode);
+              visitor.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  private ExternalArtProfile getArtProfile() throws Exception {
+    return ExternalArtProfile.builder()
+        .addMethodRule(Reference.methodFromMethod(I.class.getDeclaredMethod("m")))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
+      throws Exception {
+    if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+      ClassSubject iClassSubject = inspector.clazz(I.class);
+      assertThat(iClassSubject, isPresent());
+
+      MethodSubject privateInterfaceMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+      assertThat(privateInterfaceMethodSubject, isPresent());
+
+      profileInspector
+          .assertContainsMethodRule(privateInterfaceMethodSubject)
+          .assertContainsNoOtherRules();
+    } else {
+      ClassSubject companionClassSubject =
+          inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
+      assertThat(companionClassSubject, isPresent());
+
+      MethodSubject privateInterfaceMethodSubject =
+          companionClassSubject.uniqueMethodWithOriginalName(
+              SyntheticItemsTestUtils.syntheticPrivateInterfaceMethodAsCompanionMethod(
+                      I.class.getDeclaredMethod("m"))
+                  .getMethodName());
+      assertThat(privateInterfaceMethodSubject, isPresent());
+
+      profileInspector
+          .assertContainsClassRule(companionClassSubject)
+          .assertContainsMethodRule(privateInterfaceMethodSubject)
+          .assertContainsNoOtherRules();
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new A().bridge();
+    }
+  }
+
+  @NoVerticalClassMerging
+  interface I {
+
+    default void bridge() {
+      m(); // invoke-special
+    }
+
+    /*private*/ default void m() {
+      System.out.println("Hello, world!");
+    }
+  }
+
+  static class A implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
index e2f0c2d..6c8b08b 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
@@ -60,20 +60,25 @@
       ClassSubject iClassSubject = inspector.clazz(I.class);
       assertThat(iClassSubject, isPresent());
 
-      MethodSubject mMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
-      assertThat(mMethodSubject, isPresent());
+      MethodSubject staticInterfaceMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+      assertThat(staticInterfaceMethodSubject, isPresent());
 
-      profileInspector.assertContainsMethodRule(mMethodSubject).assertContainsNoOtherRules();
+      profileInspector
+          .assertContainsMethodRule(staticInterfaceMethodSubject)
+          .assertContainsNoOtherRules();
     } else {
       ClassSubject companionClassSubject =
           inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
       assertThat(companionClassSubject, isPresent());
 
-      MethodSubject mMethodSubject = companionClassSubject.uniqueMethodWithOriginalName("m");
-      assertThat(mMethodSubject, isPresent());
+      MethodSubject staticInterfaceMethodSubject =
+          companionClassSubject.uniqueMethodWithOriginalName("m");
+      assertThat(staticInterfaceMethodSubject, isPresent());
 
-      // TODO(b/265729283): Should contain the companion method.
-      profileInspector.assertEmpty();
+      profileInspector
+          .assertContainsClassRule(companionClassSubject)
+          .assertContainsMethodRule(staticInterfaceMethodSubject)
+          .assertContainsNoOtherRules();
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index e376dbc..93f9488 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -93,6 +93,12 @@
     ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
     assertThat(nestMemberClassSubject, isPresent());
 
+    MethodSubject instanceInitializerWithSyntheticArgumentSubject =
+        nestMemberClassSubject.uniqueInstanceInitializer();
+    assertThat(
+        instanceInitializerWithSyntheticArgumentSubject,
+        notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
     ClassSubject syntheticConstructorArgumentClassSubject =
         inspector.clazz(
             SyntheticItemsTestUtils.syntheticNestConstructorArgumentClass(
@@ -155,10 +161,22 @@
         syntheticNestStaticMethodAccessorMethodSubject,
         notIf(isPresent(), parameters.canUseNestBasedAccesses()));
 
-    // TODO(b/265729283): Should contain the nest bridge methods and the synthesized constructor
-    //  argument class.
+    // Verify the residual profile contains the synthetic nest based access bridges and the
+    // synthetic constructor argument class.
     profileInspector
         .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .applyIf(
+            !parameters.canUseNestBasedAccesses(),
+            i ->
+                i.assertContainsMethodRules(
+                        instanceInitializerWithSyntheticArgumentSubject,
+                        syntheticNestInstanceFieldGetterMethodSubject,
+                        syntheticNestInstanceFieldSetterMethodSubject,
+                        syntheticNestInstanceMethodAccessorMethodSubject,
+                        syntheticNestStaticFieldGetterMethodSubject,
+                        syntheticNestStaticFieldSetterMethodSubject,
+                        syntheticNestStaticMethodAccessorMethodSubject)
+                    .assertContainsClassRule(syntheticConstructorArgumentClassSubject))
         .assertContainsNoOtherRules();
   }
 
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/TwrCloseResourceDuplicationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/TwrCloseResourceDuplicationProfileRewritingTest.java
new file mode 100644
index 0000000..692436e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/TwrCloseResourceDuplicationProfileRewritingTest.java
@@ -0,0 +1,165 @@
+// Copyright (c) 2023, 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.profile.art.completeness;
+
+import static com.android.tools.r8.Jdk11TestUtils.getJdk11LibraryFiles;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.twr.TwrCloseResourceDuplicationTest;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TwrCloseResourceDuplicationProfileRewritingTest
+    extends TwrCloseResourceDuplicationTest {
+
+  @Test
+  public void testR8ProfileRewriting() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramFiles(TwrCloseResourceDuplicationTest.getProgramInputs())
+        .addKeepMainRule(MAIN.typeName())
+        .addKeepClassAndMembersRules(FOO.typeName(), BAR.typeName())
+        .addArtProfileForRewriting(getArtProfile())
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .applyIf(
+            parameters.isCfRuntime(),
+            testBuilder ->
+                testBuilder
+                    .addLibraryFiles(getJdk11LibraryFiles(temp))
+                    .addDefaultRuntimeLibrary(parameters)
+                    .addOptionsModification(
+                        options ->
+                            options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces()),
+            testBuilder ->
+                testBuilder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST)))
+        .noHorizontalClassMergingOfSynthetics()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), MAIN.typeName(), getZipFile())
+        .assertSuccessWithOutput(TwrCloseResourceDuplicationTest.EXPECTED);
+  }
+
+  private ExternalArtProfile getArtProfile() {
+    List<TypeReference> closeResourceFormalParameters =
+        ImmutableList.of(
+            Reference.classFromClass(Throwable.class),
+            Reference.classFromClass(AutoCloseable.class));
+    return ExternalArtProfile.builder()
+        .addMethodRule(
+            Reference.method(
+                FOO.getClassReference(),
+                "foo",
+                ImmutableList.of(Reference.classFromClass(String.class)),
+                null))
+        .addMethodRule(
+            Reference.method(
+                FOO.getClassReference(), "$closeResource", closeResourceFormalParameters, null))
+        .addMethodRule(
+            Reference.method(
+                BAR.getClassReference(),
+                "bar",
+                ImmutableList.of(Reference.classFromClass(String.class)),
+                null))
+        .addMethodRule(
+            Reference.method(
+                BAR.getClassReference(), "$closeResource", closeResourceFormalParameters, null))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+    boolean hasTwrCloseResourceSupport =
+        parameters.isCfRuntime()
+            || parameters
+                .getApiLevel()
+                .isGreaterThanOrEqualTo(apiLevelWithTwrCloseResourceSupport());
+
+    assertEquals(hasTwrCloseResourceSupport ? 3 : 7, inspector.allClasses().size());
+    assertThat(inspector.clazz(MAIN.typeName()), isPresent());
+
+    // Class Foo has two methods foo() and $closeResource().
+    ClassSubject fooClassSubject = inspector.clazz(FOO.typeName());
+    assertThat(fooClassSubject, isPresent());
+
+    MethodSubject fooMethodSubject = fooClassSubject.uniqueMethodWithOriginalName("foo");
+    assertThat(fooMethodSubject, isPresent());
+
+    MethodSubject fooCloseResourceMethodSubject =
+        fooClassSubject.uniqueMethodWithOriginalName("$closeResource");
+    assertThat(fooCloseResourceMethodSubject, isPresent());
+
+    // Class Bar has two methods bar() and $closeResource().
+    ClassSubject barClassSubject = inspector.clazz(BAR.typeName());
+    assertThat(barClassSubject, isPresent());
+
+    MethodSubject barMethodSubject = barClassSubject.uniqueMethodWithOriginalName("bar");
+    assertThat(barMethodSubject, isPresent());
+
+    MethodSubject barCloseResourceMethodSubject =
+        barClassSubject.uniqueMethodWithOriginalName("$closeResource");
+    assertThat(barCloseResourceMethodSubject, isPresent());
+
+    // There is a synthetic API outline, a backport and two twr classes.
+    ClassSubject syntheticApiOutlineClassSubject =
+        inspector.clazz(
+            SyntheticItemsTestUtils.syntheticApiOutlineClass(BAR.getClassReference(), 0));
+    assertThat(syntheticApiOutlineClassSubject, notIf(isPresent(), hasTwrCloseResourceSupport));
+
+    ClassSubject syntheticBackportClassSubject =
+        inspector.clazz(SyntheticItemsTestUtils.syntheticBackportClass(BAR.getClassReference(), 1));
+    assertThat(syntheticBackportClassSubject, notIf(isPresent(), hasTwrCloseResourceSupport));
+
+    ClassSubject syntheticTwrCloseResourceClassSubject =
+        inspector.clazz(
+            SyntheticItemsTestUtils.syntheticTwrCloseResourceClass(BAR.getClassReference(), 2));
+    assertThat(
+        syntheticTwrCloseResourceClassSubject, notIf(isPresent(), hasTwrCloseResourceSupport));
+
+    ClassSubject otherSyntheticTwrCloseResourceClassSubject =
+        inspector.clazz(
+            SyntheticItemsTestUtils.syntheticTwrCloseResourceClass(BAR.getClassReference(), 3));
+    assertThat(
+        otherSyntheticTwrCloseResourceClassSubject, notIf(isPresent(), hasTwrCloseResourceSupport));
+
+    // Verify that the residual profile contains all of the above.
+    profileInspector
+        .assertContainsMethodRules(
+            fooMethodSubject,
+            fooCloseResourceMethodSubject,
+            barMethodSubject,
+            barCloseResourceMethodSubject)
+        .applyIf(
+            !hasTwrCloseResourceSupport,
+            i ->
+                i.assertContainsClassRules(
+                        syntheticApiOutlineClassSubject,
+                        syntheticBackportClassSubject,
+                        syntheticTwrCloseResourceClassSubject,
+                        otherSyntheticTwrCloseResourceClassSubject)
+                    .assertContainsMethodRules(
+                        syntheticApiOutlineClassSubject.uniqueMethod(),
+                        syntheticBackportClassSubject.uniqueMethod(),
+                        syntheticTwrCloseResourceClassSubject.uniqueMethod(),
+                        otherSyntheticTwrCloseResourceClassSubject.uniqueMethod()))
+        .assertContainsNoOtherRules();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
index d498e18..3b75886 100644
--- a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
+++ b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
@@ -53,13 +53,20 @@
     return this;
   }
 
+  public ArtProfileInspector assertContainsClassRules(ClassReference... classReferences) {
+    for (ClassReference classReference : classReferences) {
+      assertContainsClassRule(classReference);
+    }
+    return this;
+  }
+
   public ArtProfileInspector assertContainsClassRule(ClassSubject classSubject) {
     return assertContainsClassRule(classSubject.getFinalReference());
   }
 
-  public ArtProfileInspector assertContainsClassRules(ClassReference... classReferences) {
-    for (ClassReference classReference : classReferences) {
-      assertContainsClassRule(classReference);
+  public ArtProfileInspector assertContainsClassRules(ClassSubject... classSubjects) {
+    for (ClassSubject classSubject : classSubjects) {
+      assertContainsClassRule(classSubject);
     }
     return this;
   }
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
index a0029a0..2741404 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.TestParameters;
 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.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
@@ -84,16 +85,26 @@
     }
   }
 
+  enum ProviderType {
+    API,
+    INJARS
+  }
+
   @Parameter(0)
   public TestParameters parameters;
 
   @Parameter(1)
   public LibraryType libraryType;
 
-  @Parameters(name = "{0} AAR: {1}")
+  @Parameter(2)
+  public ProviderType providerType;
+
+  @Parameters(name = "{0}, AAR: {1}, {2}")
   public static List<Object[]> data() {
     return buildParameters(
-        getTestParameters().withAllRuntimesAndApiLevels().build(), LibraryType.values());
+        getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(),
+        LibraryType.values(),
+        ProviderType.values());
   }
 
   private Path buildLibrary(List<String> rules) throws Exception {
@@ -121,8 +132,10 @@
   }
 
   private CodeInspector runTest(List<String> rules) throws Exception {
+    Path library = buildLibrary(rules);
     return testForR8(parameters.getBackend())
-        .addProgramFiles(buildLibrary(rules))
+        .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+        .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspector();
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index cf968ea..d7df00b 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.PRIVATE_METHOD_PREFIX;
 import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_GET_NAME_PREFIX;
 import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_PUT_NAME_PREFIX;
 import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_METHOD_NAME_PREFIX;
@@ -205,6 +206,16 @@
         originalMethod.getMethodDescriptor());
   }
 
+  public static MethodReference syntheticPrivateInterfaceMethodAsCompanionMethod(Method method) {
+    MethodReference originalMethod = Reference.methodFromMethod(method);
+    ClassReference companionClassReference =
+        syntheticCompanionClass(originalMethod.getHolderClass());
+    return Reference.methodFromDescriptor(
+        companionClassReference,
+        PRIVATE_METHOD_PREFIX + method.getName(),
+        originalMethod.getMethodDescriptor());
+  }
+
   public static ClassReference syntheticEnumUnboxingLocalUtilityClass(Class<?> clazz) {
     return Reference.classFromTypeName(
         clazz.getTypeName() + naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS.getDescriptor());
diff --git a/tools/release_smali.py b/tools/release_smali.py
index 12a9389..902e517 100755
--- a/tools/release_smali.py
+++ b/tools/release_smali.py
@@ -7,6 +7,7 @@
 import sys
 
 import gmaven
+import utils
 
 ARCHIVE_BUCKET = 'r8-releases'
 REPO = 'https://github.com/google/smali'
@@ -26,6 +27,7 @@
 
 def Main():
   options = parse_options()
+  utils.check_gcert()
   gfile = ('/bigstore/r8-releases/smali/%s/smali-maven-release-%s.zip'
        % (options.version, options.version))
   release_id = gmaven.publisher_stage([gfile], options.dry_run)