Merge commit 'f38f0382' into dev-release
diff --git a/build.gradle b/build.gradle index 3032180..3c96101 100644 --- a/build.gradle +++ b/build.gradle
@@ -988,14 +988,19 @@ return baseD8CommandLine(allArgs) } -def r8LibCreateTask(name, pgConfs = [], r8Task, output, args = ["--release"], libs = []) { +def r8LibCreateTask(name, pgConfs = [], r8Task, output, libs = []) { return tasks.create("r8Lib${name}", Exec) { - inputs.files ([pgConfs, r8WithRelocatedDeps.outputs, r8Task.outputs, libs]) + inputs.files ([pgConfs, r8WithRelocatedDeps.outputs, r8Task.outputs]) outputs.file output dependsOn downloadOpenJDKrt dependsOn r8WithRelocatedDeps dependsOn r8Task - commandLine r8CfCommandLine(r8Task.outputs.files[0], output, pgConfs, args, libs) + commandLine ([ + "python", "tools/create_r8lib.py", + "--r8jar", r8Task.outputs.files[0], + "--output", output] + + (pgConfs.collectMany { ["--pg-conf", it] }) + + (libs.collectMany { ["--lib", it] })) workingDir = projectDir } } @@ -1077,11 +1082,6 @@ workingDir = projectDir } -task R8LibApiOnly { - dependsOn r8LibCreateTask("Api", ["src/main/keep.txt"], r8NoManifest, r8LibPath) - outputs.file r8LibPath -} - task R8Lib { dependsOn r8LibCreateTask( "Main", @@ -1095,11 +1095,10 @@ task R8LibNoDeps { dependsOn r8LibCreateTask( - "NoDeps", + "MainNoDeps", ["src/main/keep.txt"], r8NoManifestWithoutDeps, r8LibExludeDepsPath, - "--release", repackageDeps.outputs.files ).dependsOn(repackageDeps) inputs.files ([r8NoManifestWithoutDeps.outputs, repackageDeps.outputs])
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java index 7f17170..c867e02 100644 --- a/src/main/java/com/android/tools/r8/D8.java +++ b/src/main/java/com/android/tools/r8/D8.java
@@ -261,7 +261,7 @@ if (options.isGeneratingClassFiles()) { ProguardMapSupplier proguardMapSupplier = - finalizeApplication(inputApp, appView, namingLens); + finalizeApplication(inputApp, appView, executor, namingLens); new CfApplicationWriter( appView, marker, GraphLens.getIdentityLens(), namingLens, proguardMapSupplier) .write(options.getClassFileConsumer()); @@ -306,7 +306,7 @@ appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo)); } ProguardMapSupplier proguardMapSupplier = - finalizeApplication(inputApp, appView, namingLens); + finalizeApplication(inputApp, appView, executor, namingLens); new ApplicationWriter( appView, @@ -330,8 +330,12 @@ } private static ProguardMapSupplier finalizeApplication( - AndroidApp inputApp, AppView<AppInfo> appView, NamingLens namingLens) { - SyntheticFinalization.finalize(appView); + AndroidApp inputApp, + AppView<AppInfo> appView, + ExecutorService executorService, + NamingLens namingLens) + throws ExecutionException { + SyntheticFinalization.finalize(appView, executorService); if (appView.options().proguardMapConsumer == null) { return null; }
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index 3fbff40..2013fb9 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -480,7 +480,7 @@ assert internal.neverMergePrefixes.contains("j$."); // Assert some of R8 optimizations are disabled. - assert !internal.enableInlining; + assert !internal.inlinerOptions().enableInlining; assert !internal.enableClassInlining; assert !internal.enableVerticalClassMerging; assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java index d5f8787..f51d700 100644 --- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java +++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -78,7 +78,7 @@ options.enableMainDexListCheck = false; options.minimalMainDex = minimalMainDex; assert !options.isMinifying(); - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.outline.enabled = false; ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java index d71cabc..875635f 100644 --- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java +++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -67,7 +67,7 @@ options.ignoreMainDexMissingClasses = true; options.minimalMainDex = false; assert !options.isMinifying(); - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.outline.enabled = false; try {
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java index 2a794ab..1edb9007 100644 --- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java +++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -118,12 +118,9 @@ */ public static List<String> run(GenerateMainDexListCommand command) throws CompilationFailedException { - ExecutorService executorService = ThreadUtils.getExecutorService(command.getInternalOptions()); - try { - return run(command, executorService); - } finally { - executorService.shutdown(); - } + AndroidApp app = command.getInputApp(); + InternalOptions options = command.getInternalOptions(); + return runForTesting(app, options); } /** @@ -145,28 +142,43 @@ InternalOptions options = command.getInternalOptions(); List<String> result = new ArrayList<>(); ExceptionUtils.withMainDexListHandler( - command.getReporter(), + command.getReporter(), () -> run(app, executor, options, result)); + return result; + } + + static List<String> runForTesting(AndroidApp app, InternalOptions options) + throws CompilationFailedException { + ExecutorService executorService = ThreadUtils.getExecutorService(options); + List<String> result = new ArrayList<>(); + ExceptionUtils.withMainDexListHandler( + options.reporter, () -> { try { - new GenerateMainDexList(options) - .run( - app, - executor, - new SortingStringConsumer( - new ForwardingConsumer(options.mainDexListConsumer) { - @Override - public void accept(String string, DiagnosticsHandler handler) { - result.add(string); - super.accept(string, handler); - } - })); + run(app, executorService, options, result); } finally { - executor.shutdown(); + executorService.shutdown(); } }); return result; } + private static void run( + AndroidApp app, ExecutorService executor, InternalOptions options, List<String> result) + throws IOException { + new GenerateMainDexList(options) + .run( + app, + executor, + new SortingStringConsumer( + new ForwardingConsumer(options.mainDexListConsumer) { + @Override + public void accept(String string, DiagnosticsHandler handler) { + result.add(string); + super.accept(string, handler); + } + })); + } + public static void main(String[] args) throws CompilationFailedException { GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.parse(args); GenerateMainDexListCommand command = builder.build();
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java index 85bc305..2512782 100644 --- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java +++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -223,7 +223,7 @@ internal.mainDexKeptGraphConsumer = mainDexKeptGraphConsumer; internal.minimalMainDex = internal.debug; internal.enableEnumValueOptimization = false; - internal.enableInlining = false; + internal.inlinerOptions().enableInlining = false; return internal; } }
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java index 8470275..9946047 100644 --- a/src/main/java/com/android/tools/r8/L8.java +++ b/src/main/java/com/android/tools/r8/L8.java
@@ -141,7 +141,7 @@ new IRConverter(appView, timing).convert(appView, executor); - SyntheticFinalization.finalize(appView); + SyntheticFinalization.finalize(appView, executor); NamingLens namingLens = PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView); new GenericSignatureRewriter(appView, namingLens).run(appView.appInfo().classes(), executor);
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java index 3f2ce3d..133ca25 100644 --- a/src/main/java/com/android/tools/r8/L8Command.java +++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -176,7 +176,7 @@ assert !internal.passthroughDexCode; // Assert some of R8 optimizations are disabled. - assert !internal.enableInlining; + assert !internal.inlinerOptions().enableInlining; assert !internal.enableClassInlining; assert !internal.enableVerticalClassMerging; assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index 3eab5e6..ec0f361 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -419,7 +419,8 @@ .setPrunedApp(prunedApp) .addRemovedClasses(removedClasses) .addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging()) - .build()); + .build(), + executorService); new AbstractMethodRemover( appViewWithLiveness, appViewWithLiveness.appInfo().computeSubtypingInfo()) .run(); @@ -618,7 +619,8 @@ .setPrunedApp(application) .addRemovedClasses(CollectionUtils.mergeSets(prunedTypes, removedClasses)) .addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging()) - .build()); + .build(), + executorService); new BridgeHoisting(appViewWithLiveness).run(); @@ -728,9 +730,9 @@ } if (appView.appInfo().hasLiveness()) { - SyntheticFinalization.finalizeWithLiveness(appView.withLiveness()); + SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService); } else { - SyntheticFinalization.finalizeWithClassHierarchy(appView); + SyntheticFinalization.finalizeWithClassHierarchy(appView, executorService); } // Clear the reference type lattice element cache. This is required since class merging may
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java index ab664c3..5e7cf59 100644 --- a/src/main/java/com/android/tools/r8/R8CommandParser.java +++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -6,6 +6,8 @@ import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.FlagFile; +import com.android.tools.r8.utils.MapIdTemplateProvider; +import com.android.tools.r8.utils.SourceFileTemplateProvider; import com.android.tools.r8.utils.StringDiagnostic; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -34,6 +36,8 @@ "--pg-map-output", "--desugared-lib", "--desugared-lib-pg-conf-output", + "--map-id-template", + "--source-file-template", THREAD_COUNT_FLAG); private static final Set<String> OPTIONS_WITH_TWO_PARAMETERS = ImmutableSet.of("--feature"); @@ -262,6 +266,11 @@ builder.setDesugaredLibraryKeepRuleConsumer(consumer); } else if (arg.equals("--no-data-resources")) { state.includeDataResources = false; + } else if (arg.equals("--map-id-template")) { + builder.setMapIdProvider(MapIdTemplateProvider.create(nextArg, builder.getReporter())); + } else if (arg.equals("--source-file-template")) { + builder.setSourceFileProvider( + SourceFileTemplateProvider.create(nextArg, builder.getReporter())); } else if (arg.startsWith("--")) { if (tryParseAssertionArgument(builder, arg, argsOrigin)) { continue;
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java index df45e6b..e5a744b 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfo.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -15,6 +15,8 @@ import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.IterableUtils; import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.function.Consumer; public class AppInfo implements DexDefinitionSupplier { @@ -63,7 +65,8 @@ this.obsolete = obsolete; } - public AppInfo prunedCopyFrom(PrunedItems prunedItems) { + public AppInfo prunedCopyFrom(PrunedItems prunedItems, ExecutorService executorService) + throws ExecutionException { assert getClass() == AppInfo.class; assert checkIfObsolete(); assert prunedItems.getPrunedApp() == app();
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java index 12a5e8d..a13b905 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -40,6 +40,8 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.function.Function; /* Specific subclass of AppInfo designed to support desugaring in D8. Desugaring requires a @@ -123,7 +125,8 @@ } @Override - public AppInfoWithClassHierarchy prunedCopyFrom(PrunedItems prunedItems) { + public AppInfoWithClassHierarchy prunedCopyFrom( + PrunedItems prunedItems, ExecutorService executorService) throws ExecutionException { assert getClass() == AppInfoWithClassHierarchy.class; assert checkIfObsolete(); assert prunedItems.getPrunedApp() == app();
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java index 0e165f7..4d3d50a 100644 --- a/src/main/java/com/android/tools/r8/graph/AppView.java +++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -51,6 +51,8 @@ import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -687,19 +689,20 @@ return !cfByteCodePassThrough.isEmpty(); } - public void pruneItems(PrunedItems prunedItems) { + public void pruneItems(PrunedItems prunedItems, ExecutorService executorService) + throws ExecutionException { if (prunedItems.isEmpty()) { assert appInfo().app() == prunedItems.getPrunedApp(); return; } if (appInfo.hasLiveness()) { AppView<AppInfoWithLiveness> self = withLiveness(); - self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems)); + self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems, executorService)); } else if (appInfo.hasClassHierarchy()) { AppView<AppInfoWithClassHierarchy> self = withClassHierarchy(); - self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems)); + self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems, executorService)); } else { - pruneAppInfo(prunedItems, this); + pruneAppInfo(prunedItems, this, executorService); } if (appServices() != null) { setAppServices(appServices().prunedCopy(prunedItems)); @@ -714,8 +717,11 @@ } @SuppressWarnings("unchecked") - private static void pruneAppInfo(PrunedItems prunedItems, AppView<?> appView) { - ((AppView<AppInfo>) appView).setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems)); + private static void pruneAppInfo( + PrunedItems prunedItems, AppView<?> appView, ExecutorService executorService) + throws ExecutionException { + ((AppView<AppInfo>) appView) + .setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems, executorService)); } public void rewriteWithLens(NonIdentityGraphLens lens) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java index aa201c2..8409ee1 100644 --- a/src/main/java/com/android/tools/r8/graph/DexCode.java +++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -118,7 +118,7 @@ @Override public int estimatedSizeForInlining() { - return instructions.length; + return codeSizeInBytes(); } @Override
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java index fdba79b..f0675ba 100644 --- a/src/main/java/com/android/tools/r8/graph/GraphLens.java +++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -26,7 +26,6 @@ import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import java.util.ArrayList; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -564,9 +563,9 @@ this::lookupType, this::getRenamedFieldSignature, this::getRenamedMethodSignature); } - public Set<DexReference> rewriteReferences(Set<DexReference> references) { - Set<DexReference> result = SetUtils.newIdentityHashSet(references.size()); - for (DexReference reference : references) { + public <T extends DexReference> Set<T> rewriteReferences(Set<T> references) { + Set<T> result = SetUtils.newIdentityHashSet(references.size()); + for (T reference : references) { result.add(rewriteReference(reference)); } return result; @@ -602,30 +601,15 @@ return result; } - public Object2BooleanMap<DexReference> rewriteReferenceKeys(Object2BooleanMap<DexReference> map) { - Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>(); - for (Object2BooleanMap.Entry<DexReference> entry : map.object2BooleanEntrySet()) { + public <T extends DexReference> Object2BooleanMap<T> rewriteReferenceKeys( + Object2BooleanMap<T> map) { + Object2BooleanMap<T> result = new Object2BooleanArrayMap<>(); + for (Object2BooleanMap.Entry<T> entry : map.object2BooleanEntrySet()) { result.put(rewriteReference(entry.getKey()), entry.getBooleanValue()); } return result; } - public ImmutableSet<DexMethod> rewriteMethods(Set<DexMethod> methods) { - ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder(); - for (DexMethod method : methods) { - builder.add(getRenamedMethodSignature(method)); - } - return builder.build(); - } - - public ImmutableSet<DexField> rewriteFields(Set<DexField> fields) { - ImmutableSet.Builder<DexField> builder = ImmutableSet.builder(); - for (DexField field : fields) { - builder.add(getRenamedFieldSignature(field)); - } - return builder.build(); - } - public <T> ImmutableMap<DexField, T> rewriteFieldKeys(Map<DexField, T> map) { ImmutableMap.Builder<DexField, T> builder = ImmutableMap.builder(); map.forEach((field, value) -> builder.put(getRenamedFieldSignature(field), value)); @@ -649,7 +633,7 @@ newMap.put( rewrittenType, previousValue != null ? merge.apply(value, previousValue) : value); }); - return Collections.unmodifiableMap(newMap); + return newMap; } public boolean verifyMappingToOriginalProgram(
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java index 1f8aa68..4489421 100644 --- a/src/main/java/com/android/tools/r8/graph/PrunedItems.java +++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -41,15 +41,18 @@ } public boolean isEmpty() { - return removedClasses.isEmpty() && additionalPinnedItems.isEmpty(); + return removedClasses.isEmpty() + && removedFields.isEmpty() + && removedMethods.isEmpty() + && additionalPinnedItems.isEmpty(); } public boolean isRemoved(DexField field) { - return removedFields.contains(field); + return removedFields.contains(field) || removedClasses.contains(field.getHolderType()); } public boolean isRemoved(DexMethod method) { - return removedMethods.contains(method); + return removedMethods.contains(method) || removedClasses.contains(method.getHolderType()); } public boolean isRemoved(DexType type) { @@ -72,10 +75,26 @@ return !removedClasses.isEmpty(); } + public boolean hasRemovedFields() { + return !removedFields.isEmpty(); + } + + public boolean hasRemovedMembers() { + return hasRemovedFields() || hasRemovedMethods(); + } + + public boolean hasRemovedMethods() { + return !removedMethods.isEmpty(); + } + public Set<DexType> getRemovedClasses() { return removedClasses; } + public Set<DexField> getRemovedFields() { + return removedFields; + } + public Set<DexMethod> getRemovedMethods() { return removedMethods; } @@ -86,7 +105,7 @@ private final Set<DexReference> additionalPinnedItems = Sets.newIdentityHashSet(); private final Set<DexType> noLongerSyntheticItems = Sets.newIdentityHashSet(); - private final Set<DexType> removedClasses = Sets.newIdentityHashSet(); + private Set<DexType> removedClasses = Sets.newIdentityHashSet(); private final Set<DexField> removedFields = Sets.newIdentityHashSet(); private final Set<DexMethod> removedMethods = Sets.newIdentityHashSet(); @@ -122,6 +141,11 @@ return this; } + public Builder setRemovedClasses(Set<DexType> removedClasses) { + this.removedClasses = removedClasses; + return this; + } + public PrunedItems build() { return new PrunedItems( prunedApp,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java index 666bd64..2f9a4ee 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -123,7 +123,10 @@ // Prune keep info. KeepInfoCollection keepInfo = appView.getKeepInfo(); - keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources())); + keepInfo.mutate( + mutator -> + mutator.removeKeepInfoForPrunedItems( + PrunedItems.builder().setRemovedClasses(mergedClasses.getSources()).build())); // Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation // sites, fields accesses, etc. are correctly transferred to the target classes. @@ -142,7 +145,8 @@ .setPrunedApp(appView.appInfo().app()) .addRemovedClasses(mergedClasses.getSources()) .addNoLongerSyntheticItems(mergedClasses.getSources()) - .build()); + .build(), + executorService); } private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java index 92998f3..efbcd4c 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -32,6 +32,7 @@ import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; import com.android.tools.r8.ir.regalloc.RegisterAllocator; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.ImmutableList; import java.util.BitSet; @@ -70,6 +71,10 @@ } } + public int getFirstNonReceiverArgumentIndex() { + return BooleanUtils.intValue(isInvokeMethodWithReceiver()); + } + public abstract boolean getInterfaceBit(); @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java index e063b8f..9179014 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -132,8 +132,7 @@ receiverLowerBoundType); } else { // In D8, allow lookupSingleTarget() to be used for finding final library methods. This is - // used - // for library modeling. + // used for library modeling. DexType holder = method.holder; if (holder.isClassType()) { DexClass clazz = appView.definitionFor(holder);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java index 4899a50..5d93463 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java
@@ -77,7 +77,7 @@ continue; } - if (appView.options().disableInliningOfLibraryMethodOverrides + if (appView.options().inlinerOptions().disableInliningOfLibraryMethodOverrides && method.getDefinition().isLibraryMethodOverride().isTrue()) { continue; }
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 4fde21a..293635b 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
@@ -258,7 +258,9 @@ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness(); assumeInserter = new AssumeInserter(appViewWithLiveness); this.classInliner = - options.enableClassInlining && options.enableInlining ? new ClassInliner() : null; + options.enableClassInlining && options.inlinerOptions().enableInlining + ? new ClassInliner() + : null; this.classStaticizer = options.enableClassStaticizer ? new ClassStaticizer(appViewWithLiveness, this) : null; this.dynamicTypeOptimization = new DynamicTypeOptimization(appViewWithLiveness); @@ -654,9 +656,6 @@ classStaticizer, classStaticizer -> classStaticizer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass)); - ConsumerUtils.acceptIfNotNull( - inliner, - inliner -> inliner.initializeDoubleInlineCallers(graphLensForPrimaryOptimizationPass)); outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass); if (fieldAccessAnalysis != null) { @@ -1257,7 +1256,7 @@ previous = printMethod(code, "IR after generated message lite shrinking (SSA)", previous); - if (!isDebugMode && options.enableInlining && inliner != null) { + if (!isDebugMode && options.inlinerOptions().enableInlining && inliner != null) { timing.begin("Inlining"); inliner.performInlining(code.context(), code, feedback, methodProcessor, timing); timing.end(); @@ -1408,7 +1407,7 @@ timing.begin("Inline classes"); // Class inliner should work before lambda merger, so if it inlines the // lambda, it does not get collected by merger. - assert options.enableInlining && inliner != null; + assert options.inlinerOptions().enableInlining && inliner != null; classInliner.processMethodCode( appView.withLiveness(), codeRewriter, @@ -1425,7 +1424,6 @@ inliner.createDefaultOracle( code.context(), methodProcessor, - options.classInliningInstructionLimit, // Inlining instruction allowance is not needed for the class inliner since it // always uses a force inlining oracle for inlining. -1))); @@ -1684,7 +1682,7 @@ } private boolean shouldComputeInliningConstraint(ProgramMethod method) { - if (!options.enableInlining || inliner == null) { + if (!options.inlinerOptions().enableInlining || inliner == null) { return false; } DexEncodedMethod definition = method.getDefinition(); @@ -1957,12 +1955,30 @@ * Called when a method is pruned as a result of optimizations during IR processing in R8, to * allow optimizations that track sets of methods to fixup their state. */ - public void pruneMethod(ProgramMethod method) { + public void onMethodPruned(ProgramMethod method) { assert appView.enableWholeProgramOptimizations(); assert method.getHolder().lookupMethod(method.getReference()) == null; - appView.withArgumentPropagator(argumentPropagator -> argumentPropagator.pruneMethod(method)); + appView.withArgumentPropagator(argumentPropagator -> argumentPropagator.onMethodPruned(method)); + enumUnboxer.onMethodPruned(method); + outliner.onMethodPruned(method); if (inliner != null) { - inliner.pruneMethod(method); + inliner.onMethodPruned(method); + } + } + + /** + * Called when a method is transformed into an abstract or "throw null" method as a result of + * optimizations during IR processing in R8. + */ + public void onMethodCodePruned(ProgramMethod method) { + assert appView.enableWholeProgramOptimizations(); + assert method.getHolder().lookupMethod(method.getReference()) != null; + appView.withArgumentPropagator( + argumentPropagator -> argumentPropagator.onMethodCodePruned(method)); + enumUnboxer.onMethodCodePruned(method); + outliner.onMethodCodePruned(method); + if (inliner != null) { + inliner.onMethodCodePruned(method); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java index e7374c5..0b7b139 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -7,6 +7,9 @@ import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.collectAllMonitorEnterValues; import static com.android.tools.r8.utils.AndroidApiLevelUtils.isApiSafeForInlining; +import com.android.tools.r8.code.MoveResult; +import com.android.tools.r8.code.MoveResultObject; +import com.android.tools.r8.code.MoveResultWide; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.features.ClassToFeatureSplitMap; import com.android.tools.r8.graph.AppView; @@ -43,7 +46,7 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.synthesis.SyntheticItems; import com.android.tools.r8.utils.BooleanUtils; -import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.InternalOptions.InlinerOptions; import com.android.tools.r8.utils.Timing; import com.google.common.collect.Sets; import java.util.ArrayList; @@ -55,10 +58,10 @@ private final AppView<AppInfoWithLiveness> appView; private final Inliner inliner; + private final InlinerOptions inlinerOptions; private final ProgramMethod method; private final MethodProcessor methodProcessor; private final InliningReasonStrategy reasonStrategy; - private final int inliningInstructionLimit; private int instructionAllowance; DefaultInliningOracle( @@ -67,14 +70,13 @@ InliningReasonStrategy inliningReasonStrategy, ProgramMethod method, MethodProcessor methodProcessor, - int inliningInstructionLimit, int inliningInstructionAllowance) { this.appView = appView; this.inliner = inliner; + this.inlinerOptions = appView.options().inlinerOptions(); this.reasonStrategy = inliningReasonStrategy; this.method = method; this.methodProcessor = methodProcessor; - this.inliningInstructionLimit = inliningInstructionLimit; this.instructionAllowance = inliningInstructionAllowance; } @@ -168,7 +170,7 @@ } } - Set<Reason> validInliningReasons = appView.options().testing.validInliningReasons; + Set<Reason> validInliningReasons = appView.testing().validInliningReasons; if (validInliningReasons != null && !validInliningReasons.contains(reason)) { whyAreYouNotInliningReporter.reportInvalidInliningReason(reason, validInliningReasons); return false; @@ -180,26 +182,7 @@ return false; } - if (reason == Reason.DUAL_CALLER) { - assert methodProcessor.isPrimaryMethodProcessor() || methodProcessor.isPostMethodProcessor(); - if (satisfiesRequirementsForSimpleInlining(invoke, singleTarget)) { - // When we have a method with two call sites, we simply inline the method as we normally do - // when the method is small. We still need to ensure that the other call site is also - // inlined, though. Therefore, we record here that we have seen one of the two call sites - // as we normally do. - inliner.recordDoubleInliningCandidate(method, singleTarget, methodProcessor); - } else if (inliner.isDoubleInliningEnabled(methodProcessor)) { - if (!inliner.satisfiesRequirementsForDoubleInlining( - method, singleTarget, methodProcessor)) { - whyAreYouNotInliningReporter.reportInvalidDoubleInliningCandidate(); - return false; - } - } else { - // TODO(b/142300882): Should in principle disallow inlining in this case. - inliner.recordDoubleInliningCandidate(method, singleTarget, methodProcessor); - } - } else if (reason == Reason.SIMPLE - && !satisfiesRequirementsForSimpleInlining(invoke, singleTarget)) { + if (reason == Reason.SIMPLE && !satisfiesRequirementsForSimpleInlining(invoke, singleTarget)) { whyAreYouNotInliningReporter.reportInlineeNotSimple(); return false; } @@ -222,7 +205,9 @@ InvokeMethod invoke, ProgramMethod target) { // If we are looking for a simple method, only inline if actually simple. Code code = target.getDefinition().getCode(); - int instructionLimit = computeInstructionLimit(invoke, target); + int instructionLimit = + inlinerOptions.getSimpleInliningInstructionLimit() + + getInliningInstructionLimitIncrement(invoke, target); if (code.estimatedSizeForInliningAtMost(instructionLimit)) { return true; } @@ -233,15 +218,14 @@ return simpleInliningConstraint.isSatisfied(invoke); } - private int computeInstructionLimit(InvokeMethod invoke, ProgramMethod candidate) { - int instructionLimit = inliningInstructionLimit; + private int getInliningInstructionLimitIncrement(InvokeMethod invoke, ProgramMethod candidate) { + int instructionLimit = 0; BitSet hints = candidate.getDefinition().getOptimizationInfo().getNonNullParamOrThrow(); if (hints != null) { - List<Value> arguments = invoke.inValues(); - if (invoke.isInvokeMethodWithReceiver()) { - arguments = arguments.subList(1, arguments.size()); - } - for (int index = 0; index < arguments.size(); index++) { + List<Value> arguments = invoke.arguments(); + for (int index = invoke.getFirstNonReceiverArgumentIndex(); + index < arguments.size(); + index++) { Value argument = arguments.get(index); if ((argument.isArgument() || (argument.getType().isReferenceType() && argument.isNeverNull())) @@ -251,6 +235,13 @@ } } } + if (appView.options().isGeneratingDex() + && invoke.hasOutValue() + && invoke.outValue().hasNonDebugUsers()) { + assert MoveResult.SIZE == MoveResultObject.SIZE; + assert MoveResult.SIZE == MoveResultWide.SIZE; + instructionLimit += MoveResult.SIZE; + } return instructionLimit; } @@ -336,8 +327,7 @@ .getDefinition() .getOptimizationInfo() .checksNullReceiverBeforeAnySideEffect()) { - InternalOptions options = appView.options(); - if (!options.enableInliningOfInvokesWithNullableReceivers) { + if (!inlinerOptions.enableInliningOfInvokesWithNullableReceivers) { whyAreYouNotInliningReporter.reportReceiverMaybeNull(); return null; } @@ -358,7 +348,7 @@ return action; } if (appView.canUseInitClass() - && appView.options().enableInliningOfInvokesWithClassInitializationSideEffects) { + && inlinerOptions.enableInliningOfInvokesWithClassInitializationSideEffects) { action.setShouldSynthesizeInitClass(); return action; } @@ -438,7 +428,7 @@ return true; } - int threshold = appView.options().applyInliningToInlineeMaxDepth; + int threshold = inlinerOptions.applyInliningToInlineeMaxDepth; if (inliningDepth <= threshold) { return true; } @@ -644,7 +634,7 @@ int numberOfMonitorEnterValuesAfterInlining = constantMonitorEnterValues.size() + nonConstantMonitorEnterValues.size(); - int threshold = appView.options().inliningMonitorEnterValuesAllowance; + int threshold = inlinerOptions.inliningMonitorEnterValuesAllowance; if (numberOfMonitorEnterValuesAfterInlining > threshold) { whyAreYouNotInliningReporter.reportWillExceedMonitorEnterValuesBudget( numberOfMonitorEnterValuesAfterInlining, threshold); @@ -693,7 +683,7 @@ numberOfThrowingInstructionsInInlinee * block.numberOfCatchHandlers(); // Abort if inlining could lead to an explosion in the number of control flow // resolution blocks that setup the register state before the actual catch handler. - int threshold = appView.options().inliningControlFlowResolutionBlocksThreshold; + int threshold = inlinerOptions.inliningControlFlowResolutionBlocksThreshold; if (estimatedNumberOfControlFlowResolutionBlocks >= threshold) { whyAreYouNotInliningReporter.reportPotentialExplosionInExceptionalControlFlowResolutionBlocks( estimatedNumberOfControlFlowResolutionBlocks, threshold);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index ecf8f25..32d2228 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -63,6 +63,7 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.MainDexInfo; import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.InternalOptions.InlinerOptions; import com.android.tools.r8.utils.IteratorUtils; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.SetUtils; @@ -75,7 +76,6 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; -import java.util.IdentityHashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -94,13 +94,9 @@ // due to not being processed at the time of inlining. private final LongLivedProgramMethodSetBuilder<ProgramMethodSet> singleInlineCallers; - // State for inlining methods which are known to be called twice. - private LongLivedProgramMethodSetBuilder<ProgramMethodSet> doubleInlineCallers; - private final ProgramMethodSet doubleInlineSelectedTargets = ProgramMethodSet.create(); - private final Map<DexEncodedMethod, ProgramMethod> doubleInlineeCandidates = - new IdentityHashMap<>(); - - private final Map<DexProgramClass, ProgramMethodSet> singleCallerInlinedMethods = + // The set of methods that have been single caller inlined in the current wave. These need to be + // pruned when the wave ends. + private final Map<DexProgramClass, ProgramMethodSet> singleCallerInlinedMethodsInWave = new ConcurrentHashMap<>(); private final AvailableApiExceptions availableApiExceptions; @@ -219,47 +215,6 @@ return false; } - public synchronized boolean isDoubleInlineSelectedTarget(ProgramMethod method) { - return doubleInlineSelectedTargets.contains(method); - } - - synchronized boolean satisfiesRequirementsForDoubleInlining( - ProgramMethod method, ProgramMethod target, MethodProcessor methodProcessor) { - if (isDoubleInliningEnabled(methodProcessor)) { - // Don't perform the actual inlining if this was not selected. - return doubleInlineSelectedTargets.contains(target); - } - - // Just preparing for double inlining. - recordDoubleInliningCandidate(method, target, methodProcessor); - return false; - } - - synchronized void recordDoubleInliningCandidate( - ProgramMethod method, ProgramMethod target, MethodProcessor methodProcessor) { - if (isDoubleInliningEnabled(methodProcessor)) { - return; - } - - if (doubleInlineeCandidates.containsKey(target.getDefinition())) { - // Both calls can be inlined. - GraphLens currentGraphLens = appView.graphLens(); - ProgramMethod doubleInlineeCandidate = doubleInlineeCandidates.get(target.getDefinition()); - doubleInlineCallers.add(doubleInlineeCandidate, currentGraphLens); - doubleInlineCallers.add(method, currentGraphLens); - doubleInlineSelectedTargets.add(target); - } else { - // First call can be inlined. - doubleInlineeCandidates.put(target.getDefinition(), method); - } - } - - public void initializeDoubleInlineCallers(GraphLens graphLensForPrimaryOptimizationPass) { - assert appView.graphLens() == graphLensForPrimaryOptimizationPass; - doubleInlineCallers = - LongLivedProgramMethodSetBuilder.createForIdentitySet(graphLensForPrimaryOptimizationPass); - } - public void enqueueMethodsForReprocessing( PostMethodProcessor.Builder postMethodProcessorBuilder) { // The double inline callers are always rewritten up until the graph lens of the primary @@ -268,19 +223,7 @@ postMethodProcessorBuilder .getMethodsToReprocessBuilder() .rewrittenWithLens(appView) - .merge( - doubleInlineCallers - .rewrittenWithLens(appView) - .removeIf( - appView, - method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite())) - .merge( - singleInlineCallers - .rewrittenWithLens(appView) - .removeIf( - appView, - method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite())); - doubleInlineCallers = null; + .merge(singleInlineCallers); singleInlineCallers.clear(); } @@ -953,12 +896,11 @@ MethodProcessor methodProcessor, Timing timing, InliningReasonStrategy inliningReasonStrategy) { - InternalOptions options = appView.options(); + InlinerOptions options = appView.options().inlinerOptions(); DefaultInliningOracle oracle = createDefaultOracle( method, methodProcessor, - options.inliningInstructionLimit, options.inliningInstructionAllowance - numberOfInstructions(code), inliningReasonStrategy); InliningIRProvider inliningIRProvider = @@ -970,7 +912,7 @@ public InliningReasonStrategy createDefaultInliningReasonStrategy( MethodProcessor methodProcessor) { DefaultInliningReasonStrategy defaultInliningReasonStrategy = - new DefaultInliningReasonStrategy(appView, methodProcessor.getCallSiteInformation(), this); + new DefaultInliningReasonStrategy(appView, methodProcessor.getCallSiteInformation()); return appView.withGeneratedMessageLiteShrinker( ignore -> new ProtoInliningReasonStrategy(appView, defaultInliningReasonStrategy), defaultInliningReasonStrategy); @@ -979,12 +921,10 @@ public DefaultInliningOracle createDefaultOracle( ProgramMethod method, MethodProcessor methodProcessor, - int inliningInstructionLimit, int inliningInstructionAllowance) { return createDefaultOracle( method, methodProcessor, - inliningInstructionLimit, inliningInstructionAllowance, createDefaultInliningReasonStrategy(methodProcessor)); } @@ -992,7 +932,6 @@ public DefaultInliningOracle createDefaultOracle( ProgramMethod method, MethodProcessor methodProcessor, - int inliningInstructionLimit, int inliningInstructionAllowance, InliningReasonStrategy inliningReasonStrategy) { return new DefaultInliningOracle( @@ -1001,7 +940,6 @@ inliningReasonStrategy, method, methodProcessor, - inliningInstructionLimit, inliningInstructionAllowance); } @@ -1019,7 +957,7 @@ ClassInitializationAnalysis classInitializationAnalysis = new ClassInitializationAnalysis(appView, code); Deque<BasicBlock> inlineeStack = new ArrayDeque<>(); - InternalOptions options = appView.options(); + InlinerOptions options = appView.options().inlinerOptions(); while (blockIterator.hasNext()) { BasicBlock block = blockIterator.next(); if (!inlineeStack.isEmpty() && inlineeStack.peekFirst() == block) { @@ -1141,10 +1079,10 @@ if (inlinee.reason == Reason.SINGLE_CALLER) { assert converter.isInWave(); feedback.markInlinedIntoSingleCallSite(singleTargetMethod); - if (singleCallerInlinedMethods.isEmpty()) { + if (singleCallerInlinedMethodsInWave.isEmpty()) { converter.addWaveDoneAction(this::onWaveDone); } - singleCallerInlinedMethods + singleCallerInlinedMethodsInWave .computeIfAbsent( singleTarget.getHolder(), ignoreKey(ProgramMethodSet::createConcurrent)) .add(singleTarget); @@ -1317,18 +1255,23 @@ singleInlineCallers.add(method, appView.graphLens()); } - public void pruneMethod(ProgramMethod method) { + public void onMethodPruned(ProgramMethod method) { + onMethodCodePruned(method); + } + + public void onMethodCodePruned(ProgramMethod method) { singleInlineCallers.remove(method.getReference(), appView.graphLens()); } private void onWaveDone() { - singleCallerInlinedMethods.forEach( + singleCallerInlinedMethodsInWave.forEach( (clazz, singleCallerInlinedMethodsForClass) -> { // Convert and remove virtual single caller inlined methods to abstract or throw null. singleCallerInlinedMethodsForClass.removeIf( singleCallerInlinedMethod -> { if (singleCallerInlinedMethod.getDefinition().belongsToVirtualPool() || true) { singleCallerInlinedMethod.convertToAbstractOrThrowNullMethod(appView); + converter.onMethodCodePruned(singleCallerInlinedMethod); return true; } return false; @@ -1341,10 +1284,10 @@ .removeMethods( singleCallerInlinedMethodsForClass.toDefinitionSet( SetUtils::newIdentityHashSet)); - singleCallerInlinedMethodsForClass.forEach(converter::pruneMethod); + singleCallerInlinedMethodsForClass.forEach(converter::onMethodPruned); } }); - singleCallerInlinedMethods.clear(); + singleCallerInlinedMethodsInWave.clear(); } public static boolean verifyAllSingleCallerMethodsHaveBeenPruned(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java index c0da8fd..31801c6 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -1318,6 +1318,16 @@ } @Override + public void onMethodPruned(ProgramMethod method) { + onMethodCodePruned(method); + } + + @Override + public void onMethodCodePruned(ProgramMethod method) { + outlineCollection.remove(appView, method); + } + + @Override public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) { assert appView.graphLens() == graphLensForPrimaryOptimizationPass; assert outlineCollection == null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java index 262f8be..06e2b18 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerCostAnalysis.java
@@ -9,6 +9,9 @@ import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT; import static com.android.tools.r8.ir.code.Opcodes.RETURN; +import com.android.tools.r8.code.Iget; +import com.android.tools.r8.code.Iput; +import com.android.tools.r8.code.Return; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; @@ -75,7 +78,7 @@ IRCode inliningIR = inliningIRProvider.getAndCacheInliningIR(invoke, inlinee, false); int increment = inlinee.getDefinition().getCode().estimatedSizeForInlining() - - estimateNumberOfNonMaterializingInstructions(invoke, inliningIR); + - estimateSizeOfNonMaterializingInstructions(invoke, inliningIR); assert increment >= 0; if (exceedsInstructionBudgetAfterIncrement(increment)) { return true; @@ -93,7 +96,8 @@ private boolean exceedsInstructionBudgetAfterIncrement(int increment) { estimatedCost += increment; - return estimatedCost > appView.options().classInliningInstructionAllowance; + return estimatedCost + > appView.options().classInlinerOptions().getClassInliningInstructionAllowance(); } // TODO(b/143176500): Do not include instructions that will be canonicalized after inlining. @@ -101,7 +105,7 @@ // in the caller, which could then lead to a constant in the second inlinee being canonicalized. // TODO(b/143176500): Do not include instructions that will be dead code eliminated as a result of // constant arguments. - private int estimateNumberOfNonMaterializingInstructions(InvokeMethod invoke, IRCode inlinee) { + private int estimateSizeOfNonMaterializingInstructions(InvokeMethod invoke, IRCode inlinee) { int result = 0; Set<Value> receiverAliasesInInlinee = null; for (Instruction instruction : inlinee.instructions()) { @@ -123,13 +127,21 @@ receiverAliasesInInlinee = getReceiverAliasesInInlinee(invoke, inlinee); } if (receiverAliasesInInlinee.contains(root)) { - result++; + if (appView.options().isGeneratingClassFiles()) { + result++; + } else { + result += instruction.isInstanceGet() ? Iget.SIZE : Iput.SIZE; + } } break; case RETURN: // Wil not materialize after class inlining. - result++; + if (appView.options().isGeneratingClassFiles()) { + result++; + } else { + result += Return.SIZE; + } break; default:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java index 9320e54..e7edb01 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.GraphLens; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Phi; @@ -41,6 +42,16 @@ } @Override + public void onMethodPruned(ProgramMethod method) { + // Intentionally empty. + } + + @Override + public void onMethodCodePruned(ProgramMethod method) { + // Intentionally empty. + } + + @Override public void recordEnumState(DexProgramClass clazz, StaticFieldValues staticFieldValues) { // Intentionally empty. }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java index 2cdf22f..659e685 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.GraphLens; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.Phi; @@ -35,6 +36,10 @@ public abstract void analyzeEnums(IRCode code, MutableMethodConversionOptions conversionOptions); + public abstract void onMethodPruned(ProgramMethod method); + + public abstract void onMethodCodePruned(ProgramMethod method); + public abstract void recordEnumState(DexProgramClass clazz, StaticFieldValues staticFieldValues); public abstract Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java index 854a4e0..40e53fb 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -41,6 +41,7 @@ import com.android.tools.r8.graph.FieldResolutionResult; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues; import com.android.tools.r8.ir.analysis.type.ArrayTypeElement; @@ -642,17 +643,11 @@ .merge( dependencies .rewrittenWithLens(appView) - .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods()) - .removeIf( - appView, - method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite())) + .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods())) .merge( methodsDependingOnLibraryModelisation .rewrittenWithLens(appView) - .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods()) - .removeIf( - appView, - method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite())); + .removeAll(treeFixerResult.getPrunedItems().getRemovedMethods())); methodsDependingOnLibraryModelisation.clear(); updateOptimizationInfos(executorService, feedback, treeFixerResult); @@ -705,7 +700,10 @@ private void updateKeepInfo(Set<DexType> enumsToUnbox) { KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo(); - keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(enumsToUnbox)); + keepInfo.mutate( + mutator -> + mutator.removeKeepInfoForPrunedItems( + PrunedItems.builder().setRemovedClasses(enumsToUnbox).build())); } public EnumDataMap finishAnalysis() { @@ -1412,6 +1410,17 @@ } @Override + public void onMethodPruned(ProgramMethod method) { + onMethodCodePruned(method); + } + + @Override + public void onMethodCodePruned(ProgramMethod method) { + enumUnboxingCandidatesInfo.addPrunedMethod(method); + methodsDependingOnLibraryModelisation.remove(method.getReference(), appView.graphLens()); + } + + @Override public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) { // This has no effect during primary processing since the enumUnboxerRewriter is set // in between primary and post processing.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java index 386bcb0..39dd787 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AppView; 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.GraphLens; @@ -25,6 +26,7 @@ public class EnumUnboxingCandidateInfoCollection { private final Map<DexType, EnumUnboxingCandidateInfo> enumTypeToInfo = new ConcurrentHashMap<>(); + private final Set<DexMethod> prunedMethods = Sets.newIdentityHashSet(); public void addCandidate( AppView<AppInfoWithLiveness> appView, @@ -36,6 +38,10 @@ new EnumUnboxingCandidateInfo(appView, enumClass, graphLensForPrimaryOptimizationPass)); } + public void addPrunedMethod(ProgramMethod method) { + prunedMethods.add(method.getReference()); + } + public void removeCandidate(DexProgramClass enumClass) { removeCandidate(enumClass.getType()); } @@ -80,6 +86,7 @@ while (candidateInfoIterator.hasNext()) { allMethodDependencies.merge(candidateInfoIterator.next().methodDependencies); } + allMethodDependencies.removeAll(prunedMethods); return allMethodDependencies; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index 4959de0..15819c7 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -8,6 +8,7 @@ import static com.android.tools.r8.ir.code.Opcodes.ADD; import static com.android.tools.r8.ir.code.Opcodes.AND; import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT; +import static com.android.tools.r8.ir.code.Opcodes.ARRAY_GET; import static com.android.tools.r8.ir.code.Opcodes.ARRAY_LENGTH; import static com.android.tools.r8.ir.code.Opcodes.ASSUME; import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST; @@ -138,7 +139,7 @@ DexEncodedMethod definition = method.getDefinition(); identifyBridgeInfo(definition, code, feedback, timing); analyzeReturns(code, feedback, methodProcessor, timing); - if (options.enableInlining) { + if (options.inlinerOptions().enableInlining) { identifyInvokeSemanticsForInlining(definition, code, feedback, timing); } if (options.enableClassInlining) { @@ -471,6 +472,12 @@ } break; + case ARRAY_GET: + { + builder.setMayHaveOtherSideEffectsThanInstanceFieldAssignments(); + break; + } + default: builder .markAllFieldsAsRead()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java index 43b302b..ef00071 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -11,23 +11,21 @@ import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.conversion.CallSiteInformation; import com.android.tools.r8.ir.conversion.MethodProcessor; -import com.android.tools.r8.ir.optimize.Inliner; import com.android.tools.r8.ir.optimize.Inliner.Reason; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.InternalOptions.InlinerOptions; public class DefaultInliningReasonStrategy implements InliningReasonStrategy { private final AppView<AppInfoWithLiveness> appView; private final CallSiteInformation callSiteInformation; - private final Inliner inliner; + private final InlinerOptions options; public DefaultInliningReasonStrategy( - AppView<AppInfoWithLiveness> appView, - CallSiteInformation callSiteInformation, - Inliner inliner) { + AppView<AppInfoWithLiveness> appView, CallSiteInformation callSiteInformation) { this.appView = appView; this.callSiteInformation = callSiteInformation; - this.inliner = inliner; + this.options = appView.options().inlinerOptions(); } @Override @@ -46,7 +44,7 @@ && appView.withLiveness().appInfo().isAlwaysInlineMethod(targetReference)) { return Reason.ALWAYS; } - if (appView.options().disableInliningOfLibraryMethodOverrides + if (options.disableInliningOfLibraryMethodOverrides && targetMethod.isLibraryMethodOverride().isTrue()) { // This method will always have an implicit call site from the library, so we won't be able to // remove it after inlining even if we have single or dual call site information from the @@ -56,7 +54,8 @@ if (isSingleCallerInliningTarget(target)) { return Reason.SINGLE_CALLER; } - if (isDoubleInliningTarget(target, methodProcessor)) { + if (isDoubleInliningTarget(target)) { + assert methodProcessor.isPrimaryMethodProcessor(); return Reason.DUAL_CALLER; } return Reason.SIMPLE; @@ -69,21 +68,18 @@ if (appView.appInfo().isNeverInlineDueToSingleCallerMethod(method)) { return false; } - if (appView.options().testing.validInliningReasons != null - && !appView.options().testing.validInliningReasons.contains(Reason.SINGLE_CALLER)) { + if (appView.testing().validInliningReasons != null + && !appView.testing().validInliningReasons.contains(Reason.SINGLE_CALLER)) { return false; } return true; } - private boolean isDoubleInliningTarget(ProgramMethod candidate, MethodProcessor methodProcessor) { - if (methodProcessor.isPrimaryMethodProcessor() || methodProcessor.isPostMethodProcessor()) { - if (callSiteInformation.hasDoubleCallSite(candidate) - || inliner.isDoubleInlineSelectedTarget(candidate)) { - // 10 is found from measuring. - return candidate.getDefinition().getCode().estimatedSizeForInliningAtMost(10); - } - } - return false; + private boolean isDoubleInliningTarget(ProgramMethod candidate) { + return callSiteInformation.hasDoubleCallSite(candidate) + && candidate + .getDefinition() + .getCode() + .estimatedSizeForInliningAtMost(options.getDoubleInliningInstructionLimit()); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java index 78dc248..713322d 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
@@ -34,6 +34,11 @@ this.appliedGraphLens = graphLensForPrimaryOptimizationPass; } + public void remove(AppView<AppInfoWithLiveness> appView, ProgramMethod method) { + assert appView.graphLens() == appliedGraphLens; + outlines.remove(method.getReference()); + } + public void set( AppView<AppInfoWithLiveness> appView, ProgramMethod method, List<Outline> outlinesForMethod) { assert appView.graphLens() == appliedGraphLens; @@ -101,9 +106,7 @@ assert false; return; } - if (method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()) { - return; - } + assert !method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite(); for (Outline outline : outlinesForMethod) { methodsPerOutline.computeIfAbsent(outline, ignoreKey(ArrayList::new)).add(method); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java index a2bd90f..b3ed5f5 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.GraphLens; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.ir.optimize.OutlinerImpl; @@ -29,6 +30,16 @@ } @Override + public void onMethodPruned(ProgramMethod method) { + // Intentionally empty. + } + + @Override + public void onMethodCodePruned(ProgramMethod method) { + // Intentionally empty. + } + + @Override public void prepareForPrimaryOptimizationPass(GraphLens graphLensForPrimaryOptimizationPass) { // Intentionally empty. } @@ -52,6 +63,10 @@ public abstract void collectOutlineSites(IRCode code, Timing timing); + public abstract void onMethodPruned(ProgramMethod method); + + public abstract void onMethodCodePruned(ProgramMethod method); + public abstract void prepareForPrimaryOptimizationPass( GraphLens graphLensForPrimaryOptimizationPass);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java index 18a964f..13f384e 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -162,7 +162,7 @@ // Remove the method and notify other optimizations that the override has been removed to allow // the optimizations to fixup their state. method.getHolder().removeMethod(method.getReference()); - converter.pruneMethod(method); + converter.onMethodPruned(method); } private ProgramMethod resolveOnSuperClass(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java index e0de1d5..391f2c7 100644 --- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java +++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMember; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexString; @@ -52,7 +53,7 @@ public class IdentifierNameStringMarker { private final AppView<AppInfoWithLiveness> appView; - private final Object2BooleanMap<DexReference> identifierNameStrings; + private final Object2BooleanMap<DexMember<?, ?>> identifierNameStrings; public IdentifierNameStringMarker(AppView<AppInfoWithLiveness> appView) { this.appView = appView;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java index 922e198..158a8de 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -224,9 +224,13 @@ * * <p>Therefore, we assert that we only find a method state for direct methods. */ - public void pruneMethod(ProgramMethod method) { + public void onMethodPruned(ProgramMethod method) { assert codeScanner != null; MethodState methodState = codeScanner.getMethodStates().removeOrElse(method, null); assert methodState == null || method.getDefinition().belongsToDirectPool(); } + + public void onMethodCodePruned(ProgramMethod method) { + // Intentionally empty. + } }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java index 45630fa..6f3e907 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -58,6 +58,7 @@ import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.ListUtils; import com.android.tools.r8.utils.PredicateSet; +import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.Visibility; import com.android.tools.r8.utils.WorkList; @@ -68,12 +69,16 @@ import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; import it.unimi.dsi.fastutil.objects.Object2BooleanMap; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -179,13 +184,13 @@ * All methods and fields whose value *must* never be propagated due to a configuration directive. * (testing only). */ - private final Set<DexReference> neverPropagateValue; + private final Set<DexMember<?, ?>> neverPropagateValue; /** * All items with -identifiernamestring rule. Bound boolean value indicates the rule is explicitly * specified by users (<code>true</code>) or not, i.e., implicitly added by R8 (<code>false</code> * ). */ - public final Object2BooleanMap<DexReference> identifierNameStrings; + public final Object2BooleanMap<DexMember<?, ?>> identifierNameStrings; /** A set of types that have been removed by the {@link TreePruner}. */ final Set<DexType> prunedTypes; /** A map from switchmap class types to their corresponding switchmaps. */ @@ -230,8 +235,8 @@ Set<DexType> noClassMerging, Set<DexType> noVerticalClassMerging, Set<DexType> noHorizontalClassMerging, - Set<DexReference> neverPropagateValue, - Object2BooleanMap<DexReference> identifierNameStrings, + Set<DexMember<?, ?>> neverPropagateValue, + Object2BooleanMap<DexMember<?, ?>> identifierNameStrings, Set<DexType> prunedTypes, Map<DexField, Int2ReferenceMap<DexField>> switchMaps, Set<DexType> lockCandidates, @@ -283,7 +288,7 @@ previous.getMainDexInfo(), previous.deadProtoTypes, previous.getMissingClasses(), - CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedProgramTypes()), + CollectionUtils.addAll(previous.liveTypes, committedItems.getCommittedProgramTypes()), previous.targetedMethods, previous.failedMethodResolutionTargets, previous.failedFieldResolutionTargets, @@ -320,52 +325,208 @@ previous.initClassReferences); } - private AppInfoWithLiveness(AppInfoWithLiveness previous, PrunedItems prunedItems) { + private AppInfoWithLiveness( + AppInfoWithLiveness previous, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { this( previous.getSyntheticItems().commitPrunedItems(prunedItems), previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems), previous.getMainDexInfo().withoutPrunedItems(prunedItems), previous.deadProtoTypes, previous.getMissingClasses(), - prunedItems.hasRemovedClasses() - ? Sets.difference(previous.liveTypes, prunedItems.getRemovedClasses()) - : previous.liveTypes, - previous.targetedMethods, - previous.failedMethodResolutionTargets, - previous.failedFieldResolutionTargets, - previous.bootstrapMethods, - previous.methodsTargetedByInvokeDynamic, - previous.virtualMethodsTargetedByInvokeDirect, - previous.liveMethods, + pruneClasses(previous.liveTypes, prunedItems, executorService, futures), + pruneMethods(previous.targetedMethods, prunedItems, executorService, futures), + pruneMethods(previous.failedMethodResolutionTargets, prunedItems, executorService, futures), + pruneFields(previous.failedFieldResolutionTargets, prunedItems, executorService, futures), + pruneMethods(previous.bootstrapMethods, prunedItems, executorService, futures), + pruneMethods( + previous.methodsTargetedByInvokeDynamic, prunedItems, executorService, futures), + pruneMethods( + previous.virtualMethodsTargetedByInvokeDirect, prunedItems, executorService, futures), + pruneMethods(previous.liveMethods, prunedItems, executorService, futures), previous.fieldAccessInfoCollection, previous.methodAccessInfoCollection, previous.objectAllocationInfoCollection, previous.callSites, extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()), previous.mayHaveSideEffects, - previous.noSideEffects, - previous.assumedValues, - previous.alwaysInline, - previous.neverInline, - previous.neverInlineDueToSingleCaller, - previous.whyAreYouNotInlining, - previous.keepConstantArguments, - previous.keepUnusedArguments, - previous.reprocess, - previous.neverReprocess, + pruneMapFromMembers(previous.noSideEffects, prunedItems, executorService, futures), + pruneMapFromMembers(previous.assumedValues, prunedItems, executorService, futures), + pruneMethods(previous.alwaysInline, prunedItems, executorService, futures), + pruneMethods(previous.neverInline, prunedItems, executorService, futures), + pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures), + pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures), + pruneMethods(previous.keepConstantArguments, prunedItems, executorService, futures), + pruneMethods(previous.keepUnusedArguments, prunedItems, executorService, futures), + pruneMethods(previous.reprocess, prunedItems, executorService, futures), + pruneMethods(previous.neverReprocess, prunedItems, executorService, futures), previous.alwaysClassInline, - previous.neverClassInline, - previous.noClassMerging, - previous.noVerticalClassMerging, - previous.noHorizontalClassMerging, - previous.neverPropagateValue, - previous.identifierNameStrings, + pruneClasses(previous.neverClassInline, prunedItems, executorService, futures), + pruneClasses(previous.noClassMerging, prunedItems, executorService, futures), + pruneClasses(previous.noVerticalClassMerging, prunedItems, executorService, futures), + pruneClasses(previous.noHorizontalClassMerging, prunedItems, executorService, futures), + pruneMembers(previous.neverPropagateValue, prunedItems, executorService, futures), + pruneMapFromMembers(previous.identifierNameStrings, prunedItems, executorService, futures), prunedItems.hasRemovedClasses() ? CollectionUtils.mergeSets(previous.prunedTypes, prunedItems.getRemovedClasses()) : previous.prunedTypes, previous.switchMaps, - previous.lockCandidates, - previous.initClassReferences); + pruneClasses(previous.lockCandidates, prunedItems, executorService, futures), + pruneMapFromClasses(previous.initClassReferences, prunedItems, executorService, futures)); + } + + private static Set<DexType> pruneClasses( + Set<DexType> methods, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { + return pruneItems(methods, prunedItems.getRemovedClasses(), executorService, futures); + } + + private static Set<DexField> pruneFields( + Set<DexField> fields, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { + return pruneItems(fields, prunedItems.getRemovedFields(), executorService, futures); + } + + private static Set<DexMember<?, ?>> pruneMembers( + Set<DexMember<?, ?>> members, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { + if (prunedItems.hasRemovedMembers()) { + futures.add( + ThreadUtils.processAsynchronously( + () -> { + Set<DexField> removedFields = prunedItems.getRemovedFields(); + Set<DexMethod> removedMethods = prunedItems.getRemovedMethods(); + if (members.size() <= removedFields.size() + removedMethods.size()) { + members.removeIf( + member -> + member.isDexField() + ? removedFields.contains(member.asDexField()) + : removedMethods.contains(member.asDexMethod())); + } else { + removedFields.forEach(members::remove); + removedMethods.forEach(members::remove); + } + }, + executorService)); + } + return members; + } + + private static Set<DexMethod> pruneMethods( + Set<DexMethod> methods, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { + return pruneItems(methods, prunedItems.getRemovedMethods(), executorService, futures); + } + + private static <T> Set<T> pruneItems( + Set<T> items, Set<T> removedItems, ExecutorService executorService, List<Future<?>> futures) { + if (!removedItems.isEmpty()) { + futures.add( + ThreadUtils.processAsynchronously( + () -> { + if (items.size() <= removedItems.size()) { + items.removeAll(removedItems); + } else { + removedItems.forEach(items::remove); + } + }, + executorService)); + } + return items; + } + + private static <V> Map<DexType, V> pruneMapFromClasses( + Map<DexType, V> map, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { + return pruneMap(map, prunedItems.getRemovedClasses(), executorService, futures); + } + + private static <V> Map<DexMember<?, ?>, V> pruneMapFromMembers( + Map<DexMember<?, ?>, V> map, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { + if (prunedItems.hasRemovedMembers()) { + futures.add( + ThreadUtils.processAsynchronously( + () -> { + Set<DexField> removedFields = prunedItems.getRemovedFields(); + Set<DexMethod> removedMethods = prunedItems.getRemovedMethods(); + if (map.size() <= removedFields.size() + removedMethods.size()) { + map.keySet() + .removeIf( + member -> + member.isDexField() + ? removedFields.contains(member.asDexField()) + : removedMethods.contains(member.asDexMethod())); + } else { + removedFields.forEach(map::remove); + removedMethods.forEach(map::remove); + } + }, + executorService)); + } + return map; + } + + private static Object2BooleanMap<DexMember<?, ?>> pruneMapFromMembers( + Object2BooleanMap<DexMember<?, ?>> map, + PrunedItems prunedItems, + ExecutorService executorService, + List<Future<?>> futures) { + if (prunedItems.hasRemovedMembers()) { + futures.add( + ThreadUtils.processAsynchronously( + () -> { + Set<DexField> removedFields = prunedItems.getRemovedFields(); + Set<DexMethod> removedMethods = prunedItems.getRemovedMethods(); + if (map.size() <= removedFields.size() + removedMethods.size()) { + map.keySet() + .removeIf( + member -> + member.isDexField() + ? removedFields.contains(member.asDexField()) + : removedMethods.contains(member.asDexMethod())); + } else { + removedFields.forEach(map::remove); + removedMethods.forEach(map::remove); + } + }, + executorService)); + } + return map; + } + + private static <K, V> Map<K, V> pruneMap( + Map<K, V> map, + Set<K> removedItems, + ExecutorService executorService, + List<Future<?>> futures) { + if (!removedItems.isEmpty()) { + futures.add( + ThreadUtils.processAsynchronously( + () -> { + if (map.size() <= removedItems.size()) { + map.keySet().removeAll(removedItems); + } else { + removedItems.forEach(map::remove); + } + }, + executorService)); + } + return map; } private boolean verify() { @@ -1038,7 +1199,8 @@ * DexApplication object. */ @Override - public AppInfoWithLiveness prunedCopyFrom(PrunedItems prunedItems) { + public AppInfoWithLiveness prunedCopyFrom( + PrunedItems prunedItems, ExecutorService executorService) throws ExecutionException { assert getClass() == AppInfoWithLiveness.class; assert checkIfObsolete(); if (prunedItems.isEmpty()) { @@ -1049,14 +1211,15 @@ // Rebuild the hierarchy. objectAllocationInfoCollection.mutate( mutator -> mutator.removeAllocationsForPrunedItems(prunedItems), this); - keepInfo.mutate( - keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedClasses())); + keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems)); + } else if (prunedItems.hasRemovedMembers()) { + keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems)); } - if (!prunedItems.getRemovedMethods().isEmpty()) { - keepInfo.mutate( - keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedMethods())); - } - return new AppInfoWithLiveness(this, prunedItems); + List<Future<?>> futures = new ArrayList<>(); + AppInfoWithLiveness appInfoWithLiveness = + new AppInfoWithLiveness(this, prunedItems, executorService, futures); + ThreadUtils.awaitFutures(futures); + return appInfoWithLiveness; } public AppInfoWithLiveness rebuildWithLiveness(CommittedItems committedItems) { @@ -1084,14 +1247,14 @@ getMainDexInfo().rewrittenWithLens(getSyntheticItems(), lens), deadProtoTypes, getMissingClasses().commitSyntheticItems(committedItems), - lens.rewriteTypes(liveTypes), - lens.rewriteMethods(targetedMethods), - lens.rewriteMethods(failedMethodResolutionTargets), - lens.rewriteFields(failedFieldResolutionTargets), - lens.rewriteMethods(bootstrapMethods), - lens.rewriteMethods(methodsTargetedByInvokeDynamic), - lens.rewriteMethods(virtualMethodsTargetedByInvokeDirect), - lens.rewriteMethods(liveMethods), + lens.rewriteReferences(liveTypes), + lens.rewriteReferences(targetedMethods), + lens.rewriteReferences(failedMethodResolutionTargets), + lens.rewriteReferences(failedFieldResolutionTargets), + lens.rewriteReferences(bootstrapMethods), + lens.rewriteReferences(methodsTargetedByInvokeDynamic), + lens.rewriteReferences(virtualMethodsTargetedByInvokeDirect), + lens.rewriteReferences(liveMethods), fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens), methodAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens), objectAllocationInfoCollection.rewrittenWithLens(definitionSupplier, lens), @@ -1102,25 +1265,25 @@ // Drop assume rules in case of collisions. lens.rewriteReferenceKeys(noSideEffects, rules -> null), lens.rewriteReferenceKeys(assumedValues, rules -> null), - lens.rewriteMethods(alwaysInline), - lens.rewriteMethods(neverInline), - lens.rewriteMethods(neverInlineDueToSingleCaller), - lens.rewriteMethods(whyAreYouNotInlining), - lens.rewriteMethods(keepConstantArguments), - lens.rewriteMethods(keepUnusedArguments), - lens.rewriteMethods(reprocess), - lens.rewriteMethods(neverReprocess), + lens.rewriteReferences(alwaysInline), + lens.rewriteReferences(neverInline), + lens.rewriteReferences(neverInlineDueToSingleCaller), + lens.rewriteReferences(whyAreYouNotInlining), + lens.rewriteReferences(keepConstantArguments), + lens.rewriteReferences(keepUnusedArguments), + lens.rewriteReferences(reprocess), + lens.rewriteReferences(neverReprocess), alwaysClassInline.rewriteItems(lens::lookupType), - lens.rewriteTypes(neverClassInline), - lens.rewriteTypes(noClassMerging), - lens.rewriteTypes(noVerticalClassMerging), - lens.rewriteTypes(noHorizontalClassMerging), + lens.rewriteReferences(neverClassInline), + lens.rewriteReferences(noClassMerging), + lens.rewriteReferences(noVerticalClassMerging), + lens.rewriteReferences(noHorizontalClassMerging), lens.rewriteReferences(neverPropagateValue), lens.rewriteReferenceKeys(identifierNameStrings), // Don't rewrite pruned types - the removed types are identified by their original name. prunedTypes, lens.rewriteFieldKeys(switchMaps), - lens.rewriteTypes(lockCandidates), + lens.rewriteReferences(lockCandidates), rewriteInitClassReferences(lens)); }
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 ec6c061..a7b0a76 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -261,7 +261,7 @@ private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection; private final Map<DexCallSite, ProgramMethodSet> callSites = new IdentityHashMap<>(); - private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet(); + private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet(); private final AndroidApiLevelCompute apiLevelCompute; @@ -3830,13 +3830,13 @@ return builder.build(); } - private static Object2BooleanMap<DexReference> joinIdentifierNameStrings( - Set<DexReference> explicit, Set<DexReference> implicit) { - Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>(); - for (DexReference e : explicit) { + private static Object2BooleanMap<DexMember<?, ?>> joinIdentifierNameStrings( + Set<DexMember<?, ?>> explicit, Set<DexMember<?, ?>> implicit) { + Object2BooleanMap<DexMember<?, ?>> result = new Object2BooleanArrayMap<>(); + for (DexMember<?, ?> e : explicit) { result.putIfAbsent(e, true); } - for (DexReference i : implicit) { + for (DexMember<?, ?> i : implicit) { result.putIfAbsent(i, false); } return result;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java index c260f3e..36555ac 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -23,6 +23,7 @@ import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMember; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.shaking.KeepFieldInfo.Joiner; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.MapUtils; @@ -244,20 +245,16 @@ this.methodRuleInstances = methodRuleInstances; } - public void removeKeepInfoForPrunedItems(Set<? extends DexReference> removedReferences) { - keepClassInfo.keySet().removeIf(removedReferences::contains); - keepFieldInfo - .keySet() - .removeIf( - field -> - (removedReferences.contains(field) - || removedReferences.contains(field.getHolderType()))); - keepMethodInfo - .keySet() - .removeIf( - method -> - (removedReferences.contains(method) - || removedReferences.contains(method.getHolderType()))); + public void removeKeepInfoForPrunedItems(PrunedItems prunedItems) { + if (prunedItems.hasRemovedClasses()) { + keepClassInfo.keySet().removeAll(prunedItems.getRemovedClasses()); + } + if (prunedItems.hasRemovedClasses() || prunedItems.hasRemovedFields()) { + keepFieldInfo.keySet().removeIf(prunedItems::isRemoved); + } + if (prunedItems.hasRemovedClasses() || prunedItems.hasRemovedMembers()) { + keepMethodInfo.keySet().removeIf(prunedItems::isRemoved); + } } @Override
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 3dc1bc5..b105a80 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -122,14 +122,14 @@ private final Set<DexType> noUnusedInterfaceRemoval = Sets.newIdentityHashSet(); private final Set<DexType> noVerticalClassMerging = Sets.newIdentityHashSet(); private final Set<DexType> noHorizontalClassMerging = Sets.newIdentityHashSet(); - private final Set<DexReference> neverPropagateValue = Sets.newIdentityHashSet(); + private final Set<DexMember<?, ?>> neverPropagateValue = Sets.newIdentityHashSet(); private final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule = new IdentityHashMap<>(); private final Map<DexReference, ProguardMemberRule> mayHaveSideEffects = new IdentityHashMap<>(); private final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects = new IdentityHashMap<>(); private final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues = new IdentityHashMap<>(); - private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet(); + private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet(); private final Queue<DelayedRootSetActionItem> delayedRootSetActionItems = new ConcurrentLinkedQueue<>(); private final InternalOptions options; @@ -1612,11 +1612,11 @@ public final Set<DexType> noUnusedInterfaceRemoval; public final Set<DexType> noVerticalClassMerging; public final Set<DexType> noHorizontalClassMerging; - public final Set<DexReference> neverPropagateValue; + public final Set<DexMember<?, ?>> neverPropagateValue; public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects; public final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects; public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues; - public final Set<DexReference> identifierNameStrings; + public final Set<DexMember<?, ?>> identifierNameStrings; public final Set<ProguardIfRule> ifRules; private RootSet( @@ -1636,12 +1636,12 @@ Set<DexType> noUnusedInterfaceRemoval, Set<DexType> noVerticalClassMerging, Set<DexType> noHorizontalClassMerging, - Set<DexReference> neverPropagateValue, + Set<DexMember<?, ?>> neverPropagateValue, Map<DexReference, ProguardMemberRule> mayHaveSideEffects, Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects, Map<DexMember<?, ?>, ProguardMemberRule> assumedValues, Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule, - Set<DexReference> identifierNameStrings, + Set<DexMember<?, ?>> identifierNameStrings, Set<ProguardIfRule> ifRules, List<DelayedRootSetActionItem> delayedRootSetActionItems, ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index 02c89bf..e157ff8 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -48,6 +48,7 @@ import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.graph.ObjectAllocationInfoCollection; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.graph.RewrittenPrototypeDescription; import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.graph.TopDownClassHierarchyTraversal; @@ -717,7 +718,10 @@ appView, lensBuilder, verticallyMergedClasses, synthesizedBridges) .fixupTypeReferences(); KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo(); - keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.keySet())); + keepInfo.mutate( + mutator -> + mutator.removeKeepInfoForPrunedItems( + PrunedItems.builder().setRemovedClasses(mergedClasses.keySet()).build())); timing.end(); assert lens != null; @@ -1867,7 +1871,7 @@ } private AbortReason disallowInlining(ProgramMethod method, DexProgramClass context) { - if (appView.options().enableInlining) { + if (appView.options().inlinerOptions().enableInlining) { Code code = method.getDefinition().getCode(); if (code.isCfCode()) { CfCode cfCode = code.asCfCode();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java index 53214f3..0ce464d 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -54,6 +54,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -152,7 +154,8 @@ this.committed = committed; } - public static void finalize(AppView<AppInfo> appView) { + public static void finalize(AppView<AppInfo> appView, ExecutorService executorService) + throws ExecutionException { assert !appView.appInfo().hasClassHierarchy(); assert !appView.appInfo().hasLiveness(); Result result = appView.getSyntheticItems().computeFinalSynthetics(appView); @@ -168,10 +171,12 @@ .rewrittenWithLens(appView.getSyntheticItems(), result.lens))); appView.setGraphLens(result.lens); } - appView.pruneItems(result.prunedItems); + appView.pruneItems(result.prunedItems, executorService); } - public static void finalizeWithClassHierarchy(AppView<AppInfoWithClassHierarchy> appView) { + public static void finalizeWithClassHierarchy( + AppView<AppInfoWithClassHierarchy> appView, ExecutorService executorService) + throws ExecutionException { assert !appView.appInfo().hasLiveness(); Result result = appView.getSyntheticItems().computeFinalSynthetics(appView); appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit)); @@ -187,10 +192,12 @@ .getMainDexInfo() .rewrittenWithLens(appView.getSyntheticItems(), result.lens))); } - appView.pruneItems(result.prunedItems); + appView.pruneItems(result.prunedItems, executorService); } - public static void finalizeWithLiveness(AppView<AppInfoWithLiveness> appView) { + public static void finalizeWithLiveness( + AppView<AppInfoWithLiveness> appView, ExecutorService executorService) + throws ExecutionException { Result result = appView.getSyntheticItems().computeFinalSynthetics(appView); appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo)); if (result.lens != null) { @@ -199,7 +206,7 @@ assert result.commit.getApplication() == appView.appInfo().app(); } appView.setAppInfo(appView.appInfo().rebuildWithLiveness(result.commit)); - appView.pruneItems(result.prunedItems); + appView.pruneItems(result.prunedItems, executorService); } Result computeFinalSynthetics(AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java index 6af263f..c0e8750 100644 --- a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java +++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -13,6 +13,11 @@ public class CollectionUtils { + public static <S, T extends Collection<S>> T addAll(T collection, Collection<S> elementsToAdd) { + collection.addAll(elementsToAdd); + return collection; + } + public static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) { ImmutableSet.Builder<T> builder = ImmutableSet.builder(); builder.addAll(first);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java index 24e9259..78aab7d 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -216,7 +216,7 @@ } void enableProtoShrinking() { - applyInliningToInlinee = true; + inlinerOptions.applyInliningToInlinee = true; enableFieldBitAccessAnalysis = true; protoShrinking.enableGeneratedMessageLiteShrinking = true; protoShrinking.enableGeneratedMessageLiteBuilderShrinking = true; @@ -231,7 +231,7 @@ } public void disableGlobalOptimizations() { - enableInlining = false; + inlinerOptions.enableInlining = false; enableClassInlining = false; enableClassStaticizer = false; enableDevirtualization = false; @@ -272,17 +272,7 @@ public boolean enableVerticalClassMerging = true; public boolean enableUnusedInterfaceRemoval = true; public boolean enableDevirtualization = true; - public boolean enableInlining = - !Version.isDevelopmentVersion() - || System.getProperty("com.android.tools.r8.disableinlining") == null; public boolean enableEnumUnboxing = true; - // TODO(b/141451716): Evaluate the effect of allowing inlining in the inlinee. - public boolean applyInliningToInlinee = - System.getProperty("com.android.tools.r8.applyInliningToInlinee") != null; - public int applyInliningToInlineeMaxDepth = 0; - public boolean enableInliningOfInvokesWithClassInitializationSideEffects = true; - public boolean enableInliningOfInvokesWithNullableReceivers = true; - public boolean disableInliningOfLibraryMethodOverrides = true; public boolean enableSimpleInliningConstraints = true; public final int simpleInliningConstraintThreshold = 0; public boolean enableClassInlining = true; @@ -318,25 +308,6 @@ return 16383; } - // TODO(b/141719453): The inlining limit at least should be consistent with normal inlining. - public int classInliningInstructionLimit = 10; - public int classInliningInstructionAllowance = 50; - // This defines the limit of instructions in the inlinee - public int inliningInstructionLimit = - !Version.isDevelopmentVersion() - ? 3 - : System.getProperty("com.android.tools.r8.inliningInstructionLimit") != null - ? Integer.parseInt( - System.getProperty("com.android.tools.r8.inliningInstructionLimit")) - : 3; - // This defines how many instructions of inlinees we can inlinee overall. - public int inliningInstructionAllowance = 1500; - // Maximum number of distinct values in a method that may be used in a monitor-enter instruction. - public int inliningMonitorEnterValuesAllowance = 4; - // Maximum number of control flow resolution blocks that setup the register state before - // the actual catch handler allowed when inlining. Threshold found empirically by testing on - // GMS Core. - public int inliningControlFlowResolutionBlocksThreshold = 15; public boolean enableSwitchRewriting = true; public boolean enableStringSwitchConversion = true; public int minimumStringSwitchSize = 3; @@ -721,6 +692,8 @@ private final CallSiteOptimizationOptions callSiteOptimizationOptions = new CallSiteOptimizationOptions(); + private final ClassInlinerOptions classInlinerOptions = new ClassInlinerOptions(); + private final InlinerOptions inlinerOptions = new InlinerOptions(); private final HorizontalClassMergerOptions horizontalClassMergerOptions = new HorizontalClassMergerOptions(); private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions(); @@ -747,6 +720,14 @@ return callSiteOptimizationOptions; } + public ClassInlinerOptions classInlinerOptions() { + return classInlinerOptions; + } + + public InlinerOptions inlinerOptions() { + return inlinerOptions; + } + public HorizontalClassMergerOptions horizontalClassMergerOptions() { return horizontalClassMergerOptions; } @@ -796,6 +777,24 @@ return ImmutableSet.of(); } + private static boolean isSystemPropertyForDevelopmentSet(String propertyName) { + if (Version.isDevelopmentVersion()) { + return System.getProperty(propertyName) != null; + } + return false; + } + + private static int parseSystemPropertyForDevelopmentOrDefault( + String propertyName, int defaultValue) { + if (Version.isDevelopmentVersion()) { + String propertyValue = System.getProperty(propertyName); + if (propertyValue != null) { + return Integer.parseInt(propertyValue); + } + } + return defaultValue; + } + public static class InvalidParameterAnnotationInfo { final DexMethod method; @@ -1300,6 +1299,87 @@ } } + public class ClassInlinerOptions { + + public int classInliningInstructionAllowance = -1; + + public int getClassInliningInstructionAllowance() { + if (classInliningInstructionAllowance >= 0) { + return classInliningInstructionAllowance; + } + if (isGeneratingClassFiles()) { + return 50; + } + assert isGeneratingDex(); + return 65; + } + } + + public class InlinerOptions { + + public boolean enableInlining = + !isSystemPropertyForDevelopmentSet("com.android.tools.r8.disableinlining"); + + // This defines the limit of instructions in the inlinee + public int simpleInliningInstructionLimit = + parseSystemPropertyForDevelopmentOrDefault( + "com.android.tools.r8.inliningInstructionLimit", -1); + + // This defines the limit of instructions in the inlinee + public int doubleInliningInstructionLimit = + parseSystemPropertyForDevelopmentOrDefault( + "com.android.tools.r8.doubleInliningInstructionLimit", -1); + + // This defines how many instructions of inlinees we can inlinee overall. + public int inliningInstructionAllowance = 1500; + + // Maximum number of distinct values in a method that may be used in a monitor-enter + // instruction. + public int inliningMonitorEnterValuesAllowance = 4; + + // Maximum number of control flow resolution blocks that setup the register state before + // the actual catch handler allowed when inlining. Threshold found empirically by testing on + // GMS Core. + public int inliningControlFlowResolutionBlocksThreshold = 15; + + // TODO(b/141451716): Evaluate the effect of allowing inlining in the inlinee. + public boolean applyInliningToInlinee = + System.getProperty("com.android.tools.r8.applyInliningToInlinee") != null; + public int applyInliningToInlineeMaxDepth = 0; + + public boolean enableInliningOfInvokesWithClassInitializationSideEffects = true; + public boolean enableInliningOfInvokesWithNullableReceivers = true; + public boolean disableInliningOfLibraryMethodOverrides = true; + + public int getSimpleInliningInstructionLimit() { + // If a custom simple inlining instruction limit is set, then use that. + if (simpleInliningInstructionLimit >= 0) { + return simpleInliningInstructionLimit; + } + // Allow 3 instructions when generating to class files. + if (isGeneratingClassFiles()) { + return 3; + } + // Allow the size of the dex code to be up to 5 bytes. + assert isGeneratingDex(); + return 5; + } + + public int getDoubleInliningInstructionLimit() { + // If a custom double inlining instruction limit is set, then use that. + if (doubleInliningInstructionLimit >= 0) { + return doubleInliningInstructionLimit; + } + // Allow 10 instructions when generating to class files. + if (isGeneratingClassFiles()) { + return 10; + } + // Allow the size of the dex code to be up to 20 bytes. + assert isGeneratingDex(); + return 20; + } + } + public class HorizontalClassMergerOptions { // TODO(b/138781768): Set enable to true when this bug is resolved. @@ -1342,7 +1422,7 @@ return false; } if (mode.isInitial()) { - return enableInlining && isShrinking(); + return inlinerOptions.enableInlining && isShrinking(); } assert mode.isFinal(); return true;
diff --git a/src/main/java/com/android/tools/r8/utils/MapIdTemplateProvider.java b/src/main/java/com/android/tools/r8/utils/MapIdTemplateProvider.java new file mode 100644 index 0000000..335ca09 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/MapIdTemplateProvider.java
@@ -0,0 +1,72 @@ +// Copyright (c) 2021, 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.utils; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.MapIdEnvironment; +import com.android.tools.r8.MapIdProvider; +import com.google.common.collect.ImmutableMap; +import java.util.Map; + +public class MapIdTemplateProvider implements MapIdProvider { + + private static final char VARIABLE_PREFIX = '%'; + + private static final Map<String, MapIdProvider> HANDLERS = + ImmutableMap.<String, MapIdProvider>builder() + .put(var("MAP_HASH"), MapIdEnvironment::getMapHash) + .build(); + + private static String var(String name) { + return VARIABLE_PREFIX + name; + } + + private static int getMaxVariableLength() { + int max = 0; + for (String key : HANDLERS.keySet()) { + max = Math.max(max, key.length()); + } + return max; + } + + public static MapIdProvider create(String template, DiagnosticsHandler handler) { + String cleaned = template; + for (String variable : HANDLERS.keySet()) { + // Maintain the same size as template so indexing remains valid for error reporting. + cleaned = cleaned.replace(variable, ' ' + variable.substring(1)); + } + assert template.length() == cleaned.length(); + int unhandled = cleaned.indexOf(VARIABLE_PREFIX); + if (unhandled >= 0) { + while (unhandled >= 0) { + int endIndex = Math.min(unhandled + getMaxVariableLength(), template.length()); + String variablePrefix = template.substring(unhandled, endIndex); + handler.error( + new StringDiagnostic("Invalid template variable starting with " + variablePrefix)); + unhandled = cleaned.indexOf(VARIABLE_PREFIX, unhandled + 1); + } + return null; + } + return new MapIdTemplateProvider(template); + } + + private final String template; + private String cachedValue = null; + + private MapIdTemplateProvider(String template) { + this.template = template; + } + + @Override + public String get(MapIdEnvironment environment) { + if (cachedValue == null) { + cachedValue = template; + HANDLERS.forEach( + (variable, getter) -> { + cachedValue = cachedValue.replace(variable, getter.get(environment)); + }); + } + return cachedValue; + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/SourceFileTemplateProvider.java b/src/main/java/com/android/tools/r8/utils/SourceFileTemplateProvider.java new file mode 100644 index 0000000..b1c3cda --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/SourceFileTemplateProvider.java
@@ -0,0 +1,73 @@ +// Copyright (c) 2021, 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.utils; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.SourceFileEnvironment; +import com.android.tools.r8.SourceFileProvider; +import com.google.common.collect.ImmutableMap; +import java.util.Map; + +public class SourceFileTemplateProvider implements SourceFileProvider { + + private static final char VARIABLE_PREFIX = '%'; + + private static final Map<String, SourceFileProvider> HANDLERS = + ImmutableMap.<String, SourceFileProvider>builder() + .put(var("MAP_ID"), SourceFileEnvironment::getMapId) + .put(var("MAP_HASH"), SourceFileEnvironment::getMapHash) + .build(); + + private static String var(String name) { + return VARIABLE_PREFIX + name; + } + + private static int getMaxVariableLength() { + int max = 0; + for (String key : HANDLERS.keySet()) { + max = Math.max(max, key.length()); + } + return max; + } + + public static SourceFileProvider create(String template, DiagnosticsHandler handler) { + String cleaned = template; + for (String variable : HANDLERS.keySet()) { + // Maintain the same size as template so indexing remains valid for error reporting. + cleaned = cleaned.replace(variable, ' ' + variable.substring(1)); + } + assert template.length() == cleaned.length(); + int unhandled = cleaned.indexOf(VARIABLE_PREFIX); + if (unhandled >= 0) { + while (unhandled >= 0) { + int endIndex = Math.min(unhandled + getMaxVariableLength(), template.length()); + String variablePrefix = template.substring(unhandled, endIndex); + handler.error( + new StringDiagnostic("Invalid template variable starting with " + variablePrefix)); + unhandled = cleaned.indexOf(VARIABLE_PREFIX, unhandled + 1); + } + return null; + } + return new SourceFileTemplateProvider(template); + } + + private final String template; + private String cachedValue = null; + + private SourceFileTemplateProvider(String template) { + this.template = template; + } + + @Override + public String get(SourceFileEnvironment environment) { + if (cachedValue == null) { + cachedValue = template; + HANDLERS.forEach( + (variable, getter) -> { + cachedValue = cachedValue.replace(variable, getter.get(environment)); + }); + } + return cachedValue; + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java index a8fa794..3ae44fb 100644 --- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -20,6 +20,16 @@ public static final int NOT_SPECIFIED = -1; public static <T> Future<T> processAsynchronously( + Action action, ExecutorService executorService) { + return processAsynchronously( + () -> { + action.execute(); + return null; + }, + executorService); + } + + public static <T> Future<T> processAsynchronously( Callable<T> callable, ExecutorService executorService) { return executorService.submit(callable); }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HasherWrapper.java b/src/main/java/com/android/tools/r8/utils/structural/HasherWrapper.java index 8427cfe..112aace 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/HasherWrapper.java +++ b/src/main/java/com/android/tools/r8/utils/structural/HasherWrapper.java
@@ -75,6 +75,7 @@ hasher.putBytes(content); } + @SuppressWarnings("unchecked") @Override public <T> T hash() { return (T) hasher.hash();
diff --git a/src/test/java/com/android/tools/r8/AlwaysClassInline.java b/src/test/java/com/android/tools/r8/AlwaysClassInline.java new file mode 100644 index 0000000..101bdf1 --- /dev/null +++ b/src/test/java/com/android/tools/r8/AlwaysClassInline.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2021, 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target({ElementType.TYPE}) +public @interface AlwaysClassInline {}
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java index 03ba4b8..c0665cf 100644 --- a/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java +++ b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
@@ -7,12 +7,14 @@ import com.android.tools.r8.debug.DebugTestConfig; import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.InternalOptions; import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.function.Consumer; public class GenerateMainDexListTestBuilder extends TestBaseBuilder< @@ -22,6 +24,10 @@ GenerateMainDexListRunResult, GenerateMainDexListTestBuilder> { + public static final Consumer<InternalOptions> DEFAULT_OPTIONS = options -> {}; + + private Consumer<InternalOptions> optionsConsumer = DEFAULT_OPTIONS; + private GenerateMainDexListTestBuilder(TestState state, Builder builder) { super(state, builder); } @@ -68,8 +74,12 @@ throw new Unimplemented("No support for class path"); } - public GenerateMainDexListRunResult run() throws CompilationFailedException { - return new GenerateMainDexListRunResult(GenerateMainDexList.run(builder.build()), getState()); + public GenerateMainDexListRunResult run() throws CompilationFailedException, IOException { + GenerateMainDexListCommand command = builder.build(); + InternalOptions internalOptions = command.getInternalOptions(); + optionsConsumer.accept(internalOptions); + return new GenerateMainDexListRunResult( + GenerateMainDexList.runForTesting(command.getInputApp(), internalOptions), getState()); } public GenerateMainDexListTestBuilder addMainDexRules(Collection<String> rules) { @@ -94,4 +104,12 @@ builder.setMainDexListOutputPath(output); return self(); } + + public GenerateMainDexListTestBuilder addOptionsModification( + Consumer<InternalOptions> optionsConsumer) { + if (optionsConsumer != null) { + this.optionsConsumer = this.optionsConsumer.andThen(optionsConsumer); + } + return self(); + } }
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java index 9a95fa7..f0c648b 100644 --- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java +++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -736,6 +736,17 @@ "550-new-instance-clinit", TestCondition.match( TestCondition.D8_COMPILER, TestCondition.runtimes(DexVm.Version.V6_0_1))) + // Regression test for an issue that is not fixed on version 5.1.1. Throws an Exception + // instance instead of the expected NullPointerException. This bug is only tickled when + // running the R8 generated code when starting from jar or from dex code generated with + // dx. However, the code that R8 generates is valid and there is nothing we can do for + // this one. + .put( + "551-implicit-null-checks", + TestCondition.match( + TestCondition.tools(DexTool.NONE, DexTool.DX), + TestCondition.R8DEX_COMPILER, + TestCondition.runtimes(DexVm.Version.V5_1_1))) // Contains a method (B.<init>) which pass too few arguments to invoke. Also, contains an // iput on a static field. .put( @@ -1630,7 +1641,7 @@ @Override public void accept(InternalOptions options) { if (disableInlining) { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; } if (disableClassInlining) { options.enableClassInlining = false; @@ -1837,7 +1848,7 @@ options -> { compilationOptions.accept(options); // Make sure we don't depend on this settings. - options.classInliningInstructionAllowance = 10000; + options.classInlinerOptions().classInliningInstructionAllowance = 10000; options.lineNumberOptimization = LineNumberOptimization.OFF; }); break;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java index 5d3c306..45d03e7 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -120,14 +120,14 @@ .withOptionConsumer(opts -> opts.enableClassInlining = false) .withBuilderTransformation( b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 9, "lambdadesugaring")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 10, "lambdadesugaring")) .run(); test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring") .withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K)) .withBuilderTransformation( b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaring")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring")) .run(); } @@ -159,14 +159,14 @@ .withOptionConsumer(opts -> opts.enableClassInlining = false) .withBuilderTransformation( b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 9, "lambdadesugaring")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 10, "lambdadesugaring")) .run(); test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring") .withMinApiLevel(AndroidApiLevel.N) .withBuilderTransformation( b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaring")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaring")) .run(); } @@ -244,7 +244,7 @@ b -> b.addProguardConfiguration( getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 0, "lambdadesugaringnplus")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaringnplus")) .run(); }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java index 2233484..d30e214 100644 --- a/src/test/java/com/android/tools/r8/R8TestBuilder.java +++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -345,6 +345,16 @@ return self(); } + public T enableAlwaysClassInlineAnnotations() { + return addAlwaysClassInlineAnnotation() + .enableAlwaysClassInlineAnnotations(AlwaysClassInline.class.getPackage().getName()); + } + + public T enableAlwaysClassInlineAnnotations(String annotationPackageName) { + return addInternalKeepRules( + "-alwaysclassinline @" + annotationPackageName + ".AlwaysClassInline class *"); + } + public T enableAlwaysInliningAnnotations() { return addAlwaysInliningAnnotations() .enableAlwaysInliningAnnotations(AlwaysInline.class.getPackage().getName());
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java index be7b225..beb7da4 100644 --- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java +++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -386,6 +386,10 @@ public abstract T addApplyMapping(String proguardMap); + public final T addAlwaysClassInlineAnnotation() { + return addTestingAnnotation(AlwaysClassInline.class); + } + public final T addAlwaysInliningAnnotations() { return addTestingAnnotation(AlwaysInline.class); }
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java index 972d058..4f91077 100644 --- a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java +++ b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
@@ -186,7 +186,7 @@ .addProgramClasses(CLASSES) .addOptionsModification( o -> { - o.enableInlining = false; + o.inlinerOptions().enableInlining = false; o.enableVerticalClassMerging = false; }) .noMinification()
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java index 209aa18..8a91c84 100644 --- a/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java +++ b/src/test/java/com/android/tools/r8/accessrelaxation/InvokeTypeConversionTest.java
@@ -79,7 +79,7 @@ .addKeepRules( // We're testing lens-based invocation type conversions. "-dontoptimize", "-dontobfuscate", "-allowaccessmodification") - .addOptionsModification(o -> o.enableInlining = false) + .addOptionsModification(o -> o.inlinerOptions().enableInlining = false) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), CLASS_NAME); if (expectedException == null) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java index 8a0c060..19e3299 100644 --- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -121,16 +121,6 @@ } @Test - public void initializeApiDatabaseTimeTest() { - DexItemFactory factory = new DexItemFactory(); - long start = System.currentTimeMillis(); - new AndroidApiLevelHashingDatabaseImpl(factory, ImmutableList.of()); - long end = System.currentTimeMillis(); - long timeSpan = end - start; - assertTrue("Time used was " + timeSpan, timeSpan < 100); - } - - @Test public void testCanLookUpAllParsedApiClassesAndMembers() throws Exception { List<ParsedApiClass> parsedApiClasses = AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java index 1790724..036dd46 100644 --- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java +++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -505,6 +505,6 @@ // bothers what the tests want to check, such as exact instructions in the body that include // invocation kinds, like virtual call to a bridge. // Disable inlining to avoid the (short) tested method from being inlined and then removed. - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; } }
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java index 32fa0b8..ed0081e 100644 --- a/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java +++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -104,7 +104,7 @@ testForR8(parameters.getBackend()) .addProgramClassFileData(programClassFileData) .addKeepMainRule(mainClass.name) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .allowAccessModification() .minification(minification) .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java b/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java index 16dfaa7..1db3ee4 100644 --- a/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java +++ b/src/test/java/com/android/tools/r8/cf/InlineCmpDoubleTest.java
@@ -41,7 +41,7 @@ testForR8(parameters.getBackend()) .addProgramClasses(TestClass.class) .addKeepMainRule(TestClass.class) - .addOptionsModification(options -> options.enableInlining = enableInlining) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = enableInlining) .setMinApi(parameters.getApiLevel()) .compile() .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java index 94b6238..568c66f 100644 --- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java +++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
@@ -48,7 +48,7 @@ } private void noInlining(InternalOptions options) { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; } private Matcher<Diagnostic> discardCheckFailedMatcher() {
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java index 389d347..ef048be 100644 --- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java +++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -69,7 +69,7 @@ } private void noInlining(InternalOptions options) { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; } private String checkDiscardRule(boolean member, Class<?> annotation) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/IllegalInliningOfMergedConstructorTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/IllegalInliningOfMergedConstructorTest.java index aad40d0..c0ba9c8 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/IllegalInliningOfMergedConstructorTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/IllegalInliningOfMergedConstructorTest.java
@@ -36,7 +36,8 @@ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(Reprocess.class)) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertMergedInto(B.class, A.class)) - .addOptionsModification(options -> options.inliningInstructionLimit = 4) + .addOptionsModification( + options -> options.inlinerOptions().simpleInliningInstructionLimit = 4) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java index 01c8b8c..d2bf4cd 100644 --- a/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java
@@ -40,7 +40,7 @@ .addKeepMainRule(Main.class) // Disable inlining to ensure that the synthetic lambdas remain in the residual // program. - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .addVerticallyMergedClassesInspector( inspector -> { if (parameters.isCfRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java index 8f4273f..46ecc50 100644 --- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -347,7 +347,7 @@ testForR8(parameters.getBackend()) .addKeepRules(getProguardConfig(EXAMPLE_KEEP)) .addOptionsModification(this::configure) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .allowUnusedProguardConfigurationRules(), main, readProgramFiles(programFiles),
diff --git a/src/test/java/com/android/tools/r8/compatproguard/reflection/ReflectionTest.java b/src/test/java/com/android/tools/r8/compatproguard/reflection/ReflectionTest.java index 352186b..fb1be42 100644 --- a/src/test/java/com/android/tools/r8/compatproguard/reflection/ReflectionTest.java +++ b/src/test/java/com/android/tools/r8/compatproguard/reflection/ReflectionTest.java
@@ -517,7 +517,8 @@ ImmutableList.of(keepMainProguardConfigurationWithInliningAnnotation(mainClass)), Origin.unknown()); ToolHelper.allowTestProguardOptions(builder); - AndroidApp output = ToolHelper.runR8(builder.build(), o -> o.enableInlining = false); + AndroidApp output = + ToolHelper.runR8(builder.build(), o -> o.inlinerOptions().enableInlining = false); runOnVM(output, mainClass, backend); }
diff --git a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java index 965b610..05bbb73 100644 --- a/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java +++ b/src/test/java/com/android/tools/r8/debug/DebugInfoWhenInliningTest.java
@@ -43,7 +43,8 @@ options.testing.forceJumboStringProcessing = forceJumboStringProcessing; // TODO(b/117848700): Can we make these tests neutral to inlining threshold? // Also CF needs improvements here. - options.inliningInstructionLimit = parameters.isCfRuntime() ? 5 : 4; + options.inlinerOptions().simpleInliningInstructionLimit = + parameters.isCfRuntime() ? 5 : 4; }) .compile(); DebugTestConfig config = result.debugConfig();
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java index 004a443..d227ea3 100644 --- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java +++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -61,7 +61,7 @@ if (!dontOptimizeByEnablingDebug) { options.lineNumberOptimization = lineNumberOptimization; } - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; }) .compile();
diff --git a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java index 4898ef3..e1b76d6 100644 --- a/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java +++ b/src/test/java/com/android/tools/r8/debuginfo/InliningWithoutPositionsTestRunner.java
@@ -104,7 +104,8 @@ .noMinification() .addKeepAttributeSourceFile() .addKeepAttributeLineNumberTable() - .addOptionsModification(options -> options.inliningInstructionLimit = 40) + .addOptionsModification( + options -> options.inlinerOptions().simpleInliningInstructionLimit = 40) .run(parameters.getRuntime(), MAIN_CLASS) .assertFailure();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java index f1f19ff..15d6a42 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
@@ -70,7 +70,7 @@ .addOptionsModification( options -> { // Need to increase a little bit to inline System.out.println - options.inliningInstructionLimit = 4; + options.inlinerOptions().simpleInliningInstructionLimit = 4; }) .apply(configuration) .setMinApi(parameters.getApiLevel()) @@ -119,7 +119,8 @@ buildAndRun( mainClass, ImmutableList.of(IntrinsicsDeputy.class, NotPinnedClass.class, mainClass), - R8TestBuilder::enableInliningAnnotations); + testBuilder -> + testBuilder.enableAlwaysInliningAnnotations().enableInliningAnnotations()); ClassSubject mainSubject = inspector.clazz(mainClass); assertThat(mainSubject, isPresent()); @@ -147,7 +148,8 @@ buildAndRun( mainClass, ImmutableList.of(IntrinsicsDeputy.class, NotPinnedClass.class, mainClass), - R8TestBuilder::enableInliningAnnotations); + testBuilder -> + testBuilder.enableAlwaysInliningAnnotations().enableInliningAnnotations()); ClassSubject mainSubject = inspector.clazz(mainClass); assertThat(mainSubject, isPresent()); @@ -179,7 +181,11 @@ NonNullParamAfterInvokeVirtual.class, NotPinnedClass.class, mainClass), - builder -> builder.enableNeverClassInliningAnnotations().enableInliningAnnotations()); + builder -> + builder + .enableAlwaysInliningAnnotations() + .enableNeverClassInliningAnnotations() + .enableInliningAnnotations()); ClassSubject mainSubject = inspector.clazz(NonNullParamAfterInvokeVirtual.class); assertThat(mainSubject, isPresent()); @@ -232,6 +238,7 @@ NonNullParamAfterInvokeInterface.class, NonNullParamInterfaceImpl.class)) .addOptionsModification(this::disableDevirtualization) + .enableAlwaysInliningAnnotations() .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java index b60f6c9..21f1be3 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -119,9 +119,9 @@ // and // that the class is therefore made abstract. o.enableClassInlining = false; - o.enableInlining = inlining; - o.enableInliningOfInvokesWithNullableReceivers = false; - o.inliningInstructionLimit = 6; + o.inlinerOptions().enableInlining = inlining; + o.inlinerOptions().enableInliningOfInvokesWithNullableReceivers = false; + o.inlinerOptions().simpleInliningInstructionLimit = 6; // Tests depend on nullability of receiver and argument in general. Learning very // accurate // nullability from actual usage in tests bothers what we want to test.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java index b1e3cfc..d9b5cd1 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderTest.java
@@ -9,6 +9,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.AlwaysClassInline; +import com.android.tools.r8.AlwaysInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoHorizontalClassMerging; import com.android.tools.r8.TestParameters; @@ -55,8 +57,8 @@ testForR8(parameters.getBackend()) .addInnerClasses(ClassInlinerSimplePairBuilderTest.class) .addKeepMainRule(TestClass.class) - // TODO(b/143129517): This relies on PairBuilder::build being inlined, thus the limit of 6. - .addOptionsModification(options -> options.inliningInstructionLimit = 6) + .enableAlwaysClassInlineAnnotations() + .enableAlwaysInliningAnnotations() .enableInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() .noMinification() @@ -128,6 +130,7 @@ } } + @AlwaysClassInline @NoHorizontalClassMerging static class PairBuilder<F, S> { @@ -148,6 +151,7 @@ return this; } + @AlwaysInline public Pair<F, S> build() { return new Pair<>(first, second); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java index 6e5dc39..d23951c 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerSimplePairBuilderWithMultipleBuildsTest.java
@@ -9,6 +9,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.AlwaysInline; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.utils.StringUtils; @@ -48,8 +49,7 @@ testForR8(parameters.getBackend()) .addInnerClasses(ClassInlinerSimplePairBuilderWithMultipleBuildsTest.class) .addKeepMainRule(TestClass.class) - // TODO(b/143129517): This relies on PairBuilder::build being inlined, thus the limit of 6. - .addOptionsModification(options -> options.inliningInstructionLimit = 6) + .enableAlwaysInliningAnnotations() .noMinification() .setMinApi(parameters.getApiLevel()) .compile() @@ -130,6 +130,7 @@ System.out.println("[after] second = " + this.second); } + @AlwaysInline public Pair<F, S> build() { return new Pair<>(first, second); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java index cb494cb..52fcf43 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -41,7 +41,8 @@ import com.android.tools.r8.utils.codeinspector.FoundClassSubject; import com.google.common.collect.Sets; import java.util.Collections; -import java.util.Set; +import java.util.HashSet; +import java.util.List; import java.util.stream.Collectors; import org.junit.Assume; import org.junit.Test; @@ -248,8 +249,7 @@ .addOptionsModification( o -> { // TODO(b/143129517, 141719453): The limit seems to only be needed for DEX... - o.classInliningInstructionLimit = 100; - o.classInliningInstructionAllowance = 1000; + o.classInlinerOptions().classInliningInstructionAllowance = 1000; }) .allowAccessModification() .noMinification() @@ -300,35 +300,32 @@ .addProgramClasses(classes) .addKeepMainRule(main) .addKeepAttributes("LineNumberTable") - .addOptionsModification( - o -> { - // TODO(b/141719453): Identify single instances instead of increasing the limit. - o.classInliningInstructionLimit = 20; - }) .allowAccessModification() .enableInliningAnnotations() .noMinification() - .run(main) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), main) .assertSuccessWithOutput(javaOutput); CodeInspector inspector = result.inspector(); ClassSubject clazz = inspector.clazz(main); - assertEquals( - Sets.newHashSet("java.lang.StringBuilder"), - collectTypes(clazz.uniqueMethodWithName("testStatelessLambda"))); - - // TODO(b/120814598): Should only be "java.lang.StringBuilder". Lambdas are not class inlined - // because parameter usage is not available for each lambda constructor. - Set<String> expectedTypes = Sets.newHashSet("java.lang.StringBuilder"); - expectedTypes.addAll( + List<String> synthesizedJavaLambdaClasses = inspector.allClasses().stream() .filter(FoundClassSubject::isSynthesizedJavaLambdaClass) .map(FoundClassSubject::getFinalName) - .collect(Collectors.toList())); - assertEquals(expectedTypes, collectTypes(clazz.uniqueMethodWithName("testStatefulLambda"))); + .collect(Collectors.toList()); + + // TODO(b/120814598): Should only be "java.lang.StringBuilder". + assertEquals( + new HashSet<>(synthesizedJavaLambdaClasses), + collectTypes(clazz.uniqueMethodWithName("testStatelessLambda"))); assertTrue( - inspector.allClasses().stream().noneMatch(ClassSubject::isSynthesizedJavaLambdaClass)); + inspector.allClasses().stream().anyMatch(ClassSubject::isSynthesizedJavaLambdaClass)); + + assertEquals( + Sets.newHashSet("java.lang.StringBuilder"), + collectTypes(clazz.uniqueMethodWithName("testStatefulLambda"))); } private String getProguardConfig(String main) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java index 1b5d955..187c5f3 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTupleBuilderConstructorsTest.java
@@ -10,6 +10,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.AlwaysClassInline; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.utils.StringUtils; @@ -44,6 +45,7 @@ testForR8(parameters.getBackend()) .addInnerClasses(ClassInlinerTupleBuilderConstructorsTest.class) .addKeepMainRule(TestClass.class) + .enableAlwaysClassInlineAnnotations() .noMinification() .setMinApi(parameters.getApiLevel()) .compile() @@ -89,6 +91,7 @@ } } + @AlwaysClassInline static class Tuple { boolean z;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java index 24c1c8b..7b68b6d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/LibraryOverrideClassInliningTest.java
@@ -6,7 +6,7 @@ import static com.android.tools.r8.references.Reference.methodFromMethod; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import com.android.tools.r8.NeverInline; @@ -114,10 +114,12 @@ .holder .toString() .equals(SimpleLibraryOverride.class.getTypeName()))); - // TODO(b/181942160): The non-simple run should ideally not inlined by the class inliner, since - // there is an instance of NonSimpleLibraryOverride that is not eligible for class inlining. - assertFalse( + // TODO(b/181942160): The non-simple run should ideally not inlined independent of the class + // inliner instruction allowance, since there is an instance of NonSimpleLibraryOverride that + // is not eligible for class inlining. + assertEquals( "Expected NonSimple.run invoke in:\n" + main.getMethod().codeToString(), + parameters.isDexRuntime(), main.streamInstructions() .anyMatch( i ->
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java index 9df6cb2..79d6062 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java
@@ -39,7 +39,10 @@ .addInnerClasses(NonMaterializingFieldAccessesAfterClassInliningTest.class) .addKeepMainRule(TestClass.class) // Should be able to class inline Builder even when the threshold is low. - .addOptionsModification(options -> options.classInliningInstructionAllowance = 3) + .addOptionsModification( + options -> + options.classInlinerOptions().classInliningInstructionAllowance = + parameters.isCfRuntime() ? 3 : 6) .enableInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java index 32ea22a..2a88107 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeInterfaceToInvokeVirtualTest.java
@@ -52,7 +52,8 @@ .addProgramClasses(I.class, A.class, A0.class, A1.class, Main.class) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableInliningOfInvokesWithNullableReceivers = false) + options -> + options.inlinerOptions().enableInliningOfInvokesWithNullableReceivers = false) .enableNoHorizontalClassMergingAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java index 59e5118..b4c0a63 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningAfterClassInitializationTest.java
@@ -223,7 +223,9 @@ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P)) .addKeepMainRule(mainClass) .addOptionsModification( - options -> options.enableInliningOfInvokesWithClassInitializationSideEffects = false) + options -> + options.inlinerOptions().enableInliningOfInvokesWithClassInitializationSideEffects = + false) .enableConstantArgumentAnnotations() .enableUnusedArgumentAnnotations() .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java index 2012c6f..1f51c2f 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -47,8 +47,8 @@ .addKeepMainRule(TestClass.class) .addOptionsModification( options -> { - options.applyInliningToInlinee = true; - options.applyInliningToInlineeMaxDepth = maxInliningDepth; + options.inlinerOptions().applyInliningToInlinee = true; + options.inlinerOptions().applyInliningToInlineeMaxDepth = maxInliningDepth; }) .enableAlwaysInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlinerMonitorEnterValuesThresholdTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlinerMonitorEnterValuesThresholdTest.java index 18d1deb..6ac9e07 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlinerMonitorEnterValuesThresholdTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlinerMonitorEnterValuesThresholdTest.java
@@ -41,7 +41,8 @@ testForR8(parameters.getBackend()) .addProgramClasses(TestClass.class) .addKeepMainRule(TestClass.class) - .addOptionsModification(options -> options.inliningMonitorEnterValuesAllowance = threshold) + .addOptionsModification( + options -> options.inlinerOptions().inliningMonitorEnterValuesAllowance = threshold) .setMinApi(parameters.getApiLevel()) .compile() .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NotPinnedClass.java b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NotPinnedClass.java index b9f368d..d629975 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NotPinnedClass.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/nonnull/NotPinnedClass.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.optimize.nonnull; +import com.android.tools.r8.AlwaysInline; + public class NotPinnedClass { final int field; @@ -10,6 +12,7 @@ this.field = field; } + @AlwaysInline void act() { System.out.println(field); }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java index 5db4d92..f19dea9 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b111893131/B111893131.java
@@ -14,7 +14,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; -import com.android.tools.r8.VmTestRunner; import com.android.tools.r8.code.Instruction; import com.android.tools.r8.code.InvokeVirtual; import com.android.tools.r8.graph.Code; @@ -27,7 +26,6 @@ import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.google.common.collect.ImmutableList; import org.junit.Test; -import org.junit.runner.RunWith; class TestClass { public interface Act { @@ -76,11 +74,14 @@ builder.setDisableMinification(true); String config = keepMainProguardConfiguration(TestClass.class); builder.addProguardConfiguration(ImmutableList.of(config), Origin.unknown()); - AndroidApp app = ToolHelper.runR8(builder.build(), options -> { - // To trigger outliner, set # of expected outline candidate as threshold. - options.outline.threshold = 2; - options.enableInlining = false; - }); + AndroidApp app = + ToolHelper.runR8( + builder.build(), + options -> { + // To trigger outliner, set # of expected outline candidate as threshold. + options.outline.threshold = 2; + options.inlinerOptions().enableInlining = false; + }); ProcessResult result = runOnArtRaw(app, TestClass.class); assertEquals(result.toString(), 0, result.exitCode); assertEquals(javaResult, result.stdout);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java index d5e8708..685d805 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b112247415/B112247415.java
@@ -90,7 +90,7 @@ options -> { // To trigger outliner, set # of expected outline candidate as threshold. options.outline.threshold = 2; - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; }) .noHorizontalClassMergingOfSynthetics() .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java index 44d3e31..837a84e 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/NestedInterfaceMethodTest.java
@@ -61,7 +61,7 @@ .addOptionsModification( options -> { options.enableDevirtualization = false; - options.enableInliningOfInvokesWithNullableReceivers = false; + options.inlinerOptions().enableInliningOfInvokesWithNullableReceivers = false; }) .setMinApi(AndroidApiLevel.B) .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java index 79880c8..5759deb 100644 --- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java +++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -31,10 +31,10 @@ import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.FieldSubject; +import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import org.junit.Assume; @@ -66,32 +66,49 @@ } protected static void checkMethodIsInvokedAtLeastOnce( - DexCode dexCode, MethodSignature... methodSignatures) { + MethodSubject subject, MethodSignature... methodSignatures) { for (MethodSignature methodSignature : methodSignatures) { - checkMethodIsInvokedAtLeastOnce(dexCode, methodSignature); + checkMethodIsInvokedAtLeastOnce(subject, methodSignature); } } private static void checkMethodIsInvokedAtLeastOnce( - DexCode dexCode, MethodSignature methodSignature) { - assertTrue("No invoke to '" + methodSignature.toString() + "'", - Arrays.stream(dexCode.instructions) - .filter((instr) -> instr.getMethod() != null) - .anyMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name))); + MethodSubject subject, MethodSignature methodSignature) { + assertTrue( + "No invoke to '" + methodSignature.toString() + "'", + subject + .streamInstructions() + .filter(InstructionSubject::isInvoke) + .anyMatch( + instructionSubject -> + instructionSubject + .getMethod() + .getName() + .toString() + .equals(methodSignature.name))); } protected static void checkMethodIsNeverInvoked( - DexCode dexCode, MethodSignature... methodSignatures) { + MethodSubject subject, MethodSignature... methodSignatures) { for (MethodSignature methodSignature : methodSignatures) { - checkMethodIsNeverInvoked(dexCode, methodSignature); + checkMethodIsNeverInvoked(subject, methodSignature); } } - private static void checkMethodIsNeverInvoked(DexCode dexCode, MethodSignature methodSignature) { - assertTrue("At least one invoke to '" + methodSignature.toString() + "'", - Arrays.stream(dexCode.instructions) - .filter((instr) -> instr.getMethod() != null) - .noneMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name))); + private static void checkMethodIsNeverInvoked( + MethodSubject subject, MethodSignature methodSignature) { + assertTrue( + "At least one invoke to '" + methodSignature.toString() + "'", + subject + .streamInstructions() + .filter(InstructionSubject::isInvoke) + .noneMatch( + instructionSubject -> + instructionSubject + .getMethod() + .getName() + .toString() + .equals(methodSignature.name))); } protected static void checkMethodsPresence(
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java index 105d097..c245f5a 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -14,13 +14,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.TestParameters; +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.cf.code.CfNew; +import com.android.tools.r8.code.Instruction; import com.android.tools.r8.code.NewInstance; import com.android.tools.r8.code.SgetObject; -import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.utils.IntBox; @@ -35,7 +36,6 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -244,8 +244,6 @@ @Test public void testDataClass() throws Exception { - // TODO(b/179866251): Update tests. - assumeTrue(kotlinc.is(KOTLINC_1_3_72) && testParameters.isDexRuntime()); String mainClassName = "class_inliner_data_class.MainKt"; runTest("class_inliner_data_class", mainClassName) .inspect( @@ -260,7 +258,10 @@ String[].class.getCanonicalName())); assertEquals( Lists.newArrayList( - "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)"), + kotlinc.is(KOTLINC_1_3_72) + ? "void kotlin.jvm.internal.Intrinsics.throwParameterIsNullException(java.lang.String)" + : "void kotlin.jvm.internal.Intrinsics.checkNotNullParameter(java.lang.Object," + + " java.lang.String)"), collectStaticCalls(clazz, "main", String[].class.getCanonicalName())); }); } @@ -272,15 +273,30 @@ String... params) { assertNotNull(clazz); MethodSignature signature = new MethodSignature(methodName, "void", params); - // TODO(b/179866251): Allow for CF code here. - DexCode code = clazz.method(signature).getMethod().getCode().asDexCode(); - return Stream.concat( - filterInstructionKind(code, NewInstance.class) - .map(insn -> ((NewInstance) insn).getType()), - filterInstructionKind(code, SgetObject.class) - .map(insn -> insn.getField().holder) - ) - .filter(isTypeOfInterest) + return clazz + .method(signature) + .streamInstructions() + .filter(instruction -> instruction.isNewInstance() || instruction.isStaticGet()) + .map( + instruction -> { + if (instruction.isCfInstruction()) { + CfInstruction baseInstruction = instruction.asCfInstruction().getInstruction(); + if (baseInstruction instanceof CfNew) { + return ((CfNew) baseInstruction).getType(); + } else if (instruction.getField().getType().isReferenceType()) { + return instruction.getField().getHolderType(); + } + } else { + Instruction baseInstruction = instruction.asDexInstruction().getInstruction(); + if (baseInstruction instanceof SgetObject) { + return baseInstruction.getField().getHolderType(); + } else if (baseInstruction instanceof NewInstance) { + return ((NewInstance) baseInstruction).getType(); + } + } + return null; + }) + .filter(type -> type != null && isTypeOfInterest.test(type)) .map(DexType::toSourceString) .collect(Collectors.toSet()); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java index 89e517a..af6986a 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -32,9 +32,9 @@ ); private Consumer<InternalOptions> optionsModifier = - o -> { - o.enableInlining = false; - }; + o -> { + o.inlinerOptions().enableInlining = false; + }; @Parameterized.Parameters(name = "{0}, {1}, allowAccessModification: {2}") public static Collection<Object[]> data() {
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java index 6ec949a..c407a5b 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -3,13 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; -import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.TestParameters; @@ -53,8 +53,6 @@ @Test public void b139432507() throws Exception { - // TODO(b/179866251): Update tests. - assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification); testForR8(parameters.getBackend()) .addProgramFiles( compiledJars.getForConfiguration(kotlinc, targetVersion), @@ -78,7 +76,9 @@ MethodSubject isSupported = main.uniqueMethodWithName("isSupported"); assertThat(isSupported, isPresent()); assertEquals( - allowAccessModification ? 0 : 1, + !kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72) || allowAccessModification + ? 0 + : 1, countCall(isSupported, "checkParameterIsNotNull")); // In general cases, null check won't be invoked only once or twice, hence no subtle @@ -91,21 +91,22 @@ @Test public void b139432507_isSupported() throws Exception { - // TODO(b/179866251): Update tests. - assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification); assumeTrue("Different inlining behavior on CF backend", parameters.isDexRuntime()); - testSingle("isSupported"); + testSingle( + "isSupported", + kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72) && !allowAccessModification); } @Test public void b139432507_containsArray() throws Exception { - // TODO(b/179866251): Update tests. - assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification); assumeTrue("Different inlining behavior on CF backend", parameters.isDexRuntime()); - testSingle("containsArray"); + // One for each of the method's own arguments, unless building with + // -allowaccessmodification. + testSingle("containsArray", allowAccessModification); } - private void testSingle(String methodName) throws Exception { + private void testSingle(String methodName, boolean checkParameterIsNotNullCountIsArity) + throws Exception { testForR8(parameters.getBackend()) .addProgramFiles( compiledJars.getForConfiguration(kotlinc, targetVersion), @@ -129,10 +130,8 @@ MethodSubject method = main.uniqueMethodWithName(methodName); assertThat(method, isPresent()); int arity = method.getMethod().getReference().getArity(); - // One for each of the method's own arguments, unless building with - // -allowaccessmodification. assertEquals( - allowAccessModification ? 0 : arity, + checkParameterIsNotNullCountIsArity ? arity : 0, countCall(method, "checkParameterIsNotNull")); }); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java index 9d66188..692f73b 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -3,13 +3,11 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; -import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.TestParameters; @@ -49,8 +47,6 @@ @Test public void b110196118() throws Exception { - // TODO(b/179866251): Update tests. - assumeTrue(kotlinc.is(KOTLINC_1_3_72)); final String mainClassName = "unused_singleton.MainKt"; final String moduleName = "unused_singleton.TestModule"; runTest("unused_singleton", mainClassName)
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java index 282d8d7..8cc28de 100644 --- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -8,7 +8,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.R8TestBuilder; @@ -388,8 +387,6 @@ @Test public void testAccessorForInnerClassIsRemovedWhenNotUsed() throws Exception { - // TODO(b/185493636): Kotlinc 1.5 generated property accessors are not removed. - assumeTrue(kotlinc.isNot(KOTLINC_1_5_0)); String mainClass = addMainToClasspath( "accessors.PropertyAccessorForInnerClassKt", "noUseOfPropertyAccessorFromInnerClass"); @@ -443,14 +440,7 @@ String mainClass = addMainToClasspath(testedClass.className + "Kt", "usePrivateLateInitPropertyAccessorFromInnerClass"); runTest("accessors", mainClass) - .inspect( - inspector -> { - if (kotlinc.getCompilerVersion() == KOTLINC_1_5_0 && testParameters.isDexRuntime()) { - checkClassIsKept(inspector, testedClass.getClassName()); - } else { - checkClassIsRemoved(inspector, testedClass.getClassName()); - } - }); + .inspect(inspector -> checkClassIsRemoved(inspector, testedClass.getClassName())); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java index 2cb21d4..1da9c86 100644 --- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,11 +4,8 @@ package com.android.tools.r8.kotlin; -import static org.junit.Assume.assumeTrue; - import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.kotlin.TestKotlinClass.Visibility; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.utils.BooleanUtils; @@ -62,8 +59,6 @@ @Test public void test_dataclass_gettersOnly() throws Exception { - // TODO(b/179866251): Allow for CF code. - assumeTrue(testParameters.isDexRuntime()); String mainClassName = "dataclass.MainGettersOnlyKt"; MethodSignature testMethodSignature = new MethodSignature("testDataClassGetters", "void", Collections.emptyList()); @@ -99,20 +94,17 @@ ClassSubject classSubject = checkClassIsKept(inspector, mainClassName); MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature); - DexCode dexCode = getDexCode(testMethod); if (allowAccessModification) { // Both getters should be inlined - checkMethodIsNeverInvoked(dexCode, NAME_GETTER_METHOD, AGE_GETTER_METHOD); + checkMethodIsNeverInvoked(testMethod, NAME_GETTER_METHOD, AGE_GETTER_METHOD); } else { - checkMethodIsInvokedAtLeastOnce(dexCode, NAME_GETTER_METHOD, AGE_GETTER_METHOD); + checkMethodIsInvokedAtLeastOnce(testMethod, NAME_GETTER_METHOD, AGE_GETTER_METHOD); } }); } @Test public void test_dataclass_componentOnly() throws Exception { - // TODO(b/179866251): Allow for CF code. - assumeTrue(testParameters.isDexRuntime()); String mainClassName = "dataclass.MainComponentOnlyKt"; MethodSignature testMethodSignature = new MethodSignature("testAllDataClassComponentFunctions", "void", Collections.emptyList()); @@ -150,19 +142,16 @@ ClassSubject classSubject = checkClassIsKept(inspector, mainClassName); MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature); - DexCode dexCode = getDexCode(testMethod); if (allowAccessModification) { - checkMethodIsNeverInvoked(dexCode, COMPONENT1_METHOD, COMPONENT2_METHOD); + checkMethodIsNeverInvoked(testMethod, COMPONENT1_METHOD, COMPONENT2_METHOD); } else { - checkMethodIsInvokedAtLeastOnce(dexCode, COMPONENT1_METHOD, COMPONENT2_METHOD); + checkMethodIsInvokedAtLeastOnce(testMethod, COMPONENT1_METHOD, COMPONENT2_METHOD); } }); } @Test public void test_dataclass_componentPartial() throws Exception { - // TODO(b/179866251): Allow for CF code. - assumeTrue(testParameters.isDexRuntime()); String mainClassName = "dataclass.MainComponentPartialKt"; MethodSignature testMethodSignature = new MethodSignature("testSomeDataClassComponentFunctions", "void", Collections.emptyList()); @@ -198,11 +187,10 @@ ClassSubject classSubject = checkClassIsKept(inspector, mainClassName); MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature); - DexCode dexCode = getDexCode(testMethod); if (allowAccessModification) { - checkMethodIsNeverInvoked(dexCode, COMPONENT2_METHOD); + checkMethodIsNeverInvoked(testMethod, COMPONENT2_METHOD); } else { - checkMethodIsInvokedAtLeastOnce(dexCode, COMPONENT2_METHOD); + checkMethodIsInvokedAtLeastOnce(testMethod, COMPONENT2_METHOD); } }); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java index 4457386..142e48d 100644 --- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,9 +4,7 @@ package com.android.tools.r8.kotlin; -import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72; -import static org.junit.Assume.assumeTrue; - +import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.TestParameters; import com.android.tools.r8.naming.MemberNaming.MethodSignature; @@ -44,12 +42,9 @@ @Test public void testParameterNullCheckIsInlined() throws Exception { - // TODO(b/179866251): Update tests. - assumeTrue(kotlinc.is(KOTLINC_1_3_72)); final String extraRules = keepClassMethod("intrinsics.IntrinsicsKt", new MethodSignature("expectsNonNullParameters", "java.lang.String", Lists.newArrayList("java.lang.String", "java.lang.String"))); - runTest( "intrinsics", "intrinsics.IntrinsicsKt", @@ -67,13 +62,15 @@ "throwParameterIsNullException", "void", Collections.singletonList("java.lang.String")), - true) + // throwParameterIsNullException is not added for test starting from 1.4 + kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72)) .put( new MethodSignature( "checkParameterIsNotNull", "void", Lists.newArrayList("java.lang.Object", "java.lang.String")), - !allowAccessModification) + kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72) + && !allowAccessModification) .build()); }); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java index 091a8b4..488271b 100644 --- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -3,9 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; -import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72; import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeTrue; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.TestParameters; @@ -69,20 +67,20 @@ // Find forMakeAndModel(...) after parameter removal. MethodSubject testMethod = clazz.uniqueMethodWithName(testMethodSignature.name); long ifzCount = - testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count(); + testMethod + .streamInstructions() + .filter(i -> i.isIfEqz() || i.isIfNez() || i.isIfNonNull()) + .count(); long paramNullCheckCount = countCall(testMethod, "Intrinsics", "checkParameterIsNotNull"); // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null. - // TODO(b/179951729): Not the same amount of ifz on CF and DEX. - assertEquals(testParameters.isCfRuntime() ? 1 : 2, ifzCount); + assertEquals(2, ifzCount); assertEquals(0, paramNullCheckCount); }); } @Test public void test_example2() throws Exception { - // TODO(b/179866251): Update tests. - assumeTrue(kotlinc.is(KOTLINC_1_3_72) || allowAccessModification); final TestKotlinClass ex2 = new TestKotlinClass("non_null.Example2Kt"); final MethodSignature testMethodSignature = new MethodSignature("aOrDefault", STRING, ImmutableList.of(STRING, STRING)); @@ -93,15 +91,16 @@ .inspect( inspector -> { ClassSubject clazz = checkClassIsKept(inspector, ex2.getClassName()); - MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature); long ifzCount = - testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count(); + testMethod + .streamInstructions() + .filter(i -> i.isIfEqz() || i.isIfNez() || i.isIfNull() || i.isIfNonNull()) + .count(); long paramNullCheckCount = countCall(testMethod, "Intrinsics", "checkParameterIsNotNull"); // ?: in aOrDefault - // TODO(b/179951729): Not the same amount of ifz on CF and DEX. - assertEquals(testParameters.isCfRuntime() ? 0 : 1, ifzCount); + assertEquals(1, ifzCount); assertEquals(0, paramNullCheckCount); }); } @@ -118,14 +117,15 @@ .inspect( inspector -> { ClassSubject clazz = checkClassIsKept(inspector, ex3.getClassName()); - MethodSubject testMethod = checkMethodIsKept(clazz, testMethodSignature); long ifzCount = - testMethod.streamInstructions().filter(i -> i.isIfEqz() || i.isIfNez()).count(); + testMethod + .streamInstructions() + .filter(i -> i.isIfEqz() || i.isIfNez() || i.isIfNull() || i.isIfNonNull()) + .count(); // !! operator inside explicit null check should be gone. // One explicit null-check as well as 4 bar? accesses. - // TODO(b/179951729): Not the same amount of ifz on CF and DEX. - assertEquals(testParameters.isCfRuntime() ? 0 : 5, ifzCount); + assertEquals(5, ifzCount); }); } }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java index 729869f..78613d4 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -37,7 +37,7 @@ "lambdas_jstyle_runnable", mainClassName, testBuilder -> - testBuilder - .addOptionsModification(options -> options.inliningInstructionAllowance = 3)); + testBuilder.addOptionsModification( + options -> options.inlinerOptions().inliningInstructionAllowance = 3)); } }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java index 7bd2b8c..a4f497f 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -70,7 +70,7 @@ internalOptions -> // Setting inliningInstructionAllowance = 1 will force each switch branch to // contain an invoke instruction to a private method. - internalOptions.inliningInstructionAllowance = 1)) + internalOptions.inlinerOptions().inliningInstructionAllowance = 1)) .addHorizontallyMergedClassesInspector( inspector -> inspector.assertIsCompleteMergeGroup(
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java index 42cd647..013a15b 100644 --- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java +++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -118,7 +118,7 @@ builder -> builder.addOptionsModification( options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.mainDexKeptGraphConsumer = graphConsumer; })); { @@ -211,7 +211,9 @@ Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"), Paths.get(EXAMPLE_O_SRC_DIR, "multidex004", "ref-list-1.txt"), AndroidApiLevel.I, - builder -> builder.addOptionsModification(options -> options.enableInlining = false)); + builder -> + builder.addOptionsModification( + options -> options.inlinerOptions().enableInlining = false)); } @Test @@ -311,7 +313,9 @@ expectedR8MainDexList, expectedMainDexList, minSdk, - builder -> builder.addOptionsModification(options -> options.enableInlining = false)); + builder -> + builder.addOptionsModification( + options -> options.inlinerOptions().enableInlining = false)); } private void doTest(
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java index 756cf01..48c9501 100644 --- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -104,7 +104,7 @@ ToolHelper.runR8( builder.build(), options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.enableRedundantFieldLoadElimination = false; }); }
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java index 3eae470..97073ac 100644 --- a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeInSubInterfaceTest.java
@@ -86,7 +86,8 @@ "-keep,allowobfuscation class **.*$Super* { <methods>; }", "-keep,allowobfuscation class **.*$Sub* { <methods>; }", overloadAggressively ? "-overloadaggressively" : "# Not overload aggressively") - .addOptionsModification(internalOptions -> internalOptions.enableInlining = false) + .addOptionsModification( + internalOptions -> internalOptions.inlinerOptions().enableInlining = false) .run(parameters.getRuntime(), TestMain.class) .inspect(inspector -> inspect(inspector, overloadAggressively)); }
diff --git a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTestRunner.java b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTestRunner.java index 778b8ad..0e06680 100644 --- a/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTestRunner.java +++ b/src/test/java/com/android/tools/r8/naming/WarnReflectiveAccessTestRunner.java
@@ -97,7 +97,7 @@ return ToolHelper.runR8( commandBuilder.build(), o -> { - o.enableInlining = false; + o.inlinerOptions().enableInlining = false; o.forceProguardCompatibility = forceProguardCompatibility; }); }
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java index 2587730..670b6ae 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
@@ -105,7 +105,7 @@ .addProgramClasses(LIBRARY_CLASSES) .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class) .addKeepMainRule(LibClassB.class) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .compile(); CodeInspector inspector = libraryResult.inspector(); @@ -136,7 +136,7 @@ .addProgramClasses(LIBRARY_CLASSES) .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class, LibInterfaceA.class) .addKeepMainRule(LibClassB.class) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .compile(); CodeInspector inspector = libraryResult.inspector();
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java index e01926d..84a7f62 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingVirtualInvokeTest.java
@@ -109,7 +109,7 @@ .setMinApi(parameters.getRuntime()) .addOptionsModification( options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.enableVerticalClassMerging = false; options.enableClassInlining = false; })
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java index c064105..554ea5c 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
@@ -219,7 +219,7 @@ command, options -> { // Disable inlining to make this test not depend on inlining decisions. - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; }); } }
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java index a689715..da28db3 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionAsmTest.java
@@ -105,7 +105,7 @@ .addApplyMapping(pgMap) .addOptionsModification( options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.enableVerticalClassMerging = false; }) .run(parameters.getRuntime(), noMappingMain) @@ -197,7 +197,7 @@ .addApplyMapping(pgMap) .addOptionsModification( options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.enableVerticalClassMerging = false; }) .compile();
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java index cbd6e3b..7ed73c6 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
@@ -124,7 +124,7 @@ .addKeepRules("-applymapping " + mapPath) .enableMemberValuePropagationAnnotations() .enableNoVerticalClassMergingAnnotations() - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MemberResolutionTestMain.class) .assertSuccessWithOutput(expectedOutput)
diff --git a/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java index 6ceff62..4af7be5 100644 --- a/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java +++ b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
@@ -41,7 +41,7 @@ compileWithR8( input, keepMainProguardConfiguration(TestDriver.class), - options -> options.enableInlining = false, + options -> options.inlinerOptions().enableInlining = false, backend); String mainClass = TestDriver.class.getName(); assertEquals(runOnJava(TestDriver.class), runOnVM(output, mainClass, backend));
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java index e691d86..cbd22a0 100644 --- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java +++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -59,10 +59,12 @@ .setOutput(out, outputMode(backend)) .addLibraryFiles(TestBase.runtimeJar(backend)) .build(); - return ToolHelper.runR8(command, o -> { - o.enableInlining = false; - o.forceProguardCompatibility = true; - }); + return ToolHelper.runR8( + command, + o -> { + o.inlinerOptions().enableInlining = false; + o.forceProguardCompatibility = true; + }); } private ProcessResult runRaw(AndroidApp app, String main) throws IOException { @@ -183,7 +185,7 @@ testForR8Compat(backend) .addProgramClasses(MethodResolution.class, B.class) .addKeepMainRule(MethodResolution.class) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .applyIf(overloadaggressively, builder -> builder.addKeepRules("-overloadaggressively")) .enableMemberValuePropagationAnnotations() .compile()
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/LineNumberRangeTest.java b/src/test/java/com/android/tools/r8/naming/retrace/LineNumberRangeTest.java index 02ed76d..84f0389 100644 --- a/src/test/java/com/android/tools/r8/naming/retrace/LineNumberRangeTest.java +++ b/src/test/java/com/android/tools/r8/naming/retrace/LineNumberRangeTest.java
@@ -86,7 +86,7 @@ .setMinApi(parameters.getApiLevel()) .addOptionsModification( options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; }) .run(parameters.getRuntime(), Main.class) .assertFailure();
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java index c4d6dad..4472d60 100644 --- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java +++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -132,6 +132,13 @@ return this; } + public Builder applyIf(boolean condition, Consumer<Builder> fn) { + if (condition) { + fn.accept(this); + } + return this; + } + public StackTraceLine build() { String lineNumberPart = lineNumber >= 0 ? ":" + lineNumber : ""; String originalLine = className + '.' + methodName + '(' + fileName + lineNumberPart + ')'; @@ -490,6 +497,45 @@ } } + // Equivalence comparing stack traces without taking the file name into account. + public static class EquivalenceWithoutLineNumbers extends StackTraceEquivalence { + + private static class LineEquivalence extends Equivalence<StackTrace.StackTraceLine> { + + private static final LineEquivalence INSTANCE = new LineEquivalence(); + + public static LineEquivalence get() { + return INSTANCE; + } + + @Override + protected boolean doEquivalent(StackTrace.StackTraceLine a, StackTrace.StackTraceLine b) { + return a.className.equals(b.className) + && a.methodName.equals(b.methodName) + && Objects.equals(a.fileName, b.fileName); + } + + @Override + protected int doHash(StackTrace.StackTraceLine stackTraceLine) { + return stackTraceLine.className.hashCode() * 13 + + stackTraceLine.methodName.hashCode() * 7 + + Objects.hashCode(stackTraceLine.fileName); + } + } + + private static final EquivalenceWithoutLineNumbers INSTANCE = + new EquivalenceWithoutLineNumbers(); + + public static EquivalenceWithoutLineNumbers get() { + return INSTANCE; + } + + @Override + public Equivalence<StackTrace.StackTraceLine> getLineEquivalence() { + return LineEquivalence.get(); + } + } + // Equivalence comparing stack traces without taking the file name and line number into account. public static class EquivalenceWithoutFileNameAndLineNumber extends StackTraceEquivalence { @@ -637,10 +683,20 @@ } } + public static class StackTraceIgnoreLineNumbersMatcher extends StackTraceMatcherBase { + private StackTraceIgnoreLineNumbersMatcher(StackTrace expected) { + super(expected, EquivalenceWithoutLineNumbers.get(), "(ignoring line numbers)"); + } + } + public static Matcher<StackTrace> isSameExceptForFileName(StackTrace stackTrace) { return new StackTraceIgnoreFileNameMatcher(stackTrace); } + public static Matcher<StackTrace> isSameExceptForLineNumbers(StackTrace stackTrace) { + return new StackTraceIgnoreLineNumbersMatcher(stackTrace); + } + public static class StackTraceIgnoreFileNameAndLineNumberMatcher extends StackTraceMatcherBase { private StackTraceIgnoreFileNameAndLineNumberMatcher(StackTrace expected) { super(
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java new file mode 100644 index 0000000..3ad54fb --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
@@ -0,0 +1,163 @@ +// Copyright (c) 2018, 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.naming.retraceproguard; + +import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileName; +import static com.android.tools.r8.naming.retraceproguard.StackTrace.isSameExceptForFileNameAndLineNumber; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.R8TestBuilder; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine; +import com.android.tools.r8.utils.BooleanUtils; +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class VerticalClassMergingRetraceTest extends RetraceTestBase { + private Set<StackTraceLine> haveSeenLines = new HashSet<>(); + + @Parameters(name = "{0}, mode: {1}, compat: {2}") + public static Collection<Object[]> data() { + return buildParameters( + getTestParameters() + .withCfRuntimes() + // Runtimes prior to 8 will emit stack trace lines as: + // Exception in thread "main" java.lang.NullPointerException: throw with null exception + // at com.android.tools.r8.naming.retraceproguard.a.b(SourceFile) + // PG do not support retracing if no line number is specified. + .withDexRuntimesStartingFromIncluding(Version.V8_1_0) + .withAllApiLevels() + .build(), + CompilationMode.values(), + BooleanUtils.values()); + } + + public VerticalClassMergingRetraceTest( + TestParameters parameters, CompilationMode mode, boolean compat) { + super(parameters, mode, compat); + } + + @Override + public void configure(R8TestBuilder builder) { + builder.enableInliningAnnotations(); + } + + @Override + public Collection<Class<?>> getClasses() { + return ImmutableList.of(getMainClass(), ResourceWrapper.class, TintResources.class); + } + + @Override + public Class<?> getMainClass() { + return MainApp.class; + } + + private int expectedActualStackTraceHeight() { + // In RELEASE mode, a synthetic bridge will be added by vertical class merger. + int height = mode == CompilationMode.RELEASE ? 3 : 2; + if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) { + // Dalvik places a stack trace line in the bottom. + height += 1; + } + return height; + } + + private boolean filterSynthesizedMethodWhenLineNumberAvailable( + StackTraceLine retracedStackTraceLine) { + return retracedStackTraceLine.lineNumber > 0; + } + + private boolean filterSynthesizedMethod(StackTraceLine retracedStackTraceLine) { + return haveSeenLines.add(retracedStackTraceLine) + && (retracedStackTraceLine.className.contains("ResourceWrapper") + || retracedStackTraceLine.className.contains("MainApp")); + } + + @Test + public void testSourceFileAndLineNumberTable() throws Exception { + runTest( + ImmutableList.of("-keepattributes SourceFile,LineNumberTable"), + (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> { + // Even when SourceFile is present retrace replaces the file name in the stack trace. + StackTrace reprocessedStackTrace = + mode == CompilationMode.DEBUG + ? retracedStackTrace + : retracedStackTrace.filter(this::filterSynthesizedMethodWhenLineNumberAvailable); + assertThat( + reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod), + isSameExceptForFileName( + expectedStackTrace.filter(this::isNotDalvikNativeStartMethod))); + assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); + }); + } + + @Test + public void testLineNumberTableOnly() throws Exception { + assumeTrue(compat); + assumeTrue(parameters.isDexRuntime()); + runTest( + ImmutableList.of("-keepattributes LineNumberTable"), + (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> { + StackTrace reprocessedStackTrace = + mode == CompilationMode.DEBUG + ? retracedStackTrace + : retracedStackTrace.filter(this::filterSynthesizedMethodWhenLineNumberAvailable); + assertThat( + reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod), + isSameExceptForFileName( + expectedStackTrace.filter(this::isNotDalvikNativeStartMethod))); + assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); + }); + } + + @Test + public void testNoLineNumberTable() throws Exception { + assumeTrue(compat); + assumeTrue(parameters.isDexRuntime()); + haveSeenLines.clear(); + runTest( + ImmutableList.of(), + (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> { + StackTrace reprocessedStackTrace = + mode == CompilationMode.DEBUG + ? retracedStackTrace + : retracedStackTrace.filter(this::filterSynthesizedMethod); + assertThat( + reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod), + isSameExceptForFileNameAndLineNumber( + expectedStackTrace.filter(this::isNotDalvikNativeStartMethod))); + assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); + }); + } +} + +class ResourceWrapper { + // Will be merged down, and represented as: + // java.lang.String ...ResourceWrapper.foo() -> a + @NeverInline + String foo() { + throw null; + } +} + +class TintResources extends ResourceWrapper {} + +class MainApp { + public static void main(String[] args) { + TintResources t = new TintResources(); + System.out.println(t.foo()); + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/sourcefile/MapIdTemplateTest.java b/src/test/java/com/android/tools/r8/naming/sourcefile/MapIdTemplateTest.java new file mode 100644 index 0000000..7da7b54 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/sourcefile/MapIdTemplateTest.java
@@ -0,0 +1,142 @@ +// Copyright (c) 2021, 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.naming.sourcefile; + +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer; +import com.android.tools.r8.DexIndexedConsumer; +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.R8; +import com.android.tools.r8.R8Command.Builder; +import com.android.tools.r8.R8CommandParser; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestDiagnosticMessagesImpl; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.origin.Origin; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class MapIdTemplateTest extends TestBase { + + @Parameterized.Parameters(name = "{0}, {1}") + public static List<Object[]> data() { + return buildParameters(getTestParameters().withNoneRuntime().build(), Backend.values()); + } + + private final Backend backend; + + public MapIdTemplateTest(TestParameters parameters, Backend backend) { + parameters.assertNoneRuntime(); + this.backend = backend; + } + + @Test + public void testNoVariables() throws Exception { + String template = "my-build-id"; + assertEquals(template, compileWithMapIdTemplate(template)); + } + + @Test + public void testInvalidVariable() { + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + parseMapIdTemplate("my-%build-id", messages); + messages + .assertOnlyErrors() + .assertErrorsMatch( + diagnosticMessage(containsString("Invalid template variable starting with %bu"))); + } + + @Test + public void testInvalidVariablesMix() { + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + parseMapIdTemplate("my%%MAP_HASHJUNK", messages); + messages + .assertOnlyErrors() + .assertErrorsMatch( + diagnosticMessage(containsString("Invalid template variable starting with %%MAP_"))); + } + + @Test + public void testNoEscape() { + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + parseMapIdTemplate("my%%buildid", messages); + messages + .assertOnlyErrors() + .assertErrorsMatch( + Arrays.asList( + diagnosticMessage(containsString("Invalid template variable starting with %%b")), + diagnosticMessage(containsString("Invalid template variable starting with %b")))); + } + + @Test + public void testMapHash() throws Exception { + String template = "mybuildid %MAP_HASH"; + String actual = compileWithMapIdTemplate(template); + assertThat(actual, startsWith("mybuildid ")); + assertThat(actual, not(containsString("%"))); + assertEquals("mybuildid ".length() + 64, actual.length()); + } + + @Test + public void testMultiple() throws Exception { + String template = "hash %MAP_HASH hash %MAP_HASH"; + String actual = compileWithMapIdTemplate(template); + assertEquals("hash hash ".length() + 2 * 64, actual.length()); + } + + private String compileWithMapIdTemplate(String template) throws Exception { + Path out = temp.newFolder().toPath().resolve("out.jar"); + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + StringBuilder mapping = new StringBuilder(); + R8.run( + parseMapIdTemplate(template, messages) + .addProguardConfiguration( + Arrays.asList("-keep class * { *; }", "-dontwarn " + typeName(TestClass.class)), + Origin.unknown()) + .setProgramConsumer( + backend.isCf() + ? new ArchiveConsumer(out) + : new DexIndexedConsumer.ArchiveConsumer(out)) + .addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class)) + // TODO(b/201269335): What should be the expected result when no map is created? + .setProguardMapConsumer((content, handler) -> mapping.append(content)) + .build()); + messages.assertNoMessages(); + return getMapId(mapping.toString()); + } + + private Builder parseMapIdTemplate(String template, DiagnosticsHandler handler) { + return R8CommandParser.parse( + new String[] {"--map-id-template", template}, Origin.unknown(), handler); + } + + private String getMapId(String mapping) { + String lineHeader = "# pg_map_id: "; + int i = mapping.indexOf(lineHeader); + assertTrue(i >= 0); + int start = i + lineHeader.length(); + int end = mapping.indexOf('\n', start); + return mapping.substring(start, end); + } + + static class TestClass { + + public static void main(String[] args) { + System.out.println("Hello world"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/sourcefile/SourceFileTemplateTest.java b/src/test/java/com/android/tools/r8/naming/sourcefile/SourceFileTemplateTest.java new file mode 100644 index 0000000..cdff91c --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/sourcefile/SourceFileTemplateTest.java
@@ -0,0 +1,168 @@ +// Copyright (c) 2021, 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.naming.sourcefile; + +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer; +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.DexIndexedConsumer; +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.R8; +import com.android.tools.r8.R8Command.Builder; +import com.android.tools.r8.R8CommandParser; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestDiagnosticMessagesImpl; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class SourceFileTemplateTest extends TestBase { + + @Parameterized.Parameters(name = "{0}, {1}") + public static List<Object[]> data() { + return buildParameters(getTestParameters().withNoneRuntime().build(), Backend.values()); + } + + private final Backend backend; + + public SourceFileTemplateTest(TestParameters parameters, Backend backend) { + parameters.assertNoneRuntime(); + this.backend = backend; + } + + @Test + public void testNoVariables() throws Exception { + String template = "MySourceFile"; + assertEquals( + template, + new CodeInspector(compileWithSourceFileTemplate(template)) + .clazz(TestClass.class) + .getDexProgramClass() + .getSourceFile() + .toString()); + } + + @Test + public void testInvalidVariables() { + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + parseSourceFileTemplate("My%Source%File", messages); + messages + .assertOnlyErrors() + .assertErrorsMatch( + Arrays.asList( + diagnosticMessage(containsString("Invalid template variable starting with %So")), + diagnosticMessage(containsString("Invalid template variable starting with %Fi")))); + } + + @Test + public void testInvalidVariablesMix() { + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + parseSourceFileTemplate("My%%MAP_IDJUNK", messages); + messages + .assertOnlyErrors() + .assertErrorsMatch( + diagnosticMessage(containsString("Invalid template variable starting with %%MAP_"))); + } + + @Test + public void testNoEscape() { + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + parseSourceFileTemplate("My%%SourceFile", messages); + messages + .assertOnlyErrors() + .assertErrorsMatch( + Arrays.asList( + diagnosticMessage(containsString("Invalid template variable starting with %%S")), + diagnosticMessage(containsString("Invalid template variable starting with %So")))); + } + + @Test + public void testMapId() throws Exception { + String template = "MySourceFile %MAP_ID"; + String actual = + new CodeInspector(compileWithSourceFileTemplate(template)) + .clazz(TestClass.class) + .getDexProgramClass() + .getSourceFile() + .toString(); + assertThat(actual, startsWith("MySourceFile ")); + assertThat(actual, not(containsString("%"))); + assertEquals("MySourceFile ".length() + 7, actual.length()); + } + + @Test + public void testMapHash() throws Exception { + String template = "MySourceFile %MAP_HASH"; + String actual = + new CodeInspector(compileWithSourceFileTemplate(template)) + .clazz(TestClass.class) + .getDexProgramClass() + .getSourceFile() + .toString(); + assertThat(actual, startsWith("MySourceFile ")); + assertThat(actual, not(containsString("%"))); + assertEquals("MySourceFile ".length() + 64, actual.length()); + } + + @Test + public void testMultiple() throws Exception { + String template = "id %MAP_ID hash %MAP_HASH id %MAP_ID hash %MAP_HASH"; + String actual = + new CodeInspector(compileWithSourceFileTemplate(template)) + .clazz(TestClass.class) + .getDexProgramClass() + .getSourceFile() + .toString(); + assertEquals("id hash id hash ".length() + 2 * 7 + 2 * 64, actual.length()); + } + + private Path compileWithSourceFileTemplate(String template) + throws IOException, CompilationFailedException { + Path out = temp.newFolder().toPath().resolve("out.jar"); + TestDiagnosticMessagesImpl messages = new TestDiagnosticMessagesImpl(); + R8.run( + parseSourceFileTemplate(template, messages) + .addProguardConfiguration( + Arrays.asList("-keep class * { *; }", "-dontwarn " + typeName(TestClass.class)), + Origin.unknown()) + .setProgramConsumer( + backend.isCf() + ? new ArchiveConsumer(out) + : new DexIndexedConsumer.ArchiveConsumer(out)) + .addProgramFiles(ToolHelper.getClassFileForTestClass(TestClass.class)) + // TODO(b/201269335): What should be the expected result when no map is created? + .setProguardMapConsumer(StringConsumer.emptyConsumer()) + .build()); + messages.assertNoMessages(); + return out; + } + + private Builder parseSourceFileTemplate(String template, DiagnosticsHandler handler) { + return R8CommandParser.parse( + new String[] {"--source-file-template", template}, Origin.unknown(), handler); + } + + static class TestClass { + + public static void main(String[] args) { + System.out.println("Hello world"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java index 383a7ef..3e295a8 100644 --- a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java +++ b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
@@ -3,8 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.regress; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; -import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.TestBase; @@ -57,13 +57,9 @@ } private void checkJoinerIsClassInlined(CodeInspector inspector) { - assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), not(isPresent())); - // TODO(b/160640028): When compiling to DEX the outer Joiner class is not inlined. - if (parameters.isDexRuntime()) { - assertThat(inspector.clazz(Joiner.class), isPresent()); - } else { - assertThat(inspector.clazz(Joiner.class), not(isPresent())); - } + assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), isAbsent()); + // TODO(b/160640028): Joiner should be class inlined. + assertThat(inspector.clazz(Joiner.class), isPresent()); } static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java index d251e28..7138094 100644 --- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java +++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -64,10 +64,11 @@ // Check that the synthetic class is still present. assertEquals(3, classes.size()); - assertEquals(1, + assertEquals( + 1, classes.stream() .map(FoundClassSubject::getOriginalName) - .filter(name -> name.endsWith("$1")) + .filter(name -> name.endsWith("$1")) .count()); } @@ -95,10 +96,11 @@ // Check that the synthetic class is still present. assertEquals(3, classes.size()); - assertEquals(1, + assertEquals( + 1, classes.stream() .map(FoundClassSubject::getOriginalName) - .filter(name -> name.endsWith("$1")) + .filter(name -> name.endsWith("$1")) .count()); } }
diff --git a/src/test/java/com/android/tools/r8/resolution/B77944861.java b/src/test/java/com/android/tools/r8/resolution/B77944861.java index 3572263..09a3054 100644 --- a/src/test/java/com/android/tools/r8/resolution/B77944861.java +++ b/src/test/java/com/android/tools/r8/resolution/B77944861.java
@@ -61,9 +61,11 @@ .setDisableTreeShaking(true) .setDisableMinification(true) .build(); - return ToolHelper.runR8(command, o -> { - o.enableInlining = false; - }); + return ToolHelper.runR8( + command, + o -> { + o.inlinerOptions().enableInlining = false; + }); } @Test
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTest.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTest.java new file mode 100644 index 0000000..b8c86fa --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTest.java
@@ -0,0 +1,21 @@ +// Copyright (c) 2021, 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.retrace; + +public class StackTraceWithPcAndNoLineTableTest { + + public static void foo() { + if (System.nanoTime() > 0) { + throw new RuntimeException(); + } + } + + public static void bar() { + foo(); + } + + public static void main(String[] args) { + bar(); + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java new file mode 100644 index 0000000..6783fa7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
@@ -0,0 +1,116 @@ +// Copyright (c) 2021, 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.retrace; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import com.android.tools.r8.naming.retrace.StackTrace; +import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class StackTraceWithPcAndNoLineTableTestRunner extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public StackTraceWithPcAndNoLineTableTestRunner(TestParameters parameters) { + this.parameters = parameters; + } + + private Class<?> getTestClass() { + return StackTraceWithPcAndNoLineTableTest.class; + } + + @Test + public void testReference() throws Exception { + testForRuntime(parameters) + .addProgramClasses(getTestClass()) + .run(parameters.getRuntime(), getTestClass()) + .assertFailureWithErrorThatThrows(RuntimeException.class) + .inspectStackTrace( + stacktrace -> { + assertThat(stacktrace, StackTrace.isSame(getExpectedStackTrace(true))); + }); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(getTestClass()) + .addKeepMainRule(getTestClass()) + .addKeepRules("-keep,allowshrinking,allowobfuscation class * { *; }") + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), getTestClass()) + .assertFailureWithErrorThatThrows(RuntimeException.class) + .inspectStackTrace( + (stacktrace, inspector) -> { + // The source file should be set to the default SourceFile when not kept. + assertEquals( + "SourceFile", + inspector.clazz(getTestClass()).getDexProgramClass().sourceFile.toString()); + // The stack-trace should not have line numbers unless on a VM with DEX PC support. + if (parameters.isDexRuntime() + && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0)) { + if (parameters + .getApiLevel() + .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())) { + // TODO(b/202919530): With PC support it should retrace. + assertThat( + stacktrace, + StackTrace.isSameExceptForLineNumbers(getExpectedStackTrace(true))); + } else { + // Compiling for an API level before PC support it may be unfeasible to include + // the mapping information due to mapping size increase. That is up for + // discussion. + assertThat( + stacktrace, + StackTrace.isSameExceptForLineNumbers(getExpectedStackTrace(true))); + } + } else { + // Having stripped the line-number table, the raw stack will not have line info. + assertThat(stacktrace, StackTrace.isSame(getExpectedStackTrace(false))); + } + }); + } + + private StackTrace getExpectedStackTrace(boolean withLines) { + String className = getTestClass().getName(); + String sourceFile = getTestClass().getSimpleName() + ".java"; + return StackTrace.builder() + .add( + StackTraceLine.builder() + .setClassName(className) + .setFileName(sourceFile) + .setMethodName("foo") + .applyIf(withLines, b -> b.setLineNumber(10)) + .build()) + .add( + StackTraceLine.builder() + .setClassName(className) + .setFileName(sourceFile) + .setMethodName("bar") + .applyIf(withLines, b -> b.setLineNumber(15)) + .build()) + .add( + StackTraceLine.builder() + .setClassName(className) + .setFileName(sourceFile) + .setMethodName("main") + .applyIf(withLines, b -> b.setLineNumber(19)) + .build()) + .build(); + } +}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java index 239697e..7452a5a 100644 --- a/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/RemoveAssertionsTest.java
@@ -187,7 +187,7 @@ return testForR8(staticTemp, backend) .addProgramClasses(ClassWithAssertions.class) .addKeepMainRule(ClassWithAssertions.class) - .addOptionsModification(o -> o.enableInlining = false) + .addOptionsModification(o -> o.inlinerOptions().enableInlining = false) .allowAccessModification() .noMinification() .compile();
diff --git a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java index df19a94..ce08a6a 100644 --- a/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java +++ b/src/test/java/com/android/tools/r8/shaking/FieldTypeTest.java
@@ -13,7 +13,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; -import com.android.tools.r8.VmTestRunner; import com.android.tools.r8.jasmin.JasminBuilder; import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder; import com.android.tools.r8.naming.MemberNaming.FieldSignature; @@ -25,7 +24,6 @@ import com.google.common.collect.ImmutableList; import java.nio.file.Path; import org.junit.Test; -import org.junit.runner.RunWith; public class FieldTypeTest extends TestBase { @@ -126,7 +124,7 @@ // Disable inlining to avoid the (short) tested method from being inlined and then // removed. options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.testing.allowTypeErrors = true; }); @@ -201,15 +199,16 @@ assertEquals(0, javaResult.exitCode); assertThat(javaResult.stdout, containsString(impl2.name)); - AndroidApp processedApp = compileWithR8( - jasminBuilder.build(), - proguardConfig, - internalOptions -> { - // Disable inlining to avoid the (short) tested method from being inlined and then - // removed. - internalOptions.enableInlining = false; - internalOptions.testing.allowTypeErrors = true; - }); + AndroidApp processedApp = + compileWithR8( + jasminBuilder.build(), + proguardConfig, + internalOptions -> { + // Disable inlining to avoid the (short) tested method from being inlined and then + // removed. + internalOptions.inlinerOptions().enableInlining = false; + internalOptions.testing.allowTypeErrors = true; + }); // Run processed (output) program on ART ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
diff --git a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java index c5c0859..0222e3d 100644 --- a/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/shaking/MissingInterfaceTest.java
@@ -38,7 +38,7 @@ .addDontWarn(GoingToBeMissed.class) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(TestClassForB112849320.class) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .addKeepPackageNamesRule(GoingToBeMissed.class.getPackage()) .compile() .addRunClasspathFiles( @@ -64,7 +64,7 @@ .addDefaultRuntimeLibrary(parameters) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(TestClassForB112849320.class) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .addRunClasspathFiles(buildOnDexRuntime(parameters, lib)) .run(parameters.getRuntime(), TestClassForB112849320.class) .assertSuccessWithOutputLines(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java index b99494e..873b1e4 100644 --- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -128,7 +128,7 @@ .addKeepMainRule(B112452064TestMain.class) .addOptionsModification( options -> { - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval; options.enableVerticalClassMerging = enableVerticalClassMerging; }) @@ -227,7 +227,7 @@ proguardConfig, options -> { // Disable inlining to avoid the (short) tested method from being inlined and removed. - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation); }); @@ -306,7 +306,7 @@ .addOptionsModification( options -> { // Disable inlining to avoid the (short) tested method from being inlined and removed. - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation); }) .noMinification() @@ -409,7 +409,7 @@ proguardConfig, options -> { // Disable inlining to avoid the (short) tested method from being inlined and removed. - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; options.callSiteOptimizationOptions().setEnabled(enableArgumentPropagation); });
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java index 3d2e137..d241adb 100644 --- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java +++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -71,7 +71,7 @@ && parameters.getApiLevel().isLessThan(AndroidApiLevel.K), builder -> builder.addDontWarn(ReflectiveOperationException.class)) // Disable inlining to make this test not depend on inlining decisions. - .addOptionsModification(o -> o.enableInlining = false) + .addOptionsModification(o -> o.inlinerOptions().enableInlining = false) .enableProguardTestOptions() .setMinApi(parameters.getApiLevel()) .compile();
diff --git a/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java index 82e9d69..354c5ae 100644 --- a/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java
@@ -86,7 +86,7 @@ // always null, and replace the last call with null-throwing instruction. // However, we want to test return type and parameter type are kept in this scenario. o.callSiteOptimizationOptions().disableDynamicTypePropagationForTesting(); - o.enableInlining = false; + o.inlinerOptions().enableInlining = false; }) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutput(JAVA_OUTPUT)
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java index b21fc5f..86ccd24 100644 --- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -88,7 +88,7 @@ dataResourceConsumer = new DataResourceConsumerForTesting(options.dataResourceConsumer); options.dataResourceConsumer = dataResourceConsumer; - options.enableInliningOfInvokesWithNullableReceivers = false; + options.inlinerOptions().enableInliningOfInvokesWithNullableReceivers = false; }) .enableGraphInspector() .enableMemberValuePropagationAnnotations()
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java index 2fac539..5e17250 100644 --- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java +++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -130,7 +130,7 @@ builder -> builder.addProgramFiles(getProgramFiles(test)), builder -> builder.addProgramDexFileData(getProgramDexFileData(test))) .addKeepRuleFiles(Paths.get(EXAMPLES_DIR, test, "keep-rules.txt")) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .compile() .inspectProguardMap( proguardMap -> {
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java index 869deb0..e480696 100644 --- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java +++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -193,7 +193,7 @@ .addDefaultRuntimeLibrary(parameters) .addOptionsModification( options -> { - options.enableInlining = programFile.contains("inlining"); + options.inlinerOptions().enableInlining = programFile.contains("inlining"); if (optionsConsumer != null) { optionsConsumer.accept(options); }
diff --git a/src/test/java/com/android/tools/r8/shaking/b113138046/NativeMethodTest.java b/src/test/java/com/android/tools/r8/shaking/b113138046/NativeMethodTest.java index 686c35b..9d35176 100644 --- a/src/test/java/com/android/tools/r8/shaking/b113138046/NativeMethodTest.java +++ b/src/test/java/com/android/tools/r8/shaking/b113138046/NativeMethodTest.java
@@ -48,7 +48,7 @@ .setMinApi(parameters.getApiLevel()) .addProgramClassesAndInnerClasses(Keep.class, Data.class, Handler.class, Outer.class) .addKeepRules(config) - .addOptionsModification(options -> options.enableInlining = false) + .addOptionsModification(options -> options.inlinerOptions().enableInlining = false) .compile(); compileResult.runDex2Oat(parameters.getRuntime()).assertNoVerificationErrors(); CodeInspector inspector = compileResult.inspector();
diff --git a/src/test/java/com/android/tools/r8/shaking/interfacebridge/LambdaAbstractMethodErrorTest.java b/src/test/java/com/android/tools/r8/shaking/interfacebridge/LambdaAbstractMethodErrorTest.java index 48576f5..d388cec 100644 --- a/src/test/java/com/android/tools/r8/shaking/interfacebridge/LambdaAbstractMethodErrorTest.java +++ b/src/test/java/com/android/tools/r8/shaking/interfacebridge/LambdaAbstractMethodErrorTest.java
@@ -37,7 +37,7 @@ .addKeepMainRule(Main.class) .addOptionsModification( internalOptions -> { - internalOptions.enableInlining = false; + internalOptions.inlinerOptions().enableInlining = false; internalOptions.enableClassInlining = false; internalOptions.enableVerticalClassMerging = false; })
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java index b2313a7..2851643 100644 --- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java +++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -65,11 +65,9 @@ .addOptionsModification( o -> { o.enableDevirtualization = false; - o.enableInliningOfInvokesWithNullableReceivers = false; - // Tests indirectly check if a certain method is inlined or not, where the target - // method has at least 4 instructions. - o.inliningInstructionLimit = 4; + o.inlinerOptions().enableInliningOfInvokesWithNullableReceivers = false; }) + .enableAlwaysInliningAnnotations() .noMinification() .setMinApi(parameters.getApiLevel()) .compile()
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java b/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java index 97fdb2b..8a5d3fd 100644 --- a/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java +++ b/src/test/java/com/android/tools/r8/shaking/proxy/testclasses/TestClass.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.shaking.proxy.testclasses; +import com.android.tools.r8.AlwaysInline; + public class TestClass extends BaseClass implements SubInterface, Interface2 { public final String name; // Must be public to allow inlining. @@ -11,6 +13,7 @@ this.name = name; } + @AlwaysInline @Override public void method() { System.out.println(name);
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java index c9d30fd..242d5d2 100644 --- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java +++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -72,7 +72,7 @@ private Consumer<InternalOptions> configureOptions(Consumer<InternalOptions> optionsConsumer) { return options -> { // Disable inlining to make sure that code looks as expected. - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; // Disable string concatenation optimization to not bother outlining of StringBuilder usage. options.enableStringConcatenationOptimization = false; // Also apply outline options. @@ -84,7 +84,7 @@ Consumer<OutlineOptions> optionsConsumer) { return options -> { // Disable inlining to make sure that code looks as expected. - options.enableInlining = false; + options.inlinerOptions().enableInlining = false; // Disable the devirtulizer to not remove the super calls options.enableDevirtualization = false; // Disable string concatenation optimization to not bother outlining of StringBuilder usage.
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java index 64b3914..4465113 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -312,10 +312,16 @@ && ((CfStackInstruction) instruction).getOpcode() == opcode; } + @Override public boolean isIfNull() { return instruction instanceof CfIf && ((CfIf) instruction).getOpcode() == Opcodes.IFNULL; } + @Override + public boolean isIfNonNull() { + return instruction instanceof CfIf && ((CfIf) instruction).getOpcode() == Opcodes.IFNONNULL; + } + public boolean isLoad() { return instruction instanceof CfLoad; } @@ -407,4 +413,8 @@ public String toString() { return instruction.toString(); } + + public CfInstruction getInstruction() { + return instruction; + } }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java index 2bfadc8..91f0ea9 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -336,6 +336,18 @@ } @Override + public boolean isIfNull() { + // Not in DEX. + return false; + } + + @Override + public boolean isIfNonNull() { + // Not in DEX. + return false; + } + + @Override public boolean isReturn() { return instruction instanceof Return; } @@ -496,4 +508,8 @@ public String toString() { return instruction.toString(); } + + public Instruction getInstruction() { + return instruction; + } }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java index cf9ce0a..9f55b56 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -98,6 +98,10 @@ boolean isIfEqz(); + boolean isIfNull(); + + boolean isIfNonNull(); + boolean isReturn(); boolean isReturnVoid();
diff --git a/src/test/kotlinR8TestResources/class_inliner_data_class/main.kt b/src/test/kotlinR8TestResources/class_inliner_data_class/main.kt index c8ef79a..f4e2c7a 100644 --- a/src/test/kotlinR8TestResources/class_inliner_data_class/main.kt +++ b/src/test/kotlinR8TestResources/class_inliner_data_class/main.kt
@@ -9,7 +9,9 @@ alpha.right = "l" alpha.left = "r" alpha.rotate() - println("result: ${alpha.toString()}") + // For Kotlin 1.5 we need to have the toString call outside the concat. + val alphaString = alpha.toString() + println("result: ${alphaString} is good") } data class Alpha(var left: String, val middle: String, var right: String) {
diff --git a/tools/compiledump.py b/tools/compiledump.py index fb3b901..dbb4a18 100755 --- a/tools/compiledump.py +++ b/tools/compiledump.py
@@ -52,6 +52,9 @@ help='Path to an R8 jar.', default=None) parser.add_argument( + '--r8-flags', '--r8_flags', + help='Additional option(s) for the compiler.') + parser.add_argument( '-override', help='Do not override any extracted dump in temp-dir', default=False, @@ -341,6 +344,8 @@ cmd.append('-Dcom.android.tools.r8.enableTestAssertions=1') if args.print_times: cmd.append('-Dcom.android.tools.r8.printtimes=1') + if args.r8_flags: + cmd.extend(args.r8_flags.split(' ')) if hasattr(args, 'properties'): cmd.extend(args.properties); cmd.extend(determine_properties(build_properties))
diff --git a/tools/create_r8lib.py b/tools/create_r8lib.py new file mode 100755 index 0000000..329ae8e --- /dev/null +++ b/tools/create_r8lib.py
@@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021, 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. + +import argparse +import os +import subprocess +import sys + +import jdk +import utils + +VERSION_EXTRACTOR = """ +import com.android.tools.r8.Version; +public class VersionExtractor { + public static void main(String[] args) { + System.out.println(Version.LABEL); + } +} +""" + +def parse_options(): + parser = argparse.ArgumentParser(description='Tag R8 Versions') + parser.add_argument( + '--r8jar', + required=True, + help='The R8 jar to compile') + parser.add_argument( + '--output', + required=True, + help='The output path for the r8lib') + parser.add_argument( + '--pg-conf', + action='append', + help='Keep configuration') + parser.add_argument( + '--lib', + action='append', + help='Additional libraries (JDK 1.8 rt.jar already included)') + return parser.parse_args() + +def get_r8_version(r8jar): + with utils.TempDir() as temp: + name = os.path.join(temp, "VersionExtractor.java") + fd = open(name, 'w') + fd.write(VERSION_EXTRACTOR) + fd.close() + cmd = [jdk.GetJavacExecutable(), '-cp', r8jar, name] + print(' '.join(cmd)) + cp_separator = ';' if utils.IsWindows() else ':' + subprocess.check_call(cmd) + output = subprocess.check_output([ + jdk.GetJavaExecutable(), + '-cp', + cp_separator.join([r8jar, os.path.dirname(name)]), + 'VersionExtractor' + ]).decode('UTF-8').strip() + if output == 'main': + return subprocess.check_output( + ['git', 'rev-parse', 'HEAD']).decode('UTF-8').strip() + else: + return output + +def main(): + args = parse_options() + if not os.path.exists(args.r8jar): + print("Could not find jar: " + args.r8jar) + return 1 + version = get_r8_version(args.r8jar) + map_id_template = version + source_file_template = 'R8_%MAP_ID_%MAP_HASH' + # TODO(b/139725780): See if we can remove or lower the heap size (-Xmx8g). + cmd = [jdk.GetJavaExecutable(), '-Xmx8g', '-ea'] + cmd.extend(['-cp', 'build/libs/r8_with_deps.jar', 'com.android.tools.r8.R8']) + cmd.append(args.r8jar) + cmd.append('--classfile') + cmd.extend(['--map-id-template', map_id_template]) + cmd.extend(['--source-file-template', source_file_template]) + cmd.extend(['--output', args.output]) + cmd.extend(['--pg-map-output', args.output + '.map']) + cmd.extend(['--lib', 'third_party/openjdk/openjdk-rt-1.8/rt.jar']) + if args.pg_conf: + for pgconf in args.pg_conf: + cmd.extend(['--pg-conf', pgconf]) + if args.lib: + for lib in args.lib: + cmd.extend(['--lib', lib]) + print(' '.join(cmd)) + subprocess.check_call(cmd) + +if __name__ == '__main__': + sys.exit(main())
diff --git a/tools/golem_build.py b/tools/golem_build.py index cbebc4c..24a1906 100755 --- a/tools/golem_build.py +++ b/tools/golem_build.py
@@ -8,8 +8,8 @@ import gradle import sys -GRADLE_ARGS = ['--no-daemon'] -BUILD_TARGETS = ['R8', 'D8', 'R8LibApiOnly', 'buildExampleJars', +GRADLE_ARGS = ['--no-daemon', '-Pno_internal'] +BUILD_TARGETS = ['R8', 'D8', 'R8Lib', 'buildExampleJars', 'downloadAndroidCts', 'downloadDx'] def Main():
diff --git a/tools/retrace.py b/tools/retrace.py index 94ec1b3..0aa5af2 100755 --- a/tools/retrace.py +++ b/tools/retrace.py
@@ -1,13 +1,14 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2019, 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. import argparse -import jdk +import os import subprocess import sys +import jdk import utils @@ -30,7 +31,7 @@ parser.add_argument( '--map', help='Path to r8lib map.', - default=utils.R8LIB_JAR + '.map') + default=None) parser.add_argument( '--no-r8lib', default=False, @@ -38,7 +39,7 @@ help='Use r8.jar and not r8lib.jar.') parser.add_argument( '--stacktrace', - help='Path to stacktrace file.', + help='Path to stacktrace file (read from stdin if not passed).', default=None) parser.add_argument( '--quiet', @@ -63,11 +64,75 @@ return parser.parse_args() +def get_map_file(args, temp): + # default to using the specified map file. + if args.map: + return args.map + + # next try to extract it from the tag/version options. + map_path = utils.find_cloud_storage_file_from_options('r8lib.jar.map', args) + if map_path: + return map_path + + # next try to extract it from the stack-trace source-file content. + if not args.stacktrace: + if not args.quiet: + print('Waiting for stack-trace input...') + args.stacktrace = os.path.join(temp, 'stacktrace.txt') + open(args.stacktrace, 'w').writelines(sys.stdin.readlines()) + + r8_source_file = None + for line in open(args.stacktrace, 'r'): + start = line.rfind("(R8_") + if start > 0: + end = line.find(":", start) + content = line[start + 1: end] + if r8_source_file: + if content != r8_source_file: + print('WARNING: there are multiple distinct R8 source files:') + print(' ' + r8_source_file) + print(' ' + content) + else: + r8_source_file = content + + if r8_source_file: + (header, r8_version_or_hash, maphash) = r8_source_file.split('_') + if len(r8_version_or_hash) < 40: + args.version = r8_version_or_hash + else: + args.commit_hash = r8_version_or_hash + map_path = None + try: + map_path = utils.find_cloud_storage_file_from_options('r8lib.jar.map', args) + except Exception as e: + print(e) + print('WARNING: Falling back to using local mapping file.') + + if map_path: + check_maphash(map_path, maphash) + return map_path + + # If no other map file was found, use the local mapping file. + return utils.R8LIB_JAR + '.map' + + +def check_maphash(mapping_path, maphash): + map_hash_header = "# pg_map_hash: SHA-256 " + for line in open(mapping_path, 'r'): + if line.startswith(map_hash_header): + infile_maphash = line[len(map_hash_header):].strip() + if infile_maphash != maphash: + print('ERROR: The mapping file hash does not match the R8 line') + print(' In mapping file: ' + infile_maphash) + print(' In source file: ' + maphash) + sys.exit(1) + + def main(): args = parse_arguments() - map_path = utils.find_cloud_storage_file_from_options( - 'r8lib.jar.map', args, orElse=args.map) - return run( + with utils.TempDir() as temp: + map_path = get_map_file(args, temp) + return run( map_path, args.stacktrace, args.no_r8lib, @@ -76,6 +141,7 @@ regex=args.regex, verbose=args.verbose) + def run(map_path, stacktrace, no_r8lib, quiet=False, debug=False, regex=None, verbose=False): retrace_args = [jdk.GetJavaExecutable()]
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py index 30205f9..88c758d 100755 --- a/tools/run_on_app_dump.py +++ b/tools/run_on_app_dump.py
@@ -655,6 +655,7 @@ args = AttrDict({ 'dump': dump_for_app(app_dir, app), 'r8_jar': get_r8_jar(options, temp_dir, shrinker), + 'r8_flags': options.r8_flags, 'ea': False if options.disable_assertions else True, 'version': options.version, 'compiler': 'r8full' if is_full_r8(shrinker) else 'r8', @@ -931,6 +932,8 @@ help='Number of times R8 should be run on each app', default=2, type=int) + result.add_argument('--r8-flags', '--r8_flags', + help='Additional option(s) for the compiler.') result.add_argument('--run-tests', '--run_tests', help='Whether to run instrumentation tests', default=False,
diff --git a/tools/test.py b/tools/test.py index 1f6d450..5da5b71 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -452,7 +452,7 @@ def archive_and_return(return_code, options): if return_code != 0: - if options.archive_failures and os.name != 'nt': + if options.archive_failures: archive_failures(options) return return_code
diff --git a/tools/utils.py b/tools/utils.py index 53aac35..40c5c7a 100644 --- a/tools/utils.py +++ b/tools/utils.py
@@ -316,9 +316,12 @@ if not os.path.isdir(path): raise +def get_gsutil(): + return 'gsutil.py' if os.name != 'nt' else 'gsutil.py.bat' + def upload_dir_to_cloud_storage(directory, destination, is_html=False, public_read=True): # Upload and make the content encoding right for viewing directly - cmd = ['gsutil.py', '-m', 'cp'] + cmd = [get_gsutil(), '-m', 'cp'] if is_html: cmd += ['-z', 'html'] if public_read: @@ -328,7 +331,7 @@ subprocess.check_call(cmd) def upload_file_to_cloud_storage(source, destination, public_read=True): - cmd = ['gsutil.py', 'cp'] + cmd = [get_gsutil(), 'cp'] if public_read: cmd += ['-a', 'public-read'] cmd += [source, destination] @@ -336,17 +339,17 @@ subprocess.check_call(cmd) def delete_file_from_cloud_storage(destination): - cmd = ['gsutil.py', 'rm', destination] + cmd = [get_gsutil(), 'rm', destination] PrintCmd(cmd) subprocess.check_call(cmd) def ls_files_on_cloud_storage(destination): - cmd = ['gsutil.py', 'ls', destination] + cmd = [get_gsutil(), 'ls', destination] PrintCmd(cmd) return subprocess.check_output(cmd) def cat_file_on_cloud_storage(destination, ignore_errors=False): - cmd = ['gsutil.py', 'cat', destination] + cmd = [get_gsutil(), 'cat', destination] PrintCmd(cmd) try: return subprocess.check_output(cmd) @@ -357,12 +360,12 @@ raise e def file_exists_on_cloud_storage(destination): - cmd = ['gsutil.py', 'ls', destination] + cmd = [get_gsutil(), 'ls', destination] PrintCmd(cmd) return subprocess.call(cmd) == 0 def download_file_from_cloud_storage(source, destination, quiet=False): - cmd = ['gsutil.py', 'cp', source, destination] + cmd = [get_gsutil(), 'cp', source, destination] PrintCmd(cmd, quiet=quiet) subprocess.check_call(cmd) @@ -394,7 +397,7 @@ # This is not a problem in our case, but don't ever use this method # for synchronization. def cloud_storage_exists(destination): - cmd = ['gsutil.py', 'ls', destination] + cmd = [get_gsutil(), 'ls', destination] PrintCmd(cmd) exit_code = subprocess.call(cmd) return exit_code == 0