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)