New compiler API for passing startup profile to D8/R8
This adds a new setStartupProfileProvider(StartupProfileProvider) API to D8 and R8 command builders (D8Command$Builder and R8Command$Builder).
StartupProfileProvider is an interface with a single get() method that returns the startup profile as a java.lang.String, and is added to the public API of the compiler.
The startup profile is used by D8 and R8 to ensure that classes used during startup will be present in the primary classes.dex file. Moreover, R8 uses the startup profile to guide its optimizations so that (additional) post-startup code is not added to the app's startup classes.
Change-Id: Ic114a3de785219508a2c634a297f3cef226d5bbf
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 08c7a84..e8e5a04 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DumpInputFlags;
@@ -21,6 +22,7 @@
import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -55,6 +57,7 @@
private final MapIdProvider mapIdProvider;
private final SourceFileProvider sourceFileProvider;
private final boolean isAndroidPlatformBuild;
+ private final List<StartupProfileProvider> startupProfileProviders;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -74,6 +77,7 @@
mapIdProvider = null;
sourceFileProvider = null;
isAndroidPlatformBuild = false;
+ startupProfileProviders = null;
}
BaseCompilerCommand(
@@ -93,7 +97,8 @@
DumpInputFlags dumpInputFlags,
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
- boolean isAndroidPlatformBuild) {
+ boolean isAndroidPlatformBuild,
+ List<StartupProfileProvider> startupProfileProviders) {
super(app);
assert minApiLevel > 0;
assert mode != null;
@@ -113,6 +118,7 @@
this.mapIdProvider = mapIdProvider;
this.sourceFileProvider = sourceFileProvider;
this.isAndroidPlatformBuild = isAndroidPlatformBuild;
+ this.startupProfileProviders = startupProfileProviders;
}
/**
@@ -208,6 +214,10 @@
return isAndroidPlatformBuild;
}
+ List<StartupProfileProvider> getStartupProfileProviders() {
+ return startupProfileProviders;
+ }
+
DumpInputFlags getDumpInputFlags() {
return dumpInputFlags;
}
@@ -249,6 +259,7 @@
private MapIdProvider mapIdProvider = null;
private SourceFileProvider sourceFileProvider = null;
private boolean isAndroidPlatformBuild = false;
+ private List<StartupProfileProvider> startupProfileProviders = new ArrayList<>();
abstract CompilationMode defaultCompilationMode();
@@ -664,6 +675,19 @@
return isAndroidPlatformBuild;
}
+ B addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+ return addStartupProfileProviders(Arrays.asList(startupProfileProviders));
+ }
+
+ B addStartupProfileProviders(Collection<StartupProfileProvider> startupProfileProviders) {
+ this.startupProfileProviders.addAll(startupProfileProviders);
+ return self();
+ }
+
+ List<StartupProfileProvider> getStartupProfileProviders() {
+ return startupProfileProviders;
+ }
+
/**
* Allow to skip to dump into file and dump into directory instruction, this is primarily used
* for chained compilation in L8 so there are no duplicated dumps.
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 76c2966..0ea0232 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationSource;
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
@@ -264,6 +265,35 @@
return self();
}
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
@Override
Builder self() {
return this;
@@ -426,6 +456,7 @@
proguardMapConsumer,
enableMissingLibraryApiModeling,
getAndroidPlatformBuild(),
+ getStartupProfileProviders(),
factory);
}
}
@@ -516,6 +547,7 @@
StringConsumer proguardMapConsumer,
boolean enableMissingLibraryApiModeling,
boolean isAndroidPlatformBuild,
+ List<StartupProfileProvider> startupProfileProviders,
DexItemFactory factory) {
super(
inputApp,
@@ -534,7 +566,8 @@
dumpInputFlags,
mapIdProvider,
null,
- isAndroidPlatformBuild);
+ isAndroidPlatformBuild,
+ startupProfileProviders);
this.intermediate = intermediate;
this.globalSyntheticsConsumer = globalSyntheticsConsumer;
this.desugarGraphConsumer = desugarGraphConsumer;
@@ -654,6 +687,11 @@
internal.configureAndroidPlatformBuild(getAndroidPlatformBuild());
+ // TODO(b/238173796): Change StartupOptions to store a Collection<StartupProfileProvider>.
+ if (getStartupProfileProviders().size() == 1) {
+ internal.getStartupOptions().setStartupProfileProvider(getStartupProfileProviders().get(0));
+ }
+
internal.setDumpInputFlags(getDumpInputFlags());
internal.dumpOptions = dumpOptions();
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index b9fe11b..4069ece 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -117,7 +117,8 @@
dumpInputFlags,
mapIdProvider,
null,
- false);
+ false,
+ null);
this.d8Command = d8Command;
this.r8Command = r8Command;
this.desugaredLibrarySpecification = desugaredLibrarySpecification;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 9818440..68627ea 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationSourceBytes;
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
@@ -447,6 +448,37 @@
return super.createProgramOutputConsumer(path, mode, consumeDataResources);
}
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex. The given startup profiles are also used to disallow optimizations
+ * across the startup and post-startup boundary.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex. The given startup profiles are also used to disallow optimizations
+ * across the startup and post-startup boundary.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
@Override
void validate() {
if (isPrintHelp()) {
@@ -628,7 +660,8 @@
getMapIdProvider(),
getSourceFileProvider(),
enableMissingLibraryApiModeling,
- getAndroidPlatformBuild());
+ getAndroidPlatformBuild(),
+ getStartupProfileProviders());
if (inputDependencyGraphConsumer != null) {
inputDependencyGraphConsumer.finished();
@@ -814,7 +847,8 @@
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
boolean enableMissingLibraryApiModeling,
- boolean isAndroidPlatformBuild) {
+ boolean isAndroidPlatformBuild,
+ List<StartupProfileProvider> startupProfileProviders) {
super(
inputApp,
mode,
@@ -832,7 +866,8 @@
dumpInputFlags,
mapIdProvider,
sourceFileProvider,
- isAndroidPlatformBuild);
+ isAndroidPlatformBuild,
+ startupProfileProviders);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
this.mainDexKeepRules = mainDexKeepRules;
@@ -1029,6 +1064,11 @@
internal.configureAndroidPlatformBuild(getAndroidPlatformBuild());
+ // TODO(b/238173796): Change StartupOptions to store a Collection<StartupProfileProvider>.
+ if (getStartupProfileProviders().size() == 1) {
+ internal.getStartupOptions().setStartupProfileProvider(getStartupProfileProviders().get(0));
+ }
+
if (!DETERMINISTIC_DEBUGGING) {
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
internal.threadCount = getThreadCount();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
index afb4691..86df003 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -7,6 +7,9 @@
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
import com.android.tools.r8.StringResource;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.SystemPropertyUtils;
import java.nio.file.Paths;
@@ -53,7 +56,23 @@
SystemPropertyUtils.applySystemProperty(
"com.android.tools.r8.startup.profile",
propertyValue ->
- StringResource.fromFile(Paths.get(propertyValue))::getStringWithRuntimeException,
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return StringResource.fromFile(Paths.get(propertyValue))
+ .getStringWithRuntimeException();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ },
() -> null);
public boolean isMinimalStartupDexEnabled() {
diff --git a/src/main/java/com/android/tools/r8/startup/StartupClassBuilder.java b/src/main/java/com/android/tools/r8/startup/StartupClassBuilder.java
index d156ebd..43a63b4 100644
--- a/src/main/java/com/android/tools/r8/startup/StartupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/startup/StartupClassBuilder.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.startup;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.references.ClassReference;
+/** Interface for providing information about a startup class to the compiler. */
+@Keep
public interface StartupClassBuilder {
StartupClassBuilder setClassReference(ClassReference classReference);
diff --git a/src/main/java/com/android/tools/r8/startup/StartupMethodBuilder.java b/src/main/java/com/android/tools/r8/startup/StartupMethodBuilder.java
index fe3bb70..0c1efe2 100644
--- a/src/main/java/com/android/tools/r8/startup/StartupMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/startup/StartupMethodBuilder.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.startup;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.references.MethodReference;
+/** Interface for providing information about a startup method to the compiler. */
+@Keep
public interface StartupMethodBuilder {
StartupMethodBuilder setMethodReference(MethodReference methodReference);
diff --git a/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java b/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
index 9764a06..d3626a1 100644
--- a/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
+++ b/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
@@ -4,15 +4,40 @@
package com.android.tools.r8.startup;
+import com.android.tools.r8.Keep;
import java.util.function.Consumer;
+/** Interface for providing a startup profile to the compiler. */
+@Keep
public interface StartupProfileBuilder {
+ /** API for adding information about a startup class to the compiler. */
StartupProfileBuilder addStartupClass(Consumer<StartupClassBuilder> startupClassBuilderConsumer);
+ /** API for adding information about a startup method to the compiler. */
StartupProfileBuilder addStartupMethod(
Consumer<StartupMethodBuilder> startupMethodBuilderConsumer);
+ /**
+ * API for adding information about a synthetic startup method to the compiler.
+ *
+ * <p>When shrinking an app using R8, the names of synthetic classes may differ from the synthetic
+ * names that arise from dexing the app using D8. Therefore, synthetic classes and methods should
+ * not be added to the startup profile using {@link #addStartupClass(Consumer)} and {@link
+ * #addStartupMethod(Consumer)}.
+ *
+ * <p>Instead, synthetic items should be added to the startup profile using this method, which
+ * takes the name of the synthetic context instead of the synthetic name. The addition of the
+ * synthetic context will be interpreted as the presence of any method on any synthetic class that
+ * has been synthesized from the synthetic context.
+ *
+ * <p>Example: Instead of adding "Lcom/example/MainActivity$ExternalSynthetic0;->m()V" as a
+ * (non-synthetic) startup method, a synthetic startup method should be added with synthetic
+ * context "Lcom/example/MainActivity;".
+ *
+ * <p>NOTE: This should only be used when supplying a startup profile that is generated from an
+ * unobfuscated build of the app to R8.
+ */
StartupProfileBuilder addSyntheticStartupMethod(
Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java b/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java
index 3c4a036..4d98373 100644
--- a/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java
+++ b/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java
@@ -4,10 +4,17 @@
package com.android.tools.r8.startup;
-/** Interface for providing a startup profile to the compiler. */
-@FunctionalInterface
-public interface StartupProfileProvider {
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.Resource;
+/** Interface for providing a startup profile to the compiler. */
+@Keep
+public interface StartupProfileProvider extends Resource {
+
+ // TODO(b/238173796): Change the implementation to use the new API below.
/** Return the startup profile. */
String get();
+
+ /** Provides the startup profile by callbacks to the given {@param startupProfileBuilder}. */
+ void getStartupProfile(StartupProfileBuilder startupProfileBuilder);
}
diff --git a/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java b/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java
index a1f9c49..281bac7 100644
--- a/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.startup;
+import com.android.tools.r8.Keep;
import com.android.tools.r8.references.ClassReference;
+/** Interface for providing information about a synthetic startup method to the compiler. */
+@Keep
public interface SyntheticStartupMethodBuilder {
SyntheticStartupMethodBuilder setSyntheticContextReference(ClassReference classReference);
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 4150236..52d84fc 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.errors.UnsupportedFeatureDiagnostic;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
index 3288e20..28f7855 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -7,9 +7,12 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupProfile;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
@@ -63,7 +66,23 @@
toDexType(startupClass, dexItemFactory))
.build())))
.build();
- StartupProfileProvider startupProfileProvider = startupProfile::serializeToString;
+ StartupProfileProvider startupProfileProvider =
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return startupProfile.serializeToString();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
options.getStartupOptions().setStartupProfileProvider(startupProfileProvider);
})
.addHorizontallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
index 33aab91..7a10578 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.compilerapi.mockdata.MockClass;
import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
+import com.android.tools.r8.compilerapi.mockdata.PostStartupMockClass;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -75,6 +76,10 @@
return MockClassWithAssertion.class;
}
+ public Class<?> getPostStartupMockClass() {
+ return PostStartupMockClass.class;
+ }
+
public Path getJava8RuntimeJar() {
return Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar");
}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index a47d4ec..6d12c20 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -17,7 +17,9 @@
import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest;
import com.android.tools.r8.compilerapi.mockdata.MockClass;
import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
+import com.android.tools.r8.compilerapi.mockdata.PostStartupMockClass;
import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest;
+import com.android.tools.r8.compilerapi.startupprofile.StartupProfileApiTest;
import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
import com.android.tools.r8.compilerapi.wrappers.CommandLineParserTest;
import com.android.tools.r8.compilerapi.wrappers.EnableMissingLibraryApiModelingTest;
@@ -49,7 +51,7 @@
UnsupportedFeaturesDiagnosticApiTest.ApiTest.class);
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of();
+ ImmutableList.of(StartupProfileApiTest.ApiTest.class);
private final TemporaryFolder temp;
@@ -74,7 +76,11 @@
@Override
public List<Class<?>> getAdditionalClassesForTests() {
- return ImmutableList.of(CompilerApiTest.class, MockClass.class, MockClassWithAssertion.class);
+ return ImmutableList.of(
+ CompilerApiTest.class,
+ MockClass.class,
+ MockClassWithAssertion.class,
+ PostStartupMockClass.class);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
index 0aae1dd..a2d7693 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
@@ -36,7 +36,7 @@
/**
* If this test fails the test.jar needs to be regenerated and uploaded to cloud storage.
*
- * <p>See: {@code CompilerApiTestCollection.main} to regenerate.
+ * <p>See: {@link CompilerApiTestCollectionTest#main} to regenerate.
*
* <p>To preserve compatibility, make sure only to regenerate together with test changes and with
* NO changes to the compiler itself.
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java
index ee135e8..0ef8064 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java
@@ -15,9 +15,8 @@
* Base runner for all compiler API tests.
*
* <p>Using this runner will automatically create an externalized variant of the test. That is
- * useful to more quickely ensure the test itself is not using resources that are not available.
- * Note however, that it does not prevent using non-kept code in the compilers unless testing with
- * r8lib!
+ * useful to more quickly ensure the test itself is not using resources that are not available. Note
+ * however, that it does not prevent using non-kept code in the compilers unless testing with r8lib!
*/
@RunWith(Parameterized.class)
public abstract class CompilerApiTestRunner extends TestBase {
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mockdata/PostStartupMockClass.java b/src/test/java/com/android/tools/r8/compilerapi/mockdata/PostStartupMockClass.java
new file mode 100644
index 0000000..6529022
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/mockdata/PostStartupMockClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, 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.compilerapi.mockdata;
+
+// Class to use as data for the compilation.
+public class PostStartupMockClass {}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java
new file mode 100644
index 0000000..f8bc404
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java
@@ -0,0 +1,201 @@
+// Copyright (c) 2022, 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.compilerapi.startupprofile;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.BiConsumer;
+import org.junit.Test;
+
+public class StartupProfileApiTest extends CompilerApiTestRunner {
+
+ private static final int FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX = 21;
+
+ public StartupProfileApiTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void testD8ArrayApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runD8(ApiTest::addStartupProfileProviderUsingArrayApi, programConsumer));
+ }
+
+ @Test
+ public void testD8CollectionApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runD8(ApiTest::addStartupProfileProviderUsingCollectionApi, programConsumer));
+ }
+
+ @Test
+ public void testR8ArrayApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runR8(ApiTest::addStartupProfileProviderUsingArrayApi, programConsumer));
+ }
+
+ @Test
+ public void testR8CollectionApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runR8(ApiTest::addStartupProfileProviderUsingCollectionApi, programConsumer));
+ }
+
+ private void runTest(ApiTest test, ThrowingConsumer<ProgramConsumer, Exception> testRunner)
+ throws Exception {
+ Path output = temp.newFolder().toPath();
+ testRunner.accept(new DexIndexedConsumer.DirectoryConsumer(output));
+ assertThat(
+ new CodeInspector(output.resolve("classes.dex")).clazz(test.getMockClass()), isPresent());
+ // TODO(b/238173796): The PostStartupMockClass should be in classes2.dex.
+ assertThat(
+ new CodeInspector(output.resolve("classes.dex")).clazz(test.getPostStartupMockClass()),
+ isPresent());
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ private StartupProfileProvider getStartupProfileProvider() {
+ return new StartupProfileProvider() {
+ @Override
+ public String get() {
+ // Intentionally empty. All uses of this API should be rewritten to use getStartupProfile.
+ return "";
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder ->
+ startupClassBuilder.setClassReference(Reference.classFromClass(getMockClass())));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+ }
+
+ public void runD8(
+ BiConsumer<D8Command.Builder, StartupProfileProvider> startupProfileProviderInstaller,
+ ProgramConsumer programConsumer)
+ throws Exception {
+ D8Command.Builder commandBuilder =
+ D8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addClassProgramData(getBytesForClass(getPostStartupMockClass()), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setMinApiLevel(FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX)
+ .setProgramConsumer(programConsumer);
+ startupProfileProviderInstaller.accept(commandBuilder, getStartupProfileProvider());
+ D8.run(commandBuilder.build());
+ }
+
+ public void runR8(
+ BiConsumer<R8Command.Builder, StartupProfileProvider> startupProfileProviderInstaller,
+ ProgramConsumer programConsumer)
+ throws Exception {
+ R8Command.Builder commandBuilder =
+ R8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addClassProgramData(getBytesForClass(getPostStartupMockClass()), Origin.unknown())
+ .addProguardConfiguration(
+ Collections.singletonList("-keep class * { *; }"), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setMinApiLevel(FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX)
+ .setProgramConsumer(programConsumer);
+ startupProfileProviderInstaller.accept(commandBuilder, getStartupProfileProvider());
+ R8.run(commandBuilder.build());
+ }
+
+ @Test
+ public void testD8ArrayApi() throws Exception {
+ runD8(ApiTest::addStartupProfileProviderUsingArrayApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingArrayApi(
+ D8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ StartupProfileProvider[] startupProfileProviders =
+ new StartupProfileProvider[] {startupProfileProvider};
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ @Test
+ public void testD8CollectionApi() throws Exception {
+ runD8(
+ ApiTest::addStartupProfileProviderUsingCollectionApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingCollectionApi(
+ D8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ Collection<StartupProfileProvider> startupProfileProviders =
+ Collections.singleton(startupProfileProvider);
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ @Test
+ public void testR8ArrayApi() throws Exception {
+ runR8(ApiTest::addStartupProfileProviderUsingArrayApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingArrayApi(
+ R8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ StartupProfileProvider[] startupProfileProviders =
+ new StartupProfileProvider[] {startupProfileProvider};
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ @Test
+ public void testR8CollectionApi() throws Exception {
+ runR8(
+ ApiTest::addStartupProfileProviderUsingCollectionApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingCollectionApi(
+ R8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ Collection<StartupProfileProvider> startupProfileProviders =
+ Collections.singleton(startupProfileProvider);
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
index 1e65ba1..c7211db 100644
--- a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
@@ -19,6 +19,9 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ZipUtils;
@@ -236,8 +239,24 @@
Path outDirectory)
throws Exception {
StartupProfileProvider startupProfileProvider =
- StringResource.fromFile(chromeDirectory.resolve("startup.txt"))
- ::getStringWithRuntimeException;
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return StringResource.fromFile(chromeDirectory.resolve("startup.txt"))
+ .getStringWithRuntimeException();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+
buildR8(
testBuilder ->
testBuilder.addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index ad1af08..ad43216 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.experimental.startup.StartupConfigurationParser;
import com.android.tools.r8.experimental.startup.StartupItem;
import com.android.tools.r8.experimental.startup.StartupProfile;
@@ -21,9 +22,11 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ClassReferenceUtils;
@@ -157,7 +160,23 @@
builder.addStartupItem(
convertStartupItemToDex(startupItem, dexItemFactory))))
.build();
- StartupProfileProvider startupProfileProvider = startupProfile::serializeToString;
+ StartupProfileProvider startupProfileProvider =
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return startupProfile.serializeToString();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
options.getStartupOptions().setStartupProfileProvider(startupProfileProvider);
});
}
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
index 4fab8f3..040d696 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-b8d49792adec87b9eddd2753cc7a46b508157d8e
\ No newline at end of file
+98dc48c246bd0855133e138c2cd2b5835cf862cf
\ No newline at end of file