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..1edb900 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