Merge commit '3295ee8ba91bb782b73433ea8f0859e834cf57a3' into dev-release
diff --git a/build.gradle b/build.gradle
index 90d75ef..db4592a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -349,7 +349,7 @@
"android_jar/lib-v31",
"android_jar/lib-v32",
"android_jar/lib-v33",
- "android_jar/lib-master",
+ "android_jar/lib-master",
"api_database/api_database",
"api-outlining/simple-app-dump",
"binary_compatibility_tests/compiler_api_tests",
diff --git a/scripts/add-android-jar.sh b/scripts/add-android-jar.sh
index c83aa2e..eabdedc 100755
--- a/scripts/add-android-jar.sh
+++ b/scripts/add-android-jar.sh
@@ -16,8 +16,8 @@
SDK_HOME=$HOME/Android/Sdk
# Modify these to match the SDK android.jar to add.
-SDK_DIR_NAME=android-Sv2
-SDK_VERSION=32
+SDK_DIR_NAME=android-33
+SDK_VERSION=33
SDK_DIR=$SDK_HOME/platforms/$SDK_DIR_NAME
THIRD_PARTY_ANDROID_JAR=third_party/android_jar
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_human_comments.md b/src/library_desugar/jdk11/desugar_jdk_libs_human_comments.md
index 3fdcd72..9f79e04 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_human_comments.md
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_human_comments.md
@@ -1,20 +1,25 @@
# Description of the human desugared library configuration file
-## Version
+## Top-level flags
+
+### Identifier
+
+The field `identifier` is the maven-coordinated id for the desugared library
+configuration file with the group id, the artifact id and the version number
+joined with colon.
+
+### Version
The field `configuration_format_version` encodes a versioning number internal to
R8/D8 in the form of an unsigned integer. It allows R8/D8 to know if the file
-given is supported. If the number if greater or equal to 100, the file is
-encoded using the human flags (by opposition to the legacy flags). Human flags
-are not shipped to external users. Human flags can be converted to machine flags
-which are shipped to external users. Users internal to Google are allowed to use
-directly human flags if we can easily update the file without backward
+given is supported. If the number is between 100 and 200, the file is encoded
+using the human flags (by opposition to the legacy and machine flags). Human
+flags are not shipped to external users. Human flags can be converted to machine
+flags which are shipped to external users. Users internal to Google are allowed
+to use directly human flags if we can easily update the file without backward
compatibility issues.
-The field `identifier` is the maven-coordinated id for the desugared library
-configuration file.
-
-## Required compilation API level
+### Required compilation API level
The field `required_compilation_api_level` encodes the minimal Android API level
required for the desugared library to be compiled correctly. If the API of
@@ -22,13 +27,13 @@
lower than this level, one has to upgrade the SDK version used to be able to use
desugared libraries.
-## Synthesize prefix
+### Synthesize prefix
-The field `synthesized_library_classes_package_prefix` is used both to prefix
-type names of synthetic classes created during the L8 compilation and for some
-of the rewritings.
+The field `synthesized_library_classes_package_prefix` is used to prefix type
+names of synthetic classes created during the L8 compilation. It is also used
+for minification in the `java.` namespace to avoid collisions with platforms.
-## Library callbacks
+### Library callbacks
The field `support_all_callbacks_from_library` is set if D8/R8 should generate
extra callbacks, i.e., methods that may be called from specific library
@@ -51,10 +56,16 @@
### Flag rewrite_prefix
`prefix: rewrittenPrefix`
-D8/R8 identifies any class type matching the prefix, and rewrite such types with
-the new prefix. Types not present as class types are not rewritten. Implicitly,
-all synthetic types derived from the matching type are also rewritten (lambdas
-and backports in the class, etc.).
+D8/R8 identifies any class which type matches the prefix, and rewrite such types
+with the new prefix. Types not present as class types are not rewritten.
+Implicitly, all synthetic types derived from the matching type are also
+rewritten (lambdas and backports in the class, etc.). Lastly, types referenced
+directly or indirectly from other flags (method retargeting, custom conversions,
+emulated interfaces, api generic type conversion) are identified and rewritten.
+
+The exact list of types is computed when generating the machine specification
+and the pattern matching never applies to user code, but instead to the
+desugared library jar and `android.jar`.
Example:
`foo.: f$.`
@@ -62,6 +73,31 @@
foo.Foo -> f$.Foo. A type present foo.Bar, which is not the type of any class,
will not generate any rewrite rule.
+### Flag maintain_prefix
+
+`prefix`
+D8/R8 identifies any class which type matches the prefix, and maintain such
+class in desugared library in the `java` namespace. Using this flag implicitly
+means that at runtime, either a class from the bootclasspath with the same name
+will take precedence and be used, or this class will be used on lower api
+levels.
+
+Using `maintain_prefix` allows desugared library code to work seamlessly with
+recent apis (no wrappers or conversions required), at the cost of preventing
+some signature changes (the signature in desugared library, even when shrinking,
+has to match the library one). This can be done only with classes which behavior
+is identical between desugared library and platform. Lastly, derived synthetic
+classes from code in such classes (lambdas, etc.) are still moved to the new
+namespace to avoid collisions with platform, so one has to be extra careful with
+package private access being broken in this set-up.
+
+Example:
+`foo.`
+A class present with the type `foo.Foo` will be maintained in the output as
+`foo.Foo`. If `synthesized_library_classes_package_prefix` is `f$`, then all the
+derived synthetic classes such as lambdas inside `foo.Foo` will be generated as
+`f$.Foo$lambda-hash`.
+
### Flag rewrite_derived_prefix
`prefix: { fromPrefix: toPrefix }`
@@ -74,6 +110,56 @@
A class present with the type foo.Foo will generate a rewrite rule:
f$.Foo -> foo.Foo.
+### Flag dont_rewrite_prefix
+
+`prefix`
+D8/R8 identifies any class type matching the prefix, does not rewrite it and
+shrinks it away from the input. This flags takes precedence over
+`rewrite_prefix` and `rewrite_derived_prefix`, allowing to disable prefix
+rewriting on subpatterns.
+
+Example:
+`foo.`
+No class prefixed with foo. will be rewritten or kept in the output.
+
+### Flag never_outline_api
+
+`method`
+D8/R8 tries to outline api calls working only on high api levels and requiring
+conversions as much as possible to share the code and avoid soft verification
+errors. Methods specified here are never outlined. This is usually worse for the
+users, unless the api is expected to introspect the stack, in which case the
+extra frame is confusing.
+
+### Flag api_generic_types_conversion
+
+`methodApi: [i0, conversionMethod0, ..., iN, conversionMethodN]`
+D8/R8 automatically generates conversions surrounding api calls with rewritten
+types which are specified in custom conversions/wrappers. D8/R8 does not
+automatically convert types from generic types such as collection items, or
+conversion requiring to convert multiple values in general
+
+This flag is used to specify a plateform api (methodApi) which parameters or
+return value require a conversion different from the default one. The value is
+an array of pair. The pair's key (i0, .., iN) is the parameter index if greater
+or equal to 0, or the return type if -1. The pair's value (conversionMethod0,
+.., N) is the conversion method to use to convert the value.
+
+Example:
+`void bar(foo.Foo): [0, foo.Foo FooConverter#convertFoo(f$.Foo)]`
+When generating conversion for the api bar, the parameter 0 foo.Foo will be
+converted using FooConverter#convertFoo instead of the default conversion logic.
+
+### Flag retarget_static_field
+
+`field: retargetField`
+D8/R8 rewrites all references from the static field to the static retargetField.
+
+Example:
+`int Foo#bar: int Zorg#foo`
+D8/R8 rewrites all references to the field named bar in Foo to the field named
+foo in Zorg.
+
### Flag retarget_method
`methodToRetarget: retargetType`
@@ -106,6 +192,13 @@
the virtual dispatch is still valid and will correctly call the overrides if
present.
+### Flag covariant_retarget_method
+
+`methodWithCovariantReturnType: alternativeReturnType`
+Any invoke to methodWithCovariantReturnType will be rewritten to a call to the
+same method with the alternativeReturnType and a checkcast. This is used to deal
+with covariant return types which are not present in desugared library.
+
### Flag amend_library_method
`modifiers method`
@@ -117,6 +210,12 @@
This flag amends the library to introduce the method, so resolution can find it
and retarget it correctly.
+### Flag amend_library_field
+
+`modifiers field`
+Similar to amend_library_method, adds a field into the library so that field
+resolution can find it and it can be retargeted.
+
### Flag dont_retarget
`type`
@@ -164,12 +263,13 @@
Type convert(RewrittenType)
RewrittenType convert(Type)
-## Extra keep rules
+## Shrinker config
-The last field is `extra_keep_rules`, it includes keep rules that are appended
-by L8 when shrinking the desugared library. It includes keep rules related to
+The last field is `shrinker_config`, it includes keep rules that are appended by
+L8 when shrinking the desugared library. It includes keep rules related to
reflection inside the desugared library, related to enum to have EnumSet working
-and to keep the j$ prefix.
+and to keep the j$ prefix. It also includes various keep rules to suppor common
+serializers.
## Copyright
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index e8e5a04..02e06d1 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -58,6 +58,7 @@
private final SourceFileProvider sourceFileProvider;
private final boolean isAndroidPlatformBuild;
private final List<StartupProfileProvider> startupProfileProviders;
+ private final ClassConflictResolver classConflictResolver;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -78,6 +79,7 @@
sourceFileProvider = null;
isAndroidPlatformBuild = false;
startupProfileProviders = null;
+ classConflictResolver = null;
}
BaseCompilerCommand(
@@ -98,7 +100,8 @@
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
boolean isAndroidPlatformBuild,
- List<StartupProfileProvider> startupProfileProviders) {
+ List<StartupProfileProvider> startupProfileProviders,
+ ClassConflictResolver classConflictResolver) {
super(app);
assert minApiLevel > 0;
assert mode != null;
@@ -119,6 +122,7 @@
this.sourceFileProvider = sourceFileProvider;
this.isAndroidPlatformBuild = isAndroidPlatformBuild;
this.startupProfileProviders = startupProfileProviders;
+ this.classConflictResolver = classConflictResolver;
}
/**
@@ -140,7 +144,8 @@
.setMinApi(getMinApiLevel())
.setOptimizeMultidexForLinearAlloc(isOptimizeMultidexForLinearAlloc())
.setThreadCount(getThreadCount())
- .setDesugarState(getDesugarState());
+ .setDesugarState(getDesugarState())
+ .setStartupProfileProviders(getStartupProfileProviders());
if (getAndroidPlatformBuild()) {
builder.setAndroidPlatformBuild(true);
}
@@ -218,6 +223,10 @@
return startupProfileProviders;
}
+ ClassConflictResolver getClassConflictResolver() {
+ return classConflictResolver;
+ }
+
DumpInputFlags getDumpInputFlags() {
return dumpInputFlags;
}
@@ -260,6 +269,7 @@
private SourceFileProvider sourceFileProvider = null;
private boolean isAndroidPlatformBuild = false;
private List<StartupProfileProvider> startupProfileProviders = new ArrayList<>();
+ private ClassConflictResolver classConflictResolver = null;
abstract CompilationMode defaultCompilationMode();
@@ -753,6 +763,9 @@
+ " or earlier");
}
}
+ if (hasDesugaredLibraryConfiguration() && getAndroidPlatformBuild()) {
+ reporter.error("Android platform builds cannot use desugared library");
+ }
super.validate();
}
@@ -782,5 +795,21 @@
List<Consumer<Inspector>> getOutputInspections() {
return outputInspections;
}
+
+ /**
+ * Set a conflict resolver to determine which class definition to use in case of duplicates.
+ *
+ * <p>If no resolver is set, the compiler will fail compilation in case of duplicates.
+ *
+ * @param resolver Resolver for choosing between duplicate classes.
+ */
+ public B setClassConflictResolver(ClassConflictResolver resolver) {
+ this.classConflictResolver = resolver;
+ return self();
+ }
+
+ ClassConflictResolver getClassConflictResolver() {
+ return classConflictResolver;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ClassConflictResolver.java b/src/main/java/com/android/tools/r8/ClassConflictResolver.java
new file mode 100644
index 0000000..8136f17
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ClassConflictResolver.java
@@ -0,0 +1,30 @@
+// 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;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import java.util.Collection;
+
+@Keep
+public interface ClassConflictResolver {
+
+ /**
+ * Callback called in case the compiler is provided with duplicate class definitions.
+ *
+ * <p>The callback may be called multiple times for the same type with different origins, or it
+ * may be called once with all origins. Assuming the client has provided unique origins for the
+ * various inputs, the number of origins in any call will be at least two.
+ *
+ * <p>Note that all the duplicates are in the program's compilation unit. In other words, none of
+ * them are classpath or library definitions.
+ *
+ * @param reference The type reference of the duplicated class.
+ * @param origins The multiple origins of the class.
+ * @param handler Diagnostics handler for reporting.
+ * @return Returns the origin to use or null to fail compilation.
+ */
+ Origin resolveDuplicateClass(
+ ClassReference reference, Collection<Origin> origins, DiagnosticsHandler handler);
+}
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 172bfa2..4eba3b9 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
@@ -457,6 +458,7 @@
enableMissingLibraryApiModeling,
getAndroidPlatformBuild(),
getStartupProfileProviders(),
+ getClassConflictResolver(),
factory);
}
}
@@ -548,6 +550,7 @@
boolean enableMissingLibraryApiModeling,
boolean isAndroidPlatformBuild,
List<StartupProfileProvider> startupProfileProviders,
+ ClassConflictResolver classConflictResolver,
DexItemFactory factory) {
super(
inputApp,
@@ -567,7 +570,8 @@
mapIdProvider,
null,
isAndroidPlatformBuild,
- startupProfileProviders);
+ startupProfileProviders,
+ classConflictResolver);
this.intermediate = intermediate;
this.globalSyntheticsConsumer = globalSyntheticsConsumer;
this.desugarGraphConsumer = desugarGraphConsumer;
@@ -690,10 +694,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.getStartupOptions().setStartupProfileProviders(getStartupProfileProviders());
+
+ internal.programClassConflictResolver =
+ ProgramClassCollection.wrappedConflictResolver(
+ getClassConflictResolver(), internal.reporter);
internal.setDumpInputFlags(getDumpInputFlags());
internal.dumpOptions = dumpOptions();
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index 4f5b985..c85fe0d 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -78,6 +78,7 @@
.addAll(ParseFlagInfoImpl.getAssertionsFlags())
.add(ParseFlagInfoImpl.getThreadCount())
.add(ParseFlagInfoImpl.getMapDiagnostics())
+ .add(ParseFlagInfoImpl.getAndroidPlatformBuild())
.add(ParseFlagInfoImpl.getVersion("d8"))
.add(ParseFlagInfoImpl.getHelp())
.build();
@@ -302,6 +303,8 @@
} else if (arg.equals("--desugared-lib-pg-conf-output")) {
StringConsumer consumer = new StringConsumer.FileConsumer(Paths.get(nextArg));
builder.setDesugaredLibraryKeepRuleConsumer(consumer);
+ } else if (arg.equals("--android-platform-build")) {
+ builder.setAndroidPlatformBuild(true);
} else if (arg.startsWith("--")) {
if (tryParseAssertionArgument(builder, arg, origin)) {
continue;
diff --git a/src/main/java/com/android/tools/r8/KeepMethodForCompileDump.java b/src/main/java/com/android/tools/r8/KeepMethodForCompileDump.java
new file mode 100644
index 0000000..b49f8d0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/KeepMethodForCompileDump.java
@@ -0,0 +1,7 @@
+// 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;
+
+@Keep
+public @interface KeepMethodForCompileDump {}
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 4069ece..7a2950d 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
@@ -99,6 +100,7 @@
int threadCount,
DumpInputFlags dumpInputFlags,
MapIdProvider mapIdProvider,
+ ClassConflictResolver classConflictResolver,
DexItemFactory factory) {
super(
inputApp,
@@ -118,7 +120,8 @@
mapIdProvider,
null,
false,
- null);
+ null,
+ classConflictResolver);
this.d8Command = d8Command;
this.r8Command = r8Command;
this.desugaredLibrarySpecification = desugaredLibrarySpecification;
@@ -208,6 +211,10 @@
.build(),
getAssertionsConfiguration());
+ internal.programClassConflictResolver =
+ ProgramClassCollection.wrappedConflictResolver(
+ getClassConflictResolver(), internal.reporter);
+
if (!DETERMINISTIC_DEBUGGING) {
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
internal.threadCount = getThreadCount();
@@ -425,6 +432,7 @@
getThreadCount(),
getDumpInputFlags(),
getMapIdProvider(),
+ getClassConflictResolver(),
factory);
}
}
diff --git a/src/main/java/com/android/tools/r8/ParseFlagInfoImpl.java b/src/main/java/com/android/tools/r8/ParseFlagInfoImpl.java
index ad88315..af8d407 100644
--- a/src/main/java/com/android/tools/r8/ParseFlagInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/ParseFlagInfoImpl.java
@@ -156,6 +156,13 @@
"Note that fatal compiler errors cannot be mapped.");
}
+ public static ParseFlagInfoImpl getAndroidPlatformBuild() {
+ return flag0(
+ "--android-platform-build",
+ "Compile as a platform build where the runtime/bootclasspath",
+ "is assumed to be the version specified by --min-api.");
+ }
+
public static ParseFlagInfoImpl flag0(String flag, String... help) {
return flag(flag, Collections.emptyList(), Arrays.asList(help));
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 6adecce..cef341c 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
+import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
@@ -661,7 +662,8 @@
getSourceFileProvider(),
enableMissingLibraryApiModeling,
getAndroidPlatformBuild(),
- getStartupProfileProviders());
+ getStartupProfileProviders(),
+ getClassConflictResolver());
if (inputDependencyGraphConsumer != null) {
inputDependencyGraphConsumer.finished();
@@ -848,7 +850,8 @@
SourceFileProvider sourceFileProvider,
boolean enableMissingLibraryApiModeling,
boolean isAndroidPlatformBuild,
- List<StartupProfileProvider> startupProfileProviders) {
+ List<StartupProfileProvider> startupProfileProviders,
+ ClassConflictResolver classConflictResolver) {
super(
inputApp,
mode,
@@ -867,7 +870,8 @@
mapIdProvider,
sourceFileProvider,
isAndroidPlatformBuild,
- startupProfileProviders);
+ startupProfileProviders,
+ classConflictResolver);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
this.mainDexKeepRules = mainDexKeepRules;
@@ -1067,10 +1071,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.getStartupOptions().setStartupProfileProviders(getStartupProfileProviders());
+
+ internal.programClassConflictResolver =
+ ProgramClassCollection.wrappedConflictResolver(
+ getClassConflictResolver(), internal.reporter);
if (!DETERMINISTIC_DEBUGGING) {
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 3b80233..9ffcdd2 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -103,6 +103,7 @@
"The <template> can reference the variables:",
" %MAP_ID: map id (e.g., value of --map-id-template).",
" %MAP_HASH: compiler generated mapping hash."))
+ .add(ParseFlagInfoImpl.getAndroidPlatformBuild())
.add(ParseFlagInfoImpl.getVersion("r8"))
.add(ParseFlagInfoImpl.getHelp())
.build();
@@ -298,6 +299,8 @@
} else if (arg.equals("--source-file-template")) {
builder.setSourceFileProvider(
SourceFileTemplateProvider.create(nextArg, builder.getReporter()));
+ } else if (arg.equals("--android-platform-build")) {
+ builder.setAndroidPlatformBuild(true);
} else if (arg.startsWith("--")) {
if (tryParseAssertionArgument(builder, arg, argsOrigin)) {
continue;
diff --git a/src/main/java/com/android/tools/r8/StringResource.java b/src/main/java/com/android/tools/r8/StringResource.java
index 515f2ab..4d097e0 100644
--- a/src/main/java/com/android/tools/r8/StringResource.java
+++ b/src/main/java/com/android/tools/r8/StringResource.java
@@ -105,13 +105,5 @@
throw new ResourceException(origin, e);
}
}
-
- public String getStringWithRuntimeException() {
- try {
- return getString();
- } catch (ResourceException e) {
- throw new RuntimeException(e);
- }
- }
}
}
diff --git a/src/main/java/com/android/tools/r8/TextInputStream.java b/src/main/java/com/android/tools/r8/TextInputStream.java
new file mode 100644
index 0000000..433df09
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/TextInputStream.java
@@ -0,0 +1,16 @@
+// 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;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+@Keep
+public interface TextInputStream {
+
+ InputStream getInputStream();
+
+ Charset getCharset();
+}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 4932691..3bd5968 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.naming.KotlinModuleSynthesizer;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
import com.android.tools.r8.origin.Origin;
@@ -224,7 +225,7 @@
StartupOrder startupOrder =
appView.appInfo().hasClassHierarchy()
? appView.appInfoWithClassHierarchy().getStartupOrder()
- : StartupOrder.createInitialStartupOrder(options);
+ : StartupOrder.createInitialStartupOrderForD8(appView);
distributor =
new VirtualFile.FillFilesDistributor(
this, classes, options, executorService, startupOrder);
@@ -605,13 +606,19 @@
ExceptionUtils.withFinishedResourceHandler(options.reporter, options.mainDexListConsumer);
}
+ KotlinModuleSynthesizer kotlinModuleSynthesizer = new KotlinModuleSynthesizer(appView);
+
DataResourceConsumer dataResourceConsumer = options.dataResourceConsumer;
if (dataResourceConsumer != null) {
ImmutableList<DataResourceProvider> dataResourceProviders =
appView.app().dataResourceProviders;
ResourceAdapter resourceAdapter = new ResourceAdapter(appView);
adaptAndPassDataResources(
- options, dataResourceConsumer, dataResourceProviders, resourceAdapter);
+ options,
+ dataResourceConsumer,
+ dataResourceProviders,
+ resourceAdapter,
+ kotlinModuleSynthesizer);
// Write the META-INF/services resources. Sort on service names and keep the order from
// the input for the implementation lines for deterministic output.
@@ -638,6 +645,10 @@
options.reporter);
});
}
+ // Rewrite/synthesize kotlin_module files
+ kotlinModuleSynthesizer
+ .synthesizeKotlinModuleFiles()
+ .forEach(file -> dataResourceConsumer.accept(file, options.reporter));
}
if (options.featureSplitConfiguration != null) {
@@ -645,7 +656,11 @@
options.featureSplitConfiguration.getDataResourceProvidersAndConsumers()) {
ResourceAdapter resourceAdapter = new ResourceAdapter(appView);
adaptAndPassDataResources(
- options, entry.getConsumer(), entry.getProviders(), resourceAdapter);
+ options,
+ entry.getConsumer(),
+ entry.getProviders(),
+ resourceAdapter,
+ kotlinModuleSynthesizer);
}
}
}
@@ -654,7 +669,8 @@
InternalOptions options,
DataResourceConsumer dataResourceConsumer,
Collection<DataResourceProvider> dataResourceProviders,
- ResourceAdapter resourceAdapter) {
+ ResourceAdapter resourceAdapter,
+ KotlinModuleSynthesizer kotlinModuleSynthesizer) {
Set<String> generatedResourceNames = new HashSet<>();
for (DataResourceProvider dataResourceProvider : dataResourceProviders) {
@@ -676,7 +692,10 @@
// META-INF/services resources are handled below.
return;
}
-
+ if (kotlinModuleSynthesizer.isKotlinModuleFile(file)) {
+ // .kotlin_module files are synthesized.
+ return;
+ }
DataEntryResource adapted = resourceAdapter.adaptIfNeeded(file);
if (generatedResourceNames.add(adapted.getName())) {
dataResourceConsumer.accept(adapted, options.reporter);
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 8d57a1c..f2b1e15 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -32,6 +32,7 @@
public static final String PG_MAP_ID = "pg-map-id";
public static final String R8_MODE = "r8-mode";
private static final String NO_LIBRARY_DESUGARING = "<no-library-desugaring>";
+ private static final String ANDROID_PLATFORM_BUILD = "platform";
public enum Tool {
D8,
@@ -268,6 +269,17 @@
return this;
}
+ public boolean isAndroidPlatformBuild() {
+ return jsonObject.has(ANDROID_PLATFORM_BUILD)
+ && jsonObject.get(ANDROID_PLATFORM_BUILD).getAsBoolean();
+ }
+
+ public Marker setAndroidPlatformBuild() {
+ assert !jsonObject.has(ANDROID_PLATFORM_BUILD);
+ jsonObject.addProperty(ANDROID_PLATFORM_BUILD, true);
+ return this;
+ }
+
@Override
public String toString() {
// In order to make printing of markers deterministic we sort the entries by key.
diff --git a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
index b297a2b..0833358 100644
--- a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
@@ -5,10 +5,10 @@
package com.android.tools.r8.dex;
import com.android.tools.r8.dex.FileWriter.MixedSectionOffsets;
-import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.profile.StartupClass;
+import com.android.tools.r8.experimental.startup.profile.StartupItem;
+import com.android.tools.r8.experimental.startup.profile.StartupMethod;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationDirectory;
@@ -82,21 +82,23 @@
virtualFile.classes().size());
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView, true);
StartupIndexedItemCollection indexedItemCollection = new StartupIndexedItemCollection();
- for (StartupItem<DexType, DexMethod, ?> startupItem : startupOrderForWriting.getItems()) {
- // All synthetic startup items should be removed after calling
- // StartupOrder#toStartupOrderForWriting.
- assert !startupItem.isSynthetic();
+ for (StartupItem startupItem : startupOrderForWriting.getItems()) {
startupItem.accept(
startupClass ->
collectStartupItems(startupClass, indexedItemCollection, virtualFileDefinitions),
startupMethod ->
collectStartupItems(
- startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter));
+ startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter),
+ syntheticStartupMethod -> {
+ // All synthetic startup items should be removed after calling
+ // StartupOrder#toStartupOrderForWriting.
+ assert false;
+ });
}
}
private void collectStartupItems(
- StartupClass<DexType, DexMethod> startupClass,
+ StartupClass startupClass,
StartupIndexedItemCollection indexedItemCollection,
Map<DexType, DexProgramClass> virtualFileDefinitions) {
DexProgramClass definition = virtualFileDefinitions.get(startupClass.getReference());
@@ -119,7 +121,7 @@
}
private void collectStartupItems(
- StartupMethod<DexType, DexMethod> startupMethod,
+ StartupMethod startupMethod,
StartupIndexedItemCollection indexedItemCollection,
Map<DexType, DexProgramClass> virtualFileDefinitions,
LensCodeRewriterUtils rewriter) {
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index fafe700..f6b057a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -1396,7 +1396,7 @@
return;
}
- assert options.getStartupOptions().hasStartupProfileProvider();
+ assert options.getStartupOptions().hasStartupProfileProviders();
// In practice, all startup classes should fit in a single dex file, so optimistically try to
// commit the startup classes using a single transaction.
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index 1d55987..27dddcc 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -10,9 +10,11 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -62,6 +64,7 @@
private final FeatureSplitConfiguration featureSplitConfiguration;
private final ProguardConfiguration proguardConfiguration;
private final List<ProguardConfigurationRule> mainDexKeepRules;
+ private final Collection<StartupProfileProvider> startupProfileProviders;
private final boolean enableMissingLibraryApiModeling;
private final boolean isAndroidPlatformBuild;
@@ -86,6 +89,7 @@
FeatureSplitConfiguration featureSplitConfiguration,
ProguardConfiguration proguardConfiguration,
List<ProguardConfigurationRule> mainDexKeepRules,
+ Collection<StartupProfileProvider> startupProfileProviders,
boolean enableMissingLibraryApiModeling,
boolean isAndroidPlatformBuild,
Map<String, String> systemProperties,
@@ -105,6 +109,7 @@
this.featureSplitConfiguration = featureSplitConfiguration;
this.proguardConfiguration = proguardConfiguration;
this.mainDexKeepRules = mainDexKeepRules;
+ this.startupProfileProviders = startupProfileProviders;
this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
this.isAndroidPlatformBuild = isAndroidPlatformBuild;
this.systemProperties = systemProperties;
@@ -268,6 +273,14 @@
return mainDexKeepRules;
}
+ public boolean hasStartupProfileProviders() {
+ return startupProfileProviders != null && !startupProfileProviders.isEmpty();
+ }
+
+ public Collection<StartupProfileProvider> getStartupProfileProviders() {
+ return startupProfileProviders;
+ }
+
public boolean dumpInputToFile() {
return dumpInputToFile;
}
@@ -293,6 +306,7 @@
private FeatureSplitConfiguration featureSplitConfiguration;
private ProguardConfiguration proguardConfiguration;
private List<ProguardConfigurationRule> mainDexKeepRules;
+ private Collection<StartupProfileProvider> startupProfileProviders;
private boolean enableMissingLibraryApiModeling = false;
private boolean isAndroidPlatformBuild = false;
@@ -386,6 +400,12 @@
return this;
}
+ public Builder setStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders) {
+ this.startupProfileProviders = startupProfileProviders;
+ return this;
+ }
+
public Builder setEnableMissingLibraryApiModeling(boolean value) {
enableMissingLibraryApiModeling = value;
return this;
@@ -432,6 +452,7 @@
featureSplitConfiguration,
proguardConfiguration,
mainDexKeepRules,
+ startupProfileProviders,
enableMissingLibraryApiModeling,
isAndroidPlatformBuild,
systemProperties,
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
index d777790..032e0b8 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.experimental.startup;
+import com.android.tools.r8.experimental.startup.profile.StartupItem;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -18,12 +19,17 @@
EmptyStartupOrder() {}
@Override
+ public boolean contains(DexMethod method, SyntheticItems syntheticItems) {
+ return false;
+ }
+
+ @Override
public boolean contains(DexType type, SyntheticItems syntheticItems) {
return false;
}
@Override
- public Collection<StartupItem<DexType, DexMethod, ?>> getItems() {
+ public Collection<StartupItem> getItems() {
return Collections.emptyList();
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
index 954fcba..25188c0 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
@@ -4,11 +4,14 @@
package com.android.tools.r8.experimental.startup;
+import com.android.tools.r8.experimental.startup.profile.StartupClass;
+import com.android.tools.r8.experimental.startup.profile.StartupItem;
+import com.android.tools.r8.experimental.startup.profile.StartupMethod;
+import com.android.tools.r8.experimental.startup.profile.SyntheticStartupMethod;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
@@ -25,28 +28,41 @@
public class NonEmptyStartupOrder extends StartupOrder {
- private final LinkedHashSet<StartupItem<DexType, DexMethod, ?>> startupItems;
+ private final LinkedHashSet<StartupItem> startupItems;
// Sets to allow efficient querying without boxing.
private final Set<DexType> nonSyntheticStartupClasses = Sets.newIdentityHashSet();
private final Set<DexType> syntheticStartupClasses = Sets.newIdentityHashSet();
- NonEmptyStartupOrder(LinkedHashSet<StartupItem<DexType, DexMethod, ?>> startupItems) {
+ private final Set<DexMethod> nonSyntheticStartupMethods = Sets.newIdentityHashSet();
+
+ NonEmptyStartupOrder(LinkedHashSet<StartupItem> startupItems) {
assert !startupItems.isEmpty();
this.startupItems = startupItems;
- for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
- if (startupItem.isSynthetic()) {
- assert startupItem.isStartupClass();
- syntheticStartupClasses.add(startupItem.asStartupClass().getReference());
- } else {
- DexReference reference =
- startupItem.apply(StartupClass::getReference, StartupMethod::getReference);
- nonSyntheticStartupClasses.add(reference.getContextType());
- }
+ for (StartupItem startupItem : startupItems) {
+ startupItem.accept(
+ startupClass -> nonSyntheticStartupClasses.add(startupClass.getReference()),
+ startupMethod -> {
+ nonSyntheticStartupClasses.add(startupMethod.getReference().getHolderType());
+ nonSyntheticStartupMethods.add(startupMethod.getReference());
+ },
+ syntheticStartupMethod ->
+ syntheticStartupClasses.add(syntheticStartupMethod.getSyntheticContextType()));
}
}
@Override
+ public boolean contains(DexMethod method, SyntheticItems syntheticItems) {
+ if (nonSyntheticStartupMethods.contains(method)) {
+ return true;
+ }
+ if (syntheticItems.isSyntheticClass(method.getHolderType())) {
+ return containsSyntheticClass(method.getHolderType(), syntheticItems);
+ }
+ return false;
+ }
+
+ @Override
public boolean contains(DexType type, SyntheticItems syntheticItems) {
return syntheticItems.isSyntheticClass(type)
? containsSyntheticClass(type, syntheticItems)
@@ -69,7 +85,7 @@
}
@Override
- public Collection<StartupItem<DexType, DexMethod, ?>> getItems() {
+ public Collection<StartupItem> getItems() {
return startupItems;
}
@@ -80,27 +96,28 @@
@Override
public StartupOrder rewrittenWithLens(GraphLens graphLens) {
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems =
- new LinkedHashSet<>(startupItems.size());
- for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
- if (startupItem.isStartupClass()) {
- StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
- rewrittenStartupItems.add(
- StartupClass.dexBuilder()
- .setClassReference(graphLens.lookupType(startupClass.getReference()))
- .setSynthetic(startupItem.isSynthetic())
- .build());
- } else {
- assert !startupItem.isSynthetic();
- StartupMethod<DexType, DexMethod> startupMethod = startupItem.asStartupMethod();
- // TODO(b/238173796): This should account for one-to-many mappings. e.g., when a bridge is
- // created.
- rewrittenStartupItems.add(
- StartupMethod.dexBuilder()
- .setMethodReference(
- graphLens.getRenamedMethodSignature(startupMethod.getReference()))
- .build());
- }
+ LinkedHashSet<StartupItem> rewrittenStartupItems = new LinkedHashSet<>(startupItems.size());
+ for (StartupItem startupItem : startupItems) {
+ // TODO(b/238173796): This should account for one-to-many mappings. e.g., when a bridge is
+ // created.
+ startupItem.apply(
+ startupClass ->
+ rewrittenStartupItems.add(
+ StartupClass.builder()
+ .setClassReference(graphLens.lookupType(startupClass.getReference()))
+ .build()),
+ startupMethod ->
+ rewrittenStartupItems.add(
+ StartupMethod.builder()
+ .setMethodReference(
+ graphLens.getRenamedMethodSignature(startupMethod.getReference()))
+ .build()),
+ syntheticStartupMethod ->
+ rewrittenStartupItems.add(
+ SyntheticStartupMethod.builder()
+ .setSyntheticContextReference(
+ graphLens.lookupType(syntheticStartupMethod.getSyntheticContextType()))
+ .build()));
}
return createNonEmpty(rewrittenStartupItems);
}
@@ -126,29 +143,27 @@
*/
@Override
public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems =
- new LinkedHashSet<>(startupItems.size());
+ LinkedHashSet<StartupItem> rewrittenStartupItems = new LinkedHashSet<>(startupItems.size());
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
appView.getSyntheticItems().computeSyntheticContextsToSyntheticClasses(appView);
- for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
+ for (StartupItem startupItem : startupItems) {
addStartupItem(
startupItem, rewrittenStartupItems, syntheticContextsToSyntheticClasses, appView);
}
- assert rewrittenStartupItems.stream().noneMatch(StartupItem::isSynthetic);
+ assert rewrittenStartupItems.stream().noneMatch(StartupItem::isSyntheticStartupMethod);
return createNonEmpty(rewrittenStartupItems);
}
private static void addStartupItem(
- StartupItem<DexType, DexMethod, ?> startupItem,
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
+ StartupItem startupItem,
+ LinkedHashSet<StartupItem> rewrittenStartupItems,
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
AppView<?> appView) {
- if (startupItem.isSynthetic()) {
- assert startupItem.isStartupClass();
- StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
+ if (startupItem.isSyntheticStartupMethod()) {
+ SyntheticStartupMethod syntheticStartupMethod = startupItem.asSyntheticStartupMethod();
List<DexProgramClass> syntheticClassesForContext =
syntheticContextsToSyntheticClasses.getOrDefault(
- startupClass.getReference(), Collections.emptyList());
+ syntheticStartupMethod.getSyntheticContextType(), Collections.emptyList());
for (DexProgramClass clazz : syntheticClassesForContext) {
addClassAndParentClasses(clazz, rewrittenStartupItems, appView);
addAllMethods(clazz, rewrittenStartupItems);
@@ -164,16 +179,13 @@
}
private static boolean addClass(
- DexProgramClass clazz,
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems) {
+ DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems) {
return rewrittenStartupItems.add(
- StartupClass.dexBuilder().setClassReference(clazz.getType()).build());
+ StartupClass.builder().setClassReference(clazz.getType()).build());
}
private static void addClassAndParentClasses(
- DexType type,
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
- AppView<?> appView) {
+ DexType type, LinkedHashSet<StartupItem> rewrittenStartupItems, AppView<?> appView) {
DexProgramClass definition = appView.app().programDefinitionFor(type);
if (definition != null) {
addClassAndParentClasses(definition, rewrittenStartupItems, appView);
@@ -181,54 +193,53 @@
}
private static void addClassAndParentClasses(
- DexProgramClass clazz,
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
- AppView<?> appView) {
+ DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems, AppView<?> appView) {
if (addClass(clazz, rewrittenStartupItems)) {
addParentClasses(clazz, rewrittenStartupItems, appView);
}
}
private static void addParentClasses(
- DexProgramClass clazz,
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
- AppView<?> appView) {
+ DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems, AppView<?> appView) {
clazz.forEachImmediateSupertype(
supertype -> addClassAndParentClasses(supertype, rewrittenStartupItems, appView));
}
private static void addAllMethods(
- DexProgramClass clazz,
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems) {
+ DexProgramClass clazz, LinkedHashSet<StartupItem> rewrittenStartupItems) {
clazz.forEachProgramMethod(
method ->
rewrittenStartupItems.add(
- StartupMethod.dexBuilder().setMethodReference(method.getReference()).build()));
+ StartupMethod.builder().setMethodReference(method.getReference()).build()));
}
@Override
public StartupOrder withoutPrunedItems(PrunedItems prunedItems, SyntheticItems syntheticItems) {
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems =
- new LinkedHashSet<>(startupItems.size());
+ LinkedHashSet<StartupItem> rewrittenStartupItems = new LinkedHashSet<>(startupItems.size());
LazyBox<Set<DexType>> contextsOfLiveSynthetics =
new LazyBox<>(
() -> computeContextsOfLiveSynthetics(prunedItems.getPrunedApp(), syntheticItems));
- for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
+ for (StartupItem startupItem : startupItems) {
// Only prune non-synthetic classes, since the pruning of a class does not imply that all
// classes synthesized from it have been pruned.
- if (startupItem.isSynthetic()) {
- assert startupItem.isStartupClass();
- StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
- if (contextsOfLiveSynthetics.computeIfAbsent().contains(startupClass.getReference())) {
- rewrittenStartupItems.add(startupClass);
- }
- } else {
- DexReference reference =
- startupItem.apply(StartupClass::getReference, StartupMethod::getReference);
- if (!prunedItems.isRemoved(reference)) {
- rewrittenStartupItems.add(startupItem);
- }
- }
+ startupItem.accept(
+ startupClass -> {
+ if (!prunedItems.isRemoved(startupClass.getReference())) {
+ rewrittenStartupItems.add(startupItem);
+ }
+ },
+ startupMethod -> {
+ if (!prunedItems.isRemoved(startupMethod.getReference())) {
+ rewrittenStartupItems.add(startupItem);
+ }
+ },
+ syntheticStartupMethod -> {
+ if (contextsOfLiveSynthetics
+ .computeIfAbsent()
+ .contains(syntheticStartupMethod.getSyntheticContextType())) {
+ rewrittenStartupItems.add(syntheticStartupMethod);
+ }
+ });
}
return createNonEmpty(rewrittenStartupItems);
}
@@ -245,8 +256,7 @@
return contextsOfLiveSynthetics;
}
- private StartupOrder createNonEmpty(
- LinkedHashSet<StartupItem<DexType, DexMethod, ?>> startupItems) {
+ private StartupOrder createNonEmpty(LinkedHashSet<StartupItem> startupItems) {
if (startupItems.isEmpty()) {
assert false;
return empty();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
deleted file mode 100644
index b6c6311..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupClass.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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.experimental.startup;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-// TODO(b/238173796): When updating the compiler to have support for taking a list of startup
-// methods, this class may likely be removed along with the StartupItem class, so that only
-// StartupMethod remains.
-public class StartupClass<C, M> extends StartupItem<C, M, C> {
-
- public StartupClass(int flags, C reference) {
- super(flags, reference);
- }
-
- public static <C, M> Builder<C, M> builder() {
- return new Builder<>();
- }
-
- public static Builder<DexType, DexMethod> dexBuilder() {
- return new Builder<>();
- }
-
- public static Builder<ClassReference, MethodReference> referenceBuilder() {
- return new Builder<>();
- }
-
- @Override
- public void accept(
- Consumer<StartupClass<C, M>> classConsumer, Consumer<StartupMethod<C, M>> methodConsumer) {
- classConsumer.accept(this);
- }
-
- @Override
- public <T> T apply(
- Function<StartupClass<C, M>, T> classFunction,
- Function<StartupMethod<C, M>, T> methodFunction) {
- return classFunction.apply(this);
- }
-
- @Override
- public boolean isStartupClass() {
- return true;
- }
-
- @Override
- public StartupClass<C, M> asStartupClass() {
- return this;
- }
-
- @Override
- public void serializeToString(
- StringBuilder builder,
- Function<C, String> classSerializer,
- Function<M, String> methodSerializer) {
- if (isSynthetic()) {
- builder.append('S');
- }
- builder.append(classSerializer.apply(getReference()));
- }
-
- public static class Builder<C, M> extends StartupItem.Builder<C, M, Builder<C, M>> {
-
- @Override
- public Builder<C, M> setMethodReference(M reference) {
- throw new Unreachable();
- }
-
- @Override
- public StartupClass<C, M> build() {
- return new StartupClass<>(flags, classReference);
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
index ca0b88f..6ef463d 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
@@ -4,15 +4,16 @@
package com.android.tools.r8.experimental.startup;
+import com.android.tools.r8.experimental.startup.profile.StartupItem;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ThrowNullCode;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
@@ -27,11 +28,16 @@
private final StartupOrder startupOrder;
private StartupCompleteness(AppView<?> appView) {
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization =
+ appView.enableWholeProgramOptimizations()
+ ? SyntheticToSyntheticContextGeneralization.createForR8()
+ : SyntheticToSyntheticContextGeneralization.createForD8();
this.appView = appView;
this.startupOrder =
appView.hasClassHierarchy()
? appView.appInfoWithClassHierarchy().getStartupOrder()
- : StartupOrder.createInitialStartupOrder(appView.options());
+ : StartupOrder.createInitialStartupOrder(
+ appView.options(), null, syntheticToSyntheticContextGeneralization);
}
/**
@@ -78,26 +84,20 @@
Set<DexReference> startupItems = Sets.newIdentityHashSet();
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
appView.getSyntheticItems().computeSyntheticContextsToSyntheticClasses(appView);
- for (StartupItem<DexType, DexMethod, ?> startupItem : startupOrder.getItems()) {
- if (startupItem.isSynthetic()) {
- assert startupItem.isStartupClass();
- List<DexProgramClass> syntheticClasses =
- syntheticContextsToSyntheticClasses.getOrDefault(
- startupItem.asStartupClass().getReference(), Collections.emptyList());
- for (DexProgramClass syntheticClass : syntheticClasses) {
- startupItems.add(syntheticClass.getType());
- syntheticClass.forEachProgramMethod(method -> startupItems.add(method.getReference()));
- }
- } else {
- if (startupItem.isStartupClass()) {
- StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
- startupItems.add(startupClass.getReference());
- } else {
- assert startupItem.isStartupMethod();
- StartupMethod<DexType, DexMethod> startupMethod = startupItem.asStartupMethod();
- startupItems.add(startupMethod.getReference());
- }
- }
+ for (StartupItem startupItem : startupOrder.getItems()) {
+ startupItem.accept(
+ startupClass -> startupItems.add(startupClass.getReference()),
+ startupMethod -> startupItems.add(startupMethod.getReference()),
+ syntheticStartupMethod -> {
+ List<DexProgramClass> syntheticClasses =
+ syntheticContextsToSyntheticClasses.getOrDefault(
+ syntheticStartupMethod.getSyntheticContextType(), Collections.emptyList());
+ for (DexProgramClass syntheticClass : syntheticClasses) {
+ startupItems.add(syntheticClass.getType());
+ syntheticClass.forEachProgramMethod(
+ method -> startupItems.add(method.getReference()));
+ }
+ });
}
return startupItems;
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java
deleted file mode 100644
index 79ebc1e..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java
+++ /dev/null
@@ -1,163 +0,0 @@
-// 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.experimental.startup;
-
-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.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
-import com.android.tools.r8.utils.DescriptorUtils;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public class StartupConfigurationParser<C, M, T> {
-
- interface MethodFactory<C, M, T> {
-
- M createMethod(
- C methodHolder, String methodName, List<T> methodParameterTypes, T methodReturnType);
- }
-
- private final Function<String, C> classFactory;
- private final MethodFactory<C, M, T> methodFactory;
- private final Function<String, T> typeFactory;
-
- StartupConfigurationParser(
- Function<String, C> classFactory,
- MethodFactory<C, M, T> methodFactory,
- Function<String, T> typeFactory) {
- this.classFactory = classFactory;
- this.methodFactory = methodFactory;
- this.typeFactory = typeFactory;
- }
-
- public static StartupConfigurationParser<DexType, DexMethod, DexType> createDexParser(
- DexItemFactory dexItemFactory) {
- return new StartupConfigurationParser<>(
- dexItemFactory::createType,
- (methodHolder, methodName, methodParameters, methodReturnType) ->
- dexItemFactory.createMethod(
- methodHolder,
- dexItemFactory.createProto(methodReturnType, methodParameters),
- dexItemFactory.createString(methodName)),
- dexItemFactory::createType);
- }
-
- public static StartupConfigurationParser<ClassReference, MethodReference, TypeReference>
- createReferenceParser() {
- return new StartupConfigurationParser<>(
- Reference::classFromDescriptor, Reference::method, Reference::returnTypeFromDescriptor);
- }
-
- public void parseLines(
- List<String> startupDescriptors,
- Consumer<? super StartupClass<C, M>> startupClassConsumer,
- Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
- Consumer<String> parseErrorHandler) {
- for (String startupDescriptor : startupDescriptors) {
- if (!startupDescriptor.isEmpty()) {
- parseLine(
- startupDescriptor, startupClassConsumer, startupMethodConsumer, parseErrorHandler);
- }
- }
- }
-
- public void parseLine(
- String startupDescriptor,
- Consumer<? super StartupClass<C, M>> startupClassConsumer,
- Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
- Consumer<String> parseErrorHandler) {
- StartupItem.Builder<C, M, ?> startupItemBuilder = StartupItem.builder();
- startupDescriptor = parseSyntheticFlag(startupDescriptor, startupItemBuilder);
- parseStartupClassOrMethod(
- startupDescriptor,
- startupItemBuilder,
- startupClassConsumer,
- startupMethodConsumer,
- parseErrorHandler);
- }
-
- private static String parseSyntheticFlag(
- String startupDescriptor, StartupItem.Builder<?, ?, ?> startupItemBuilder) {
- if (!startupDescriptor.isEmpty() && startupDescriptor.charAt(0) == 'S') {
- startupItemBuilder.setSynthetic();
- return startupDescriptor.substring(1);
- }
- return startupDescriptor;
- }
-
- private void parseStartupClassOrMethod(
- String startupDescriptor,
- StartupItem.Builder<C, M, ?> startupItemBuilder,
- Consumer<? super StartupClass<C, M>> startupClassConsumer,
- Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
- Consumer<String> parseErrorHandler) {
- int arrowStartIndex = getArrowStartIndex(startupDescriptor);
- if (arrowStartIndex >= 0) {
- M startupMethod = parseStartupMethodDescriptor(startupDescriptor, arrowStartIndex);
- if (startupMethod != null) {
- startupMethodConsumer.accept(
- startupItemBuilder.setMethodReference(startupMethod).buildStartupMethod());
- } else {
- parseErrorHandler.accept(startupDescriptor);
- }
- } else {
- C startupClass = parseStartupClassDescriptor(startupDescriptor);
- if (startupClass != null) {
- startupClassConsumer.accept(
- startupItemBuilder.setClassReference(startupClass).buildStartupClass());
- } else {
- parseErrorHandler.accept(startupDescriptor);
- }
- }
- }
-
- private static int getArrowStartIndex(String startupDescriptor) {
- return startupDescriptor.indexOf("->");
- }
-
- private C parseStartupClassDescriptor(String startupClassDescriptor) {
- if (DescriptorUtils.isClassDescriptor(startupClassDescriptor)) {
- return classFactory.apply(startupClassDescriptor);
- } else {
- return null;
- }
- }
-
- private M parseStartupMethodDescriptor(String startupMethodDescriptor, int arrowStartIndex) {
- String classDescriptor = startupMethodDescriptor.substring(0, arrowStartIndex);
- C methodHolder = parseStartupClassDescriptor(classDescriptor);
- if (methodHolder == null) {
- return null;
- }
-
- int methodNameStartIndex = arrowStartIndex + 2;
- String protoWithNameDescriptor = startupMethodDescriptor.substring(methodNameStartIndex);
- int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
- if (methodNameEndIndex <= 0) {
- return null;
- }
- String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
-
- String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
- return parseStartupMethodProto(methodHolder, methodName, protoDescriptor);
- }
-
- private M parseStartupMethodProto(C methodHolder, String methodName, String protoDescriptor) {
- List<T> parameterTypes = new ArrayList<>();
- for (String parameterTypeDescriptor :
- DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
- parameterTypes.add(typeFactory.apply(parameterTypeDescriptor));
- }
- String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
- T returnType = typeFactory.apply(returnTypeDescriptor);
- return methodFactory.createMethod(methodHolder, methodName, parameterTypes, returnType);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
deleted file mode 100644
index e2ea09b..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
+++ /dev/null
@@ -1,165 +0,0 @@
-// 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.experimental.startup;
-
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public abstract class StartupItem<C, M, R> {
-
- private static final int FLAG_SYNTHETIC = 1;
-
- protected final int flags;
- protected final R reference;
-
- public StartupItem(int flags, R reference) {
- this.flags = flags;
- this.reference = reference;
- }
-
- public abstract void accept(
- Consumer<StartupClass<C, M>> classConsumer, Consumer<StartupMethod<C, M>> methodConsumer);
-
- public abstract <T> T apply(
- Function<StartupClass<C, M>, T> classFunction,
- Function<StartupMethod<C, M>, T> methodFunction);
-
- public boolean isStartupClass() {
- return false;
- }
-
- public StartupClass<C, M> asStartupClass() {
- return null;
- }
-
- public boolean isStartupMethod() {
- return false;
- }
-
- public StartupMethod<C, M> asStartupMethod() {
- return null;
- }
-
- public static <C, M> Builder<C, M, ?> builder() {
- return new Builder<>();
- }
-
- public static Builder<DexType, DexMethod, ?> dexBuilder() {
- return new Builder<>();
- }
-
- public int getFlags() {
- return flags;
- }
-
- public R getReference() {
- return reference;
- }
-
- public boolean isSynthetic() {
- return (flags & FLAG_SYNTHETIC) != 0;
- }
-
- public abstract void serializeToString(
- StringBuilder builder,
- Function<C, String> classSerializer,
- Function<M, String> methodSerializer);
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- StartupItem<?, ?, ?> startupItem = (StartupItem<?, ?, ?>) obj;
- return flags == startupItem.flags && reference.equals(startupItem.reference);
- }
-
- @Override
- public int hashCode() {
- return (reference.hashCode() << 1) | flags;
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- if (isSynthetic()) {
- builder.append('S');
- }
- builder.append(reference);
- return builder.toString();
- }
-
- public static class Builder<C, M, B extends Builder<C, M, B>> {
-
- protected int flags;
- protected C classReference;
- protected M methodReference;
-
- public B applyIf(boolean condition, Consumer<B> thenConsumer, Consumer<B> elseConsumer) {
- if (condition) {
- thenConsumer.accept(self());
- } else {
- elseConsumer.accept(self());
- }
- return self();
- }
-
- public B setFlags(int flags) {
- this.flags = flags;
- return self();
- }
-
- public B setClassReference(C reference) {
- this.classReference = reference;
- return self();
- }
-
- public B setMethodReference(M reference) {
- this.methodReference = reference;
- return self();
- }
-
- public B setSynthetic() {
- this.flags |= FLAG_SYNTHETIC;
- return self();
- }
-
- public B setSynthetic(boolean synthetic) {
- if (synthetic) {
- return setSynthetic();
- }
- assert (flags & FLAG_SYNTHETIC) == 0;
- return self();
- }
-
- public StartupItem<C, M, ?> build() {
- if (classReference != null) {
- return buildStartupClass();
- } else {
- return buildStartupMethod();
- }
- }
-
- public StartupClass<C, M> buildStartupClass() {
- assert classReference != null;
- return new StartupClass<>(flags, classReference);
- }
-
- public StartupMethod<C, M> buildStartupMethod() {
- assert methodReference != null;
- return new StartupMethod<>(flags, methodReference);
- }
-
- @SuppressWarnings("unchecked")
- public B self() {
- return (B) this;
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
deleted file mode 100644
index 00ddca9..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupMethod.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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.experimental.startup;
-
-import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-public class StartupMethod<C, M> extends StartupItem<C, M, M> {
-
- public StartupMethod(int flags, M reference) {
- super(flags, reference);
- }
-
- public static Builder<ClassReference, MethodReference> referenceBuilder() {
- return new Builder<>();
- }
-
- @Override
- public void accept(
- Consumer<StartupClass<C, M>> classConsumer, Consumer<StartupMethod<C, M>> methodConsumer) {
- methodConsumer.accept(this);
- }
-
- @Override
- public <T> T apply(
- Function<StartupClass<C, M>, T> classFunction,
- Function<StartupMethod<C, M>, T> methodFunction) {
- return methodFunction.apply(this);
- }
-
- @Override
- public boolean isStartupMethod() {
- return true;
- }
-
- @Override
- public StartupMethod<C, M> asStartupMethod() {
- return this;
- }
-
- @Override
- public void serializeToString(
- StringBuilder builder,
- Function<C, String> classSerializer,
- Function<M, String> methodSerializer) {
- if (isSynthetic()) {
- builder.append('S');
- }
- builder.append(methodSerializer.apply(getReference()));
- }
-
- public static class Builder<C, M> extends StartupItem.Builder<C, M, Builder<C, M>> {
-
- @Override
- public Builder<C, M> setClassReference(C reference) {
- throw new Unreachable();
- }
-
- @Override
- public StartupMethod<C, M> build() {
- return new StartupMethod<>(flags, methodReference);
- }
- }
-}
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 86df003..01f91e0 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
@@ -6,13 +6,13 @@
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.InternalOptions;
import com.android.tools.r8.utils.SystemPropertyUtils;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.Collections;
public class StartupOptions {
@@ -52,28 +52,18 @@
private boolean enableStartupLayoutOptimizations =
parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.layout", true);
- private StartupProfileProvider startupProfileProvider =
- SystemPropertyUtils.applySystemProperty(
- "com.android.tools.r8.startup.profile",
- propertyValue ->
- new StartupProfileProvider() {
- @Override
- public String get() {
- return StringResource.fromFile(Paths.get(propertyValue))
- .getStringWithRuntimeException();
- }
+ private Collection<StartupProfileProvider> startupProfileProviders;
- @Override
- public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
- throw new Unimplemented();
- }
-
- @Override
- public Origin getOrigin() {
- return Origin.unknown();
- }
- },
- () -> null);
+ public StartupOptions(InternalOptions options) {
+ this.startupProfileProviders =
+ SystemPropertyUtils.applySystemProperty(
+ "com.android.tools.r8.startup.profile",
+ propertyValue ->
+ ImmutableList.of(
+ StartupProfileProviderUtils.createFromHumanReadableArtProfile(
+ Paths.get(propertyValue))),
+ Collections::emptyList);
+ }
public boolean isMinimalStartupDexEnabled() {
return enableMinimalStartupDex;
@@ -112,16 +102,17 @@
return this;
}
- public boolean hasStartupProfileProvider() {
- return startupProfileProvider != null;
+ public boolean hasStartupProfileProviders() {
+ return startupProfileProviders != null && !startupProfileProviders.isEmpty();
}
- public StartupProfileProvider getStartupProfileProvider() {
- return startupProfileProvider;
+ public Collection<StartupProfileProvider> getStartupProfileProviders() {
+ return startupProfileProviders;
}
- public StartupOptions setStartupProfileProvider(StartupProfileProvider startupProfileProvider) {
- this.startupProfileProvider = startupProfileProvider;
+ public StartupOptions setStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders) {
+ this.startupProfileProviders = startupProfileProviders;
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
index 9f9bf65..b369a94 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
@@ -4,11 +4,16 @@
package com.android.tools.r8.experimental.startup;
+import com.android.tools.r8.experimental.startup.profile.StartupItem;
+import com.android.tools.r8.experimental.startup.profile.StartupProfile;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collection;
@@ -18,21 +23,38 @@
StartupOrder() {}
- public static StartupOrder createInitialStartupOrder(InternalOptions options) {
- StartupProfile startupProfile = StartupProfile.parseStartupProfile(options);
+ public static StartupOrder createInitialStartupOrder(
+ InternalOptions options,
+ DexDefinitionSupplier definitions,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ StartupProfile startupProfile =
+ StartupProfile.parseStartupProfile(
+ options, definitions, syntheticToSyntheticContextGeneralization);
if (startupProfile == null || startupProfile.getStartupItems().isEmpty()) {
return empty();
}
return new NonEmptyStartupOrder(new LinkedHashSet<>(startupProfile.getStartupItems()));
}
+ public static StartupOrder createInitialStartupOrderForD8(AppView<?> appView) {
+ return createInitialStartupOrder(
+ appView.options(), appView, SyntheticToSyntheticContextGeneralization.createForD8());
+ }
+
+ public static StartupOrder createInitialStartupOrderForR8(DexApplication application) {
+ return createInitialStartupOrder(
+ application.options, application, SyntheticToSyntheticContextGeneralization.createForR8());
+ }
+
public static StartupOrder empty() {
return new EmptyStartupOrder();
}
+ public abstract boolean contains(DexMethod method, SyntheticItems syntheticItems);
+
public abstract boolean contains(DexType type, SyntheticItems syntheticItems);
- public abstract Collection<StartupItem<DexType, DexMethod, ?>> getItems();
+ public abstract Collection<StartupItem> getItems();
public abstract boolean isEmpty();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
deleted file mode 100644
index 6b6076f..0000000
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
+++ /dev/null
@@ -1,109 +0,0 @@
-// 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.experimental.startup;
-
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.startup.StartupProfileProvider;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.StringUtils;
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-public class StartupProfile {
-
- private final List<StartupItem<DexType, DexMethod, ?>> startupItems;
-
- public StartupProfile(List<StartupItem<DexType, DexMethod, ?>> startupItems) {
- this.startupItems = startupItems;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- /**
- * Parses the supplied startup configuration, if any. The startup configuration is a list of class
- * and method descriptors.
- *
- * <p>Example:
- *
- * <pre>
- * Landroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
- * Landroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
- * Landroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
- * Landroidx/compose/runtime/CompositionImpl;->applyChanges()V
- * Landroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
- * Landroidx/compose/runtime/ComposerImpl;
- * </pre>
- */
- public static StartupProfile parseStartupProfile(InternalOptions options) {
- if (!options.getStartupOptions().hasStartupProfileProvider()) {
- return null;
- }
- StartupProfileProvider resource = options.getStartupOptions().getStartupProfileProvider();
- List<String> startupDescriptors = StringUtils.splitLines(resource.get());
- return createStartupConfigurationFromLines(options, startupDescriptors);
- }
-
- public static StartupProfile createStartupConfigurationFromLines(
- InternalOptions options, List<String> startupDescriptors) {
- List<StartupItem<DexType, DexMethod, ?>> startupItems = new ArrayList<>();
- StartupConfigurationParser.createDexParser(options.dexItemFactory())
- .parseLines(
- startupDescriptors,
- startupItems::add,
- startupItems::add,
- error ->
- options.reporter.warning(
- new StringDiagnostic(
- "Invalid descriptor for startup class or method: " + error)));
- return new StartupProfile(startupItems);
- }
-
- public List<StartupItem<DexType, DexMethod, ?>> getStartupItems() {
- return startupItems;
- }
-
- public String serializeToString() {
- StringBuilder builder = new StringBuilder();
- for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
- startupItem.serializeToString(builder, DexType::toSmaliString, DexMethod::toSmaliString);
- builder.append('\n');
- }
- return builder.toString();
- }
-
- public static class Builder {
-
- private final ImmutableList.Builder<StartupItem<DexType, DexMethod, ?>> startupItemsBuilder =
- ImmutableList.builder();
-
- public Builder addStartupItem(StartupItem<DexType, DexMethod, ?> startupItem) {
- this.startupItemsBuilder.add(startupItem);
- return this;
- }
-
- public Builder addStartupClass(StartupClass<DexType, DexMethod> startupClass) {
- return addStartupItem(startupClass);
- }
-
- public Builder addStartupMethod(StartupMethod<DexType, DexMethod> startupMethod) {
- return addStartupItem(startupMethod);
- }
-
- public Builder apply(Consumer<Builder> consumer) {
- consumer.accept(this);
- return this;
- }
-
- public StartupProfile build() {
- return new StartupProfile(startupItemsBuilder.build());
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
new file mode 100644
index 0000000..3b5b940
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
@@ -0,0 +1,70 @@
+// 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.experimental.startup;
+
+import com.android.tools.r8.experimental.startup.profile.StartupItem;
+import com.android.tools.r8.experimental.startup.profile.StartupProfile;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.UTF8TextInputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+
+public class StartupProfileProviderUtils {
+
+ public static StartupProfileProvider createFromHumanReadableArtProfile(Path path) {
+ return new StartupProfileProvider() {
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ try {
+ startupProfileBuilder.addHumanReadableArtProfile(
+ new UTF8TextInputStream(path), ConsumerUtils.emptyConsumer());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return new PathOrigin(path);
+ }
+ };
+ }
+
+ /** Serialize the given {@param startupProfileProvider} to a string for writing it to a dump. */
+ public static String serializeToString(
+ InternalOptions options, StartupProfileProvider startupProfileProvider) {
+ // Do not report missing items.
+ MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
+ MissingStartupProfileItemsDiagnostic.Builder.nop();
+ // Do not generalize synthetic items to their synthetic context.
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization =
+ SyntheticToSyntheticContextGeneralization.createForD8();
+ StartupProfile.Builder startupProfileBuilder =
+ StartupProfile.builder(
+ options,
+ missingItemsDiagnosticBuilder,
+ startupProfileProvider,
+ syntheticToSyntheticContextGeneralization);
+ // Do not report warnings for lines that cannot be parsed.
+ startupProfileBuilder.setIgnoreWarnings();
+ // Populate the startup profile builder.
+ startupProfileProvider.getStartupProfile(startupProfileBuilder);
+ // Serialize the startup items.
+ StringBuilder resultBuilder = new StringBuilder();
+ for (StartupItem startupItem : startupProfileBuilder.build().getStartupItems()) {
+ resultBuilder.append(startupItem.serializeToString()).append('\n');
+ }
+ return resultBuilder.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
index 7e8db58..c3a72b9 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
@@ -37,13 +36,11 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.startup.generated.InstrumentationServerFactory;
import com.android.tools.r8.startup.generated.InstrumentationServerImplFactory;
-import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
-import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -174,20 +171,14 @@
instructionIterator.positionBeforeNextInstructionThatMatches(not(Instruction::isArgument));
// Insert invoke to record that the enclosing class is a startup class.
- SyntheticItems syntheticItems = appView.getSyntheticItems();
- boolean generalizeSyntheticToSyntheticOfSyntheticContexts =
- startupInstrumentationOptions.isGeneralizationOfSyntheticsToSyntheticContextEnabled()
- && syntheticItems.isSyntheticClass(method.getHolder());
- if (method.getDefinition().isClassInitializer()
- && !generalizeSyntheticToSyntheticOfSyntheticContexts) {
- DexMethod methodToInvoke = references.addNonSyntheticMethod;
+ if (method.getDefinition().isClassInitializer()) {
DexType classToPrint = method.getHolderType();
Value descriptorValue =
instructionIterator.insertConstStringInstruction(
appView, code, dexItemFactory.createString(classToPrint.toSmaliString()));
instructionIterator.add(
InvokeStatic.builder()
- .setMethod(methodToInvoke)
+ .setMethod(references.addMethod)
.setSingleArgument(descriptorValue)
.setPosition(Position.syntheticNone())
.build());
@@ -195,26 +186,13 @@
// Insert invoke to record the execution of the current method.
if (!skipMethodLogging) {
- DexMethod methodToInvoke;
- DexReference referenceToPrint;
- if (generalizeSyntheticToSyntheticOfSyntheticContexts) {
- Collection<DexType> synthesizingContexts =
- syntheticItems.getSynthesizingContextTypes(method.getHolderType());
- assert synthesizingContexts.size() == 1;
- DexType synthesizingContext = synthesizingContexts.iterator().next();
- methodToInvoke = references.addSyntheticMethod;
- referenceToPrint = synthesizingContext;
- } else {
- methodToInvoke = references.addNonSyntheticMethod;
- referenceToPrint = method.getReference();
- }
-
+ DexReference referenceToPrint = method.getReference();
Value descriptorValue =
instructionIterator.insertConstStringInstruction(
appView, code, dexItemFactory.createString(referenceToPrint.toSmaliString()));
instructionIterator.add(
InvokeStatic.builder()
- .setMethod(methodToInvoke)
+ .setMethod(references.addMethod)
.setSingleArgument(descriptorValue)
.setPosition(Position.syntheticNone())
.build());
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
index bfb3f87..0d47783 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
@@ -10,14 +10,6 @@
public class StartupInstrumentationOptions {
/**
- * When enabled, the instrumentation for synthetics will print the name of the synthetic context
- * instead of printing the name of the synthetic itself.
- */
- private boolean enableGeneralizationOfSyntheticsToSyntheticContext =
- parseSystemPropertyForDevelopmentOrDefault(
- "com.android.tools.r8.startup.instrumentation.generalizesynthetics", false);
-
- /**
* When enabled, each method will be instrumented to notify the startup InstrumentationServer that
* it has been executed.
*
@@ -82,17 +74,6 @@
return this;
}
- public boolean isGeneralizationOfSyntheticsToSyntheticContextEnabled() {
- return enableGeneralizationOfSyntheticsToSyntheticContext;
- }
-
- public StartupInstrumentationOptions setEnableGeneralizationOfSyntheticsToSyntheticContext(
- boolean enableGeneralizationOfSyntheticsToSyntheticContext) {
- this.enableGeneralizationOfSyntheticsToSyntheticContext =
- enableGeneralizationOfSyntheticsToSyntheticContext;
- return this;
- }
-
public boolean isStartupInstrumentationEnabled() {
return enableStartupInstrumentation;
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
index fbfa5e2..92d3e9c 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
@@ -12,23 +12,17 @@
final DexType instrumentationServerType;
final DexType instrumentationServerImplType;
- final DexMethod addNonSyntheticMethod;
- final DexMethod addSyntheticMethod;
+ final DexMethod addMethod;
StartupInstrumentationReferences(DexItemFactory dexItemFactory) {
instrumentationServerType =
dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;");
instrumentationServerImplType =
dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;");
- addNonSyntheticMethod =
+ addMethod =
dexItemFactory.createMethod(
instrumentationServerImplType,
dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringType),
- "addNonSyntheticMethod");
- addSyntheticMethod =
- dexItemFactory.createMethod(
- instrumentationServerImplType,
- dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.stringType),
- "addSyntheticMethod");
+ "addMethod");
}
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java
new file mode 100644
index 0000000..d281a65
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java
@@ -0,0 +1,112 @@
+// 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.experimental.startup.profile;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.startup.StartupClassBuilder;
+import com.android.tools.r8.utils.ClassReferenceUtils;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class StartupClass extends StartupItem {
+
+ private final DexType type;
+
+ StartupClass(DexType type) {
+ this.type = type;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder builder(DexItemFactory dexItemFactory) {
+ return new Builder(dexItemFactory);
+ }
+
+ @Override
+ public void accept(
+ Consumer<StartupClass> classConsumer,
+ Consumer<StartupMethod> methodConsumer,
+ Consumer<SyntheticStartupMethod> syntheticMethodConsumer) {
+ classConsumer.accept(this);
+ }
+
+ @Override
+ public <T> T apply(
+ Function<StartupClass, T> classFunction,
+ Function<StartupMethod, T> methodFunction,
+ Function<SyntheticStartupMethod, T> syntheticMethodFunction) {
+ return classFunction.apply(this);
+ }
+
+ public DexType getReference() {
+ return type;
+ }
+
+ @Override
+ public boolean isStartupClass() {
+ return true;
+ }
+
+ @Override
+ public StartupClass asStartupClass() {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ StartupClass that = (StartupClass) o;
+ return type == that.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public String serializeToString() {
+ return getReference().toDescriptorString();
+ }
+
+ public static class Builder implements StartupClassBuilder {
+
+ private final DexItemFactory dexItemFactory;
+
+ private DexType type;
+
+ Builder() {
+ this(null);
+ }
+
+ Builder(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ @Override
+ public Builder setClassReference(ClassReference classReference) {
+ assert dexItemFactory != null;
+ return setClassReference(ClassReferenceUtils.toDexType(classReference, dexItemFactory));
+ }
+
+ public Builder setClassReference(DexType type) {
+ this.type = type;
+ return this;
+ }
+
+ public StartupClass build() {
+ return new StartupClass(type);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java
new file mode 100644
index 0000000..b7e6f1c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java
@@ -0,0 +1,50 @@
+// 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.experimental.startup.profile;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public abstract class StartupItem {
+
+ public abstract void accept(
+ Consumer<StartupClass> classConsumer,
+ Consumer<StartupMethod> methodConsumer,
+ Consumer<SyntheticStartupMethod> syntheticMethodConsumer);
+
+ public abstract <T> T apply(
+ Function<StartupClass, T> classFunction,
+ Function<StartupMethod, T> methodFunction,
+ Function<SyntheticStartupMethod, T> syntheticMethodFunction);
+
+ public boolean isStartupClass() {
+ return false;
+ }
+
+ public StartupClass asStartupClass() {
+ assert false;
+ return null;
+ }
+
+ public boolean isStartupMethod() {
+ return false;
+ }
+
+ public StartupMethod asStartupMethod() {
+ assert false;
+ return null;
+ }
+
+ public boolean isSyntheticStartupMethod() {
+ return false;
+ }
+
+ public SyntheticStartupMethod asSyntheticStartupMethod() {
+ assert false;
+ return null;
+ }
+
+ public abstract String serializeToString();
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java
new file mode 100644
index 0000000..18045ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java
@@ -0,0 +1,112 @@
+// 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.experimental.startup.profile;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.startup.StartupMethodBuilder;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class StartupMethod extends StartupItem {
+
+ private final DexMethod method;
+
+ StartupMethod(DexMethod method) {
+ this.method = method;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder builder(DexItemFactory dexItemFactory) {
+ return new Builder(dexItemFactory);
+ }
+
+ @Override
+ public void accept(
+ Consumer<StartupClass> classConsumer,
+ Consumer<StartupMethod> methodConsumer,
+ Consumer<SyntheticStartupMethod> syntheticMethodConsumer) {
+ methodConsumer.accept(this);
+ }
+
+ @Override
+ public <T> T apply(
+ Function<StartupClass, T> classFunction,
+ Function<StartupMethod, T> methodFunction,
+ Function<SyntheticStartupMethod, T> syntheticMethodFunction) {
+ return methodFunction.apply(this);
+ }
+
+ public DexMethod getReference() {
+ return method;
+ }
+
+ @Override
+ public boolean isStartupMethod() {
+ return true;
+ }
+
+ @Override
+ public StartupMethod asStartupMethod() {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ StartupMethod that = (StartupMethod) o;
+ return method == that.method;
+ }
+
+ @Override
+ public int hashCode() {
+ return method.hashCode();
+ }
+
+ @Override
+ public String serializeToString() {
+ return method.toSmaliString();
+ }
+
+ public static class Builder implements StartupMethodBuilder {
+
+ private final DexItemFactory dexItemFactory;
+
+ private DexMethod method;
+
+ Builder() {
+ this(null);
+ }
+
+ Builder(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ @Override
+ public Builder setMethodReference(MethodReference classReference) {
+ assert dexItemFactory != null;
+ return setMethodReference(MethodReferenceUtils.toDexMethod(classReference, dexItemFactory));
+ }
+
+ public Builder setMethodReference(DexMethod method) {
+ this.method = method;
+ return this;
+ }
+
+ public StartupMethod build() {
+ return new StartupMethod(method);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
new file mode 100644
index 0000000..6dd7c65
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
@@ -0,0 +1,215 @@
+// 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.experimental.startup.profile;
+
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.profile.art.AlwaysTrueArtProfileRulePredicate;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
+import com.android.tools.r8.profile.art.ArtProfileRulePredicate;
+import com.android.tools.r8.profile.art.HumanReadableArtProfileParser;
+import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
+import com.android.tools.r8.startup.StartupClassBuilder;
+import com.android.tools.r8.startup.StartupMethodBuilder;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.function.Consumer;
+
+public class StartupProfile {
+
+ private final LinkedHashSet<StartupItem> startupItems;
+
+ StartupProfile(LinkedHashSet<StartupItem> startupItems) {
+ this.startupItems = startupItems;
+ }
+
+ public static Builder builder(
+ InternalOptions options,
+ MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
+ StartupProfileProvider startupProfileProvider,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ return new Builder(
+ options,
+ missingItemsDiagnosticBuilder,
+ startupProfileProvider,
+ syntheticToSyntheticContextGeneralization);
+ }
+
+ public static StartupProfile merge(Collection<StartupProfile> startupProfiles) {
+ LinkedHashSet<StartupItem> mergedStartupItems = new LinkedHashSet<>();
+ for (StartupProfile startupProfile : startupProfiles) {
+ mergedStartupItems.addAll(startupProfile.getStartupItems());
+ }
+ return new StartupProfile(mergedStartupItems);
+ }
+
+ /**
+ * Parses the supplied startup configuration, if any. The startup configuration is a list of class
+ * and method descriptors.
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * Landroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
+ * Landroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
+ * Landroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
+ * Landroidx/compose/runtime/CompositionImpl;->applyChanges()V
+ * Landroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
+ * Landroidx/compose/runtime/ComposerImpl;
+ * </pre>
+ */
+ public static StartupProfile parseStartupProfile(
+ InternalOptions options,
+ DexDefinitionSupplier definitions,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ if (!options.getStartupOptions().hasStartupProfileProviders()) {
+ return null;
+ }
+ Collection<StartupProfileProvider> startupProfileProviders =
+ options.getStartupOptions().getStartupProfileProviders();
+ List<StartupProfile> startupProfiles = new ArrayList<>(startupProfileProviders.size());
+ for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
+ MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
+ new MissingStartupProfileItemsDiagnostic.Builder(definitions)
+ .setOrigin(startupProfileProvider.getOrigin());
+ StartupProfile.Builder startupProfileBuilder =
+ StartupProfile.builder(
+ options,
+ missingItemsDiagnosticBuilder,
+ startupProfileProvider,
+ syntheticToSyntheticContextGeneralization);
+ startupProfileProvider.getStartupProfile(startupProfileBuilder);
+ startupProfiles.add(startupProfileBuilder.build());
+ if (missingItemsDiagnosticBuilder.hasMissingStartupItems()) {
+ options.reporter.warning(missingItemsDiagnosticBuilder.build());
+ }
+ }
+ return StartupProfile.merge(startupProfiles);
+ }
+
+ public Collection<StartupItem> getStartupItems() {
+ return startupItems;
+ }
+
+ public static class Builder implements StartupProfileBuilder {
+
+ private final DexItemFactory dexItemFactory;
+ private final MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder;
+ private Reporter reporter;
+ private final StartupProfileProvider startupProfileProvider;
+ private final SyntheticToSyntheticContextGeneralization
+ syntheticToSyntheticContextGeneralization;
+
+ private final LinkedHashSet<StartupItem> startupItems = new LinkedHashSet<>();
+
+ Builder(
+ InternalOptions options,
+ MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
+ StartupProfileProvider startupProfileProvider,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ this.dexItemFactory = options.dexItemFactory();
+ this.missingItemsDiagnosticBuilder = missingItemsDiagnosticBuilder;
+ this.reporter = options.reporter;
+ this.startupProfileProvider = startupProfileProvider;
+ this.syntheticToSyntheticContextGeneralization = syntheticToSyntheticContextGeneralization;
+ }
+
+ @Override
+ public Builder addStartupClass(Consumer<StartupClassBuilder> startupClassBuilderConsumer) {
+ StartupClass.Builder startupClassBuilder = StartupClass.builder(dexItemFactory);
+ startupClassBuilderConsumer.accept(startupClassBuilder);
+ StartupClass startupClass = startupClassBuilder.build();
+ if (missingItemsDiagnosticBuilder.registerStartupClass(startupClass)) {
+ return this;
+ }
+ return addStartupItem(startupClass);
+ }
+
+ @Override
+ public Builder addStartupMethod(Consumer<StartupMethodBuilder> startupMethodBuilderConsumer) {
+ StartupMethod.Builder startupMethodBuilder = StartupMethod.builder(dexItemFactory);
+ startupMethodBuilderConsumer.accept(startupMethodBuilder);
+ StartupMethod startupMethod = startupMethodBuilder.build();
+ if (missingItemsDiagnosticBuilder.registerStartupMethod(startupMethod)) {
+ return this;
+ }
+ return addStartupItem(startupMethod);
+ }
+
+ @Override
+ public StartupProfileBuilder addSyntheticStartupMethod(
+ Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer) {
+ SyntheticStartupMethod.Builder syntheticStartupMethodBuilder =
+ SyntheticStartupMethod.builder(dexItemFactory);
+ syntheticStartupMethodBuilderConsumer.accept(syntheticStartupMethodBuilder);
+ SyntheticStartupMethod syntheticStartupMethod = syntheticStartupMethodBuilder.build();
+ if (missingItemsDiagnosticBuilder.registerSyntheticStartupMethod(syntheticStartupMethod)) {
+ return this;
+ }
+ return addStartupItem(syntheticStartupMethod);
+ }
+
+ @Override
+ public StartupProfileBuilder addHumanReadableArtProfile(
+ TextInputStream textInputStream,
+ Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
+ Box<ArtProfileRulePredicate> rulePredicateBox =
+ new Box<>(new AlwaysTrueArtProfileRulePredicate());
+ parserBuilderConsumer.accept(
+ new HumanReadableArtProfileParserBuilder() {
+ @Override
+ public HumanReadableArtProfileParserBuilder setRulePredicate(
+ ArtProfileRulePredicate rulePredicate) {
+ rulePredicateBox.set(rulePredicate);
+ return this;
+ }
+ });
+
+ HumanReadableArtProfileParser parser =
+ HumanReadableArtProfileParser.builder()
+ .setReporter(reporter)
+ .setProfileBuilder(
+ ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
+ this, rulePredicateBox.get(), syntheticToSyntheticContextGeneralization))
+ .build();
+ parser.parse(textInputStream, startupProfileProvider.getOrigin());
+ return this;
+ }
+
+ private Builder addStartupItem(StartupItem startupItem) {
+ this.startupItems.add(startupItem);
+ return this;
+ }
+
+ public Builder apply(Consumer<Builder> consumer) {
+ consumer.accept(this);
+ return this;
+ }
+
+ public Builder setIgnoreWarnings() {
+ return setReporter(null);
+ }
+
+ public Builder setReporter(Reporter reporter) {
+ this.reporter = reporter;
+ return this;
+ }
+
+ public StartupProfile build() {
+ return new StartupProfile(startupItems);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java
new file mode 100644
index 0000000..c68a198
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java
@@ -0,0 +1,113 @@
+// 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.experimental.startup.profile;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
+import com.android.tools.r8.utils.ClassReferenceUtils;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class SyntheticStartupMethod extends StartupItem {
+
+ private final DexType syntheticContextType;
+
+ SyntheticStartupMethod(DexType syntheticContextType) {
+ this.syntheticContextType = syntheticContextType;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder builder(DexItemFactory dexItemFactory) {
+ return new Builder(dexItemFactory);
+ }
+
+ @Override
+ public void accept(
+ Consumer<StartupClass> classConsumer,
+ Consumer<StartupMethod> methodConsumer,
+ Consumer<SyntheticStartupMethod> syntheticMethodConsumer) {
+ syntheticMethodConsumer.accept(this);
+ }
+
+ @Override
+ public <T> T apply(
+ Function<StartupClass, T> classFunction,
+ Function<StartupMethod, T> methodFunction,
+ Function<SyntheticStartupMethod, T> syntheticMethodFunction) {
+ return syntheticMethodFunction.apply(this);
+ }
+
+ public DexType getSyntheticContextType() {
+ return syntheticContextType;
+ }
+
+ @Override
+ public boolean isSyntheticStartupMethod() {
+ return true;
+ }
+
+ @Override
+ public SyntheticStartupMethod asSyntheticStartupMethod() {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SyntheticStartupMethod that = (SyntheticStartupMethod) o;
+ return syntheticContextType == that.syntheticContextType;
+ }
+
+ @Override
+ public int hashCode() {
+ return syntheticContextType.hashCode();
+ }
+
+ @Override
+ public String serializeToString() {
+ return 'S' + syntheticContextType.toDescriptorString();
+ }
+
+ public static class Builder implements SyntheticStartupMethodBuilder {
+
+ private final DexItemFactory dexItemFactory;
+
+ private DexType syntheticContextReference;
+
+ Builder() {
+ this(null);
+ }
+
+ Builder(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ @Override
+ public Builder setSyntheticContextReference(ClassReference syntheticContextReference) {
+ assert dexItemFactory != null;
+ return setSyntheticContextReference(
+ ClassReferenceUtils.toDexType(syntheticContextReference, dexItemFactory));
+ }
+
+ public Builder setSyntheticContextReference(DexType syntheticContextReference) {
+ this.syntheticContextReference = syntheticContextReference;
+ return this;
+ }
+
+ public SyntheticStartupMethod build() {
+ return new SyntheticStartupMethod(syntheticContextReference);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
index 451ff92..8e2fbb4 100644
--- a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
public class FeatureSplitBoundaryOptimizationUtils {
@@ -54,14 +55,45 @@
ProgramMethod callee,
AppView<? extends AppInfoWithClassHierarchy> appView) {
ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
- if (classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(callee, caller, appView)) {
- return true;
+ FeatureSplit callerFeatureSplit = classToFeatureSplitMap.getFeatureSplit(caller, appView);
+ FeatureSplit calleeFeatureSplit = classToFeatureSplitMap.getFeatureSplit(callee, appView);
+
+ // First guarantee that we don't cross any actual feature split boundaries.
+ if (!calleeFeatureSplit.isBase()) {
+ if (calleeFeatureSplit != callerFeatureSplit) {
+ return false;
+ }
}
- // Still allow inlining if we inline from the base into a feature.
- if (classToFeatureSplitMap.isInBase(callee.getHolder(), appView)) {
- return true;
+
+ // Next perform startup checks.
+ StartupOrder startupOrder = appView.appInfo().getStartupOrder();
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
+ OptionalBool callerIsStartupMethod = isStartupMethod(caller, startupOrder, syntheticItems);
+ if (callerIsStartupMethod.isTrue()) {
+ // If the caller is a startup method, then only allow inlining if the callee is also a startup
+ // method.
+ if (isStartupMethod(callee, startupOrder, syntheticItems).isFalse()) {
+ return false;
+ }
+ } else if (callerIsStartupMethod.isFalse()) {
+ // If the caller is not a startup method, then only allow inlining if the caller is not a
+ // startup class or the callee is a startup class.
+ if (startupOrder.contains(caller.getHolderType(), syntheticItems)
+ && !startupOrder.contains(callee.getHolderType(), syntheticItems)) {
+ return false;
+ }
}
- return false;
+ return true;
+ }
+
+ private static OptionalBool isStartupMethod(
+ ProgramMethod method, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+ if (method.getDefinition().isD8R8Synthesized()) {
+ // Due to inadequate rewriting of the startup list during desugaring, we do not give an
+ // accurate result in this case.
+ return OptionalBool.unknown();
+ }
+ return OptionalBool.of(startupOrder.contains(method.getReference(), syntheticItems));
}
public static boolean isSafeForVerticalClassMerging(
@@ -69,7 +101,26 @@
DexProgramClass targetClass,
AppView<? extends AppInfoWithClassHierarchy> appView) {
ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
- return classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(
- sourceClass, targetClass, appView);
+ FeatureSplit sourceFeatureSplit = classToFeatureSplitMap.getFeatureSplit(sourceClass, appView);
+ FeatureSplit targetFeatureSplit = classToFeatureSplitMap.getFeatureSplit(targetClass, appView);
+
+ // First guarantee that we don't cross any actual feature split boundaries.
+ if (targetFeatureSplit.isBase()) {
+ assert sourceFeatureSplit.isBase() : "Unexpected class in base that inherits from feature";
+ } else {
+ if (sourceFeatureSplit != targetFeatureSplit) {
+ return false;
+ }
+ }
+
+ // If the source class is a startup class then require that the target class is also a startup
+ // class.
+ StartupOrder startupOrder = appView.appInfo().getStartupOrder();
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
+ if (startupOrder.contains(sourceClass.getType(), syntheticItems)
+ && !startupOrder.contains(targetClass.getType(), syntheticItems)) {
+ return false;
+ }
+ return true;
}
}
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 b4926fe..3633506 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -208,7 +208,7 @@
DexApplication application, MainDexInfo mainDexInfo) {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(application.options);
- StartupOrder startupOrder = StartupOrder.createInitialStartupOrder(application.options);
+ StartupOrder startupOrder = StartupOrder.createInitialStartupOrderForR8(application);
AppInfoWithClassHierarchy appInfo =
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
application,
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 44506fd..bfa111e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -304,6 +304,7 @@
public final DexString reflectiveOperationExceptionDescriptor =
createString("Ljava/lang/ReflectiveOperationException;");
public final DexString kotlinMetadataDescriptor = createString("Lkotlin/Metadata;");
+ public final DexString kotlinJvmNameDescriptor = createString("Lkotlin/jvm/JvmName;");
public final DexString intFieldUpdaterDescriptor =
createString("Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater;");
@@ -490,6 +491,7 @@
public final DexType reflectiveOperationExceptionType =
createStaticallyKnownType(reflectiveOperationExceptionDescriptor);
public final DexType kotlinMetadataType = createStaticallyKnownType(kotlinMetadataDescriptor);
+ public final DexType kotlinJvmNameType = createStaticallyKnownType(kotlinJvmNameDescriptor);
public final DexType javaIoFileType = createStaticallyKnownType("Ljava/io/File;");
public final DexType javaMathBigIntegerType = createStaticallyKnownType("Ljava/math/BigInteger;");
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 9a4e70c..4412a67 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -63,7 +63,7 @@
public BackportedMethodRewriter(AppView<?> appView) {
assert appView.options().desugarState.isOn();
this.appView = appView;
- this.rewritableMethods = new RewritableMethods(appView.options(), appView);
+ this.rewritableMethods = new RewritableMethods(appView);
}
public boolean hasBackports() {
@@ -114,7 +114,7 @@
TypeRewriter typeRewriter = options.getTypeRewriter();
AppView<?> appView = AppView.createForD8(appInfo, typeRewriter, Timing.empty());
BackportedMethodRewriter.RewritableMethods rewritableMethods =
- new BackportedMethodRewriter.RewritableMethods(options, appView);
+ new BackportedMethodRewriter.RewritableMethods(appView);
rewritableMethods.visit(methods::add);
if (appInfo != null) {
DesugaredLibraryRetargeter desugaredLibraryRetargeter =
@@ -159,15 +159,15 @@
// Map backported method to a provider for creating the actual target method (with code).
private final Map<DexMethod, MethodProvider> rewritable = new IdentityHashMap<>();
- RewritableMethods(InternalOptions options, AppView<?> appView) {
+ RewritableMethods(AppView<?> appView) {
+ InternalOptions options = appView.options();
+ DexItemFactory factory = options.dexItemFactory();
this.appView = appView;
- this.typeMinApi = initializeTypeMinApi(appView.dexItemFactory());
- if (!options.shouldBackportMethods()) {
+ this.typeMinApi = initializeTypeMinApi(factory);
+ if (!options.enableBackportedMethodRewriting()) {
return;
}
- DexItemFactory factory = options.itemFactory;
-
if (options.getMinApiLevel().isLessThan(AndroidApiLevel.K)) {
initializeAndroidKMethodProviders(factory);
if (typeIsAbsentOrPresentWithoutBackportsFrom(factory.objectsType, AndroidApiLevel.K)) {
@@ -265,6 +265,11 @@
|| appView
.options()
.machineDesugaredLibrarySpecification
+ .getEmulatedInterfaces()
+ .containsKey(type)
+ || appView
+ .options()
+ .machineDesugaredLibrarySpecification
.getMaintainType()
.contains(type);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 3e41a70..f68df08 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -79,7 +79,7 @@
return;
}
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
- BackportedMethodRewriter backportedMethodRewriter = null;
+ BackportedMethodRewriter backportedMethodRewriter = new BackportedMethodRewriter(appView);
desugaredLibraryRetargeter =
appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
? new DesugaredLibraryRetargeter(appView)
@@ -87,9 +87,6 @@
if (desugaredLibraryRetargeter != null) {
desugarings.add(desugaredLibraryRetargeter);
}
- if (appView.options().enableBackportedMethodRewriting()) {
- backportedMethodRewriter = new BackportedMethodRewriter(appView);
- }
if (appView.options().apiModelingOptions().enableOutliningOfMethods) {
yieldingDesugarings.add(new ApiInvokeOutlinerDesugaring(appView, apiLevelCompute));
}
@@ -127,7 +124,7 @@
desugarings.add(new InvokeToPrivateRewriter());
desugarings.add(new StringConcatInstructionDesugaring(appView));
desugarings.add(new BufferCovariantReturnTypeRewriter(appView));
- if (backportedMethodRewriter != null && backportedMethodRewriter.hasBackports()) {
+ if (backportedMethodRewriter.hasBackports()) {
desugarings.add(backportedMethodRewriter);
}
if (nestBasedAccessDesugaring != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
index afddbae..34a8e9d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
@@ -39,10 +39,14 @@
public static void main(String[] args) throws IOException {
Path jsonFile = Paths.get(args[0]);
Path desugaredLibraryJar = Paths.get(args[1]);
- Path androidJar = Paths.get(args[2]);
- Path output = Paths.get(args[3]);
+ Path customConversionsJar = Paths.get(args[2]);
+ Path androidJar = Paths.get(args[3]);
+ Path output = Paths.get(args[4]);
convertMultiLevelAnythingToMachineSpecification(
- jsonFile, ImmutableSet.of(desugaredLibraryJar), ImmutableSet.of(androidJar), output);
+ jsonFile,
+ ImmutableSet.of(desugaredLibraryJar, customConversionsJar),
+ ImmutableSet.of(androidJar),
+ output);
}
public static void convertMultiLevelAnythingToMachineSpecification(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 6dc28aa..3801d7c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -351,6 +351,9 @@
private ConstraintWithTarget forFieldInstruction(DexField field, ProgramMethod context) {
DexField lookup = graphLens.lookupField(field);
FieldResolutionResult fieldResolutionResult = appView.appInfo().resolveField(lookup);
+ if (fieldResolutionResult.isMultiFieldResolutionResult()) {
+ return ConstraintWithTarget.NEVER;
+ }
return forResolvedMember(
fieldResolutionResult.getInitialResolutionHolder(),
context,
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
index f112b4f..3daa33d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFileFacadeInfo.java
@@ -70,6 +70,10 @@
return packageName;
}
+ public String getModuleName() {
+ return packageInfo.getModuleName();
+ }
+
@Override
public int[] getMetadataVersion() {
return metadataVersion;
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
index 63e7c95..f5b40c2 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinFunctionInfo.java
@@ -121,7 +121,9 @@
AppView<?> appView) {
// TODO(b/154348683): Check method for flags to pass in.
boolean rewritten = false;
- String finalName = this.name;
+ String finalName = name;
+ // Only rewrite the kotlin method name if it was equal to the method name when reading the
+ // metadata.
if (method != null) {
String methodName = method.getReference().name.toString();
String rewrittenName = appView.getNamingLens().lookupName(method.getReference()).toString();
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
index 6c96b8d..62460ae 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMultiFileClassPartInfo.java
@@ -18,7 +18,6 @@
// Holds information about Metadata.MultiFileClassPartInfo
public class KotlinMultiFileClassPartInfo implements KotlinClassLevelInfo {
- // TODO(b/157630779): Maybe model facadeClassName.
private final String facadeClassName;
private final KotlinPackageInfo packageInfo;
private final String packageName;
@@ -78,6 +77,10 @@
return packageName;
}
+ public String getModuleName() {
+ return packageInfo.getModuleName();
+ }
+
@Override
public int[] getMetadataVersion() {
return metadataVersion;
@@ -87,4 +90,8 @@
public void trace(DexDefinitionSupplier definitionSupplier) {
packageInfo.trace(definitionSupplier);
}
+
+ public String getFacadeClassName() {
+ return facadeClassName;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index f1e2573..a0f0e81 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -97,4 +97,8 @@
containerInfo.trace(definitionSupplier);
localDelegatedProperties.trace(definitionSupplier);
}
+
+ public String getModuleName() {
+ return moduleName;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
new file mode 100644
index 0000000..7686a1e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
@@ -0,0 +1,188 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
+
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
+import com.android.tools.r8.kotlin.KotlinMultiFileClassPartInfo;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.Pair;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import kotlinx.metadata.jvm.KotlinModuleMetadata.Writer;
+
+/**
+ * The kotlin module synthesizer will scan through all file facades and multiclass files to figure
+ * out the residual package destination of these and bucket them into their original module names.
+ */
+public class KotlinModuleSynthesizer {
+
+ private final AppView<?> appView;
+
+ public KotlinModuleSynthesizer(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ public boolean isKotlinModuleFile(DataEntryResource file) {
+ return file.getName().endsWith(".kotlin_module");
+ }
+
+ public List<DataEntryResource> synthesizeKotlinModuleFiles() {
+ Map<String, KotlinModuleInfoBuilder> kotlinModuleBuilders = new HashMap<>();
+ // We cannot obtain the module name for multi class file facades. But, we can for a multi class
+ // part obtain both the module name and the multi class facade. We therefore iterate over all
+ // classes to find a multi class facade -> module name mapping, and then iterate over all
+ // classes to assign multi class facades to modules.
+ Map<String, String> moduleNamesForParts = new HashMap<>();
+ for (DexProgramClass clazz : appView.app().classesWithDeterministicOrder()) {
+ KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
+ if (kotlinInfo.isFileFacade()) {
+ kotlinModuleBuilders
+ .computeIfAbsent(
+ kotlinInfo.asFileFacade().getModuleName(),
+ moduleName -> new KotlinModuleInfoBuilder(moduleName, appView))
+ .add(clazz);
+ } else if (kotlinInfo.isMultiFileClassPart()) {
+ KotlinMultiFileClassPartInfo kotlinMultiFileClassPartInfo =
+ kotlinInfo.asMultiFileClassPart();
+ moduleNamesForParts.computeIfAbsent(
+ kotlinMultiFileClassPartInfo.getFacadeClassName(),
+ ignored -> kotlinMultiFileClassPartInfo.getModuleName());
+ kotlinModuleBuilders
+ .computeIfAbsent(
+ kotlinMultiFileClassPartInfo.getModuleName(),
+ moduleName -> new KotlinModuleInfoBuilder(moduleName, appView))
+ .add(clazz);
+ }
+ }
+ for (DexProgramClass clazz : appView.app().classesWithDeterministicOrder()) {
+ KotlinClassLevelInfo kotlinInfo = clazz.getKotlinInfo();
+ if (kotlinInfo.isMultiFileFacade()) {
+ DexType originalType = appView.graphLens().getOriginalType(clazz.getType());
+ if (originalType != null) {
+ String moduleNameForPart = moduleNamesForParts.get(originalType.toBinaryName());
+ // If module name is null then we did not find any multi class file parts and therefore
+ // do not have to do anything for the facade.
+ if (moduleNameForPart != null) {
+ KotlinModuleInfoBuilder kotlinModuleInfoBuilder =
+ kotlinModuleBuilders.get(moduleNameForPart);
+ assert kotlinModuleInfoBuilder != null;
+ kotlinModuleInfoBuilder.add(clazz);
+ }
+ }
+ }
+ }
+ if (kotlinModuleBuilders.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<DataEntryResource> newResources = new ArrayList<>();
+ kotlinModuleBuilders.values().forEach(builder -> builder.build().ifPresent(newResources::add));
+ return newResources;
+ }
+
+ private static class KotlinModuleInfoBuilder {
+
+ private final String moduleName;
+ private final GraphLens graphLens;
+ private final NamingLens namingLens;
+ private final DexItemFactory factory;
+
+ private final Map<String, List<String>> newFacades = new HashMap<>();
+ private final Map<String, List<Pair<String, String>>> multiClassFacadeOriginalToRenamed =
+ new LinkedHashMap<>();
+ private final Map<String, List<String>> multiClassPartToOriginal = new HashMap<>();
+ private final Box<int[]> metadataVersion = new Box<>();
+
+ private KotlinModuleInfoBuilder(String moduleName, AppView<?> appView) {
+ this.moduleName = moduleName;
+ this.graphLens = appView.graphLens();
+ this.namingLens = appView.getNamingLens();
+ this.factory = appView.dexItemFactory();
+ }
+
+ private void add(DexProgramClass clazz) {
+ DexType classType = clazz.getType();
+ KotlinClassLevelInfo classKotlinInfo = clazz.getKotlinInfo();
+ DexType renamedType = namingLens.lookupType(classType, factory);
+ if (classKotlinInfo.isFileFacade()) {
+ metadataVersion.computeIfAbsent(classKotlinInfo::getMetadataVersion);
+ newFacades
+ .computeIfAbsent(renamedType.getPackageName(), ignoreArgument(ArrayList::new))
+ .add(renamedType.toBinaryName());
+ } else if (classKotlinInfo.isMultiFileFacade()) {
+ metadataVersion.computeIfAbsent(classKotlinInfo::getMetadataVersion);
+ DexType originalType = graphLens.getOriginalType(classType);
+ multiClassFacadeOriginalToRenamed
+ .computeIfAbsent(renamedType.getPackageName(), ignoreArgument(ArrayList::new))
+ .add(Pair.create(originalType.toBinaryName(), renamedType.toBinaryName()));
+ } else {
+ assert classKotlinInfo.isMultiFileClassPart();
+ metadataVersion.computeIfAbsent(classKotlinInfo::getMetadataVersion);
+ KotlinMultiFileClassPartInfo classPart = classKotlinInfo.asMultiFileClassPart();
+ multiClassPartToOriginal
+ .computeIfAbsent(classPart.getFacadeClassName(), ignoreArgument(ArrayList::new))
+ .add(renamedType.toBinaryName());
+ }
+ }
+
+ private Optional<DataEntryResource> build() {
+ // If multiClassParts are non empty but multiFileFacade is, then we have no place to put
+ // the parts anyway, so we can just return empty.
+ if (newFacades.isEmpty() && multiClassFacadeOriginalToRenamed.isEmpty()) {
+ return Optional.empty();
+ }
+ assert metadataVersion.isSet();
+ List<String> packagesSorted = new ArrayList<>(newFacades.keySet());
+ for (String newPackage : multiClassFacadeOriginalToRenamed.keySet()) {
+ if (!newFacades.containsKey(newPackage)) {
+ packagesSorted.add(newPackage);
+ }
+ }
+ Collections.sort(packagesSorted);
+ Writer writer = new Writer();
+ for (String newPackage : packagesSorted) {
+ // Calling other visitors than visitPackageParts are currently not supported.
+ // https://github.com/JetBrains/kotlin/blob/master/libraries/kotlinx-metadata/
+ // jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt#L70
+ Map<String, String> newMultiFiles = new LinkedHashMap<>();
+ multiClassFacadeOriginalToRenamed
+ .getOrDefault(newPackage, Collections.emptyList())
+ .forEach(
+ pair -> {
+ String originalName = pair.getFirst();
+ String rewrittenName = pair.getSecond();
+ multiClassPartToOriginal
+ .getOrDefault(originalName, Collections.emptyList())
+ .forEach(
+ classPart -> {
+ newMultiFiles.put(classPart, rewrittenName);
+ });
+ });
+ writer.visitPackageParts(
+ newPackage,
+ newFacades.getOrDefault(newPackage, Collections.emptyList()),
+ newMultiFiles);
+ }
+ return Optional.of(
+ DataEntryResource.fromBytes(
+ writer.write(metadataVersion.get()).getBytes(),
+ "META-INF/" + moduleName + ".kotlin_module",
+ Origin.unknown()));
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/AlwaysTrueArtProfileRulePredicate.java b/src/main/java/com/android/tools/r8/profile/art/AlwaysTrueArtProfileRulePredicate.java
new file mode 100644
index 0000000..3e26bdc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/AlwaysTrueArtProfileRulePredicate.java
@@ -0,0 +1,23 @@
+// 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.profile.art;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+
+public class AlwaysTrueArtProfileRulePredicate implements ArtProfileRulePredicate {
+
+ @Override
+ public boolean testClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ return true;
+ }
+
+ @Override
+ public boolean testMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilder.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilder.java
new file mode 100644
index 0000000..fbe9d78
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilder.java
@@ -0,0 +1,15 @@
+// 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.profile.art;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+
+public interface ArtProfileBuilder {
+
+ void addClassRule(ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo);
+
+ void addMethodRule(MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
new file mode 100644
index 0000000..3426e7b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
@@ -0,0 +1,113 @@
+// 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.profile.art;
+
+import static com.android.tools.r8.synthesis.SyntheticNaming.COMPANION_CLASS_SUFFIX;
+import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+
+public class ArtProfileBuilderUtils {
+
+ public interface SyntheticToSyntheticContextGeneralization {
+
+ ClassReference getSyntheticContextReference(ClassReference classReference);
+
+ /**
+ * When a startup profile is given to D8, the program input should be dex and the startup
+ * profile should have been generated by launching the dex that is given on input. Therefore,
+ * synthetic items in the ART profile should be present in the program input to D8 with the
+ * exact same synthetic names as in the ART profile. This means that there is no need to
+ * generalize synthetic items to their synthetic context.
+ */
+ static SyntheticToSyntheticContextGeneralization createForD8() {
+ return classReference -> null;
+ }
+
+ /**
+ * When a startup profile is given to R8, the program input is class files and the startup
+ * profile should have been generated by dexing the program input (in release and intermediate
+ * mode) and then launching the resulting app. The synthetic items in the resulting ART profile
+ * do not exist in the program input to R8 (and the same synthetics may receive different names
+ * in the R8 compilation). Therefore, synthetic items in the ART profile are generalized into
+ * matching all synthetics from their synthetic context.
+ */
+ static SyntheticToSyntheticContextGeneralization createForR8() {
+ return classReference -> {
+ // TODO(b/243777722): Move this logic into synthetic items and extend the mapping from
+ // synthetic classes to their synthetic context to all synthetic kinds.
+ String classDescriptor = classReference.getDescriptor();
+ for (int i = 1; i < classDescriptor.length() - 1; i++) {
+ if (classDescriptor.charAt(i) != '$') {
+ continue;
+ }
+ if (classDescriptor.regionMatches(
+ i, COMPANION_CLASS_SUFFIX, 0, COMPANION_CLASS_SUFFIX.length())
+ || classDescriptor.regionMatches(
+ i,
+ EXTERNAL_SYNTHETIC_CLASS_SEPARATOR,
+ 0,
+ EXTERNAL_SYNTHETIC_CLASS_SEPARATOR.length())) {
+ return Reference.classFromDescriptor(classDescriptor.substring(0, i) + ";");
+ }
+ }
+ return null;
+ };
+ }
+ }
+
+ /**
+ * Helper for creating an {@link ArtProfileBuilder} that performs callbacks on the given {@param
+ * startupProfileBuilder}.
+ */
+ public static ArtProfileBuilder createBuilderForArtProfileToStartupProfileConversion(
+ StartupProfileBuilder startupProfileBuilder,
+ ArtProfileRulePredicate rulePredicate,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ return new ArtProfileBuilder() {
+
+ @Override
+ public void addClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ if (rulePredicate.testClassRule(classReference, classRuleInfo)) {
+ ClassReference syntheticContextReference =
+ syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
+ classReference);
+ if (syntheticContextReference == null) {
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder -> startupClassBuilder.setClassReference(classReference));
+ } else {
+ startupProfileBuilder.addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ syntheticContextReference));
+ }
+ }
+ }
+
+ @Override
+ public void addMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ if (rulePredicate.testMethodRule(methodReference, methodRuleInfo)) {
+ ClassReference syntheticContextReference =
+ syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
+ methodReference.getHolderClass());
+ if (syntheticContextReference == null) {
+ startupProfileBuilder.addStartupMethod(
+ startupMethodBuilder -> startupMethodBuilder.setMethodReference(methodReference));
+ } else {
+ startupProfileBuilder.addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ syntheticContextReference));
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfo.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfo.java
new file mode 100644
index 0000000..7ba6bbe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfo.java
@@ -0,0 +1,10 @@
+// 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.profile.art;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface ArtProfileClassRuleInfo {}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
new file mode 100644
index 0000000..7be8869
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
@@ -0,0 +1,16 @@
+// 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.profile.art;
+
+public class ArtProfileClassRuleInfoImpl implements ArtProfileClassRuleInfo {
+
+ private static final ArtProfileClassRuleInfoImpl INSTANCE = new ArtProfileClassRuleInfoImpl();
+
+ private ArtProfileClassRuleInfoImpl() {}
+
+ public static ArtProfileClassRuleInfoImpl empty() {
+ return INSTANCE;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfo.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfo.java
new file mode 100644
index 0000000..6a176ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfo.java
@@ -0,0 +1,20 @@
+// 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.profile.art;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public interface ArtProfileMethodRuleInfo {
+
+ /** Returns true if this method rule method rule is flagged as hot ('H'). */
+ boolean isHot();
+
+ /** Returns true if this method rule method rule is flagged as startup ('S'). */
+ boolean isStartup();
+
+ /** Returns true if this method rule method rule is flagged as post-startup ('P'). */
+ boolean isPostStartup();
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
new file mode 100644
index 0000000..ca2d241
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
@@ -0,0 +1,65 @@
+// 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.profile.art;
+
+public class ArtProfileMethodRuleInfoImpl implements ArtProfileMethodRuleInfo {
+
+ private static final int FLAG_HOT = 1;
+ private static final int FLAG_STARTUP = 2;
+ private static final int FLAG_POST_STARTUP = 4;
+
+ private final int flags;
+
+ ArtProfileMethodRuleInfoImpl(int flags) {
+ this.flags = flags;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public boolean isEmpty() {
+ return flags == 0;
+ }
+
+ @Override
+ public boolean isHot() {
+ return (flags & FLAG_HOT) != 0;
+ }
+
+ @Override
+ public boolean isStartup() {
+ return (flags & FLAG_STARTUP) != 0;
+ }
+
+ @Override
+ public boolean isPostStartup() {
+ return (flags & FLAG_POST_STARTUP) != 0;
+ }
+
+ public static class Builder {
+
+ private int flags;
+
+ public Builder setHot() {
+ flags |= FLAG_HOT;
+ return this;
+ }
+
+ public Builder setStartup() {
+ flags |= FLAG_STARTUP;
+ return this;
+ }
+
+ public Builder setPostStartup() {
+ flags |= FLAG_POST_STARTUP;
+ return this;
+ }
+
+ public ArtProfileMethodRuleInfoImpl build() {
+ return new ArtProfileMethodRuleInfoImpl(flags);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRulePredicate.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRulePredicate.java
new file mode 100644
index 0000000..ea843f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRulePredicate.java
@@ -0,0 +1,17 @@
+// 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.profile.art;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+
+@Keep
+public interface ArtProfileRulePredicate {
+
+ boolean testClassRule(ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo);
+
+ boolean testMethodRule(MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
new file mode 100644
index 0000000..5f0c92b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
@@ -0,0 +1,132 @@
+// 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.profile.art;
+
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.diagnostic.HumanReadableArtProfileParserErrorDiagnostic;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.ClassReferenceUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.Reporter;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UncheckedIOException;
+
+public class HumanReadableArtProfileParser {
+
+ private final ArtProfileBuilder profileBuilder;
+ private final Reporter reporter;
+
+ HumanReadableArtProfileParser(ArtProfileBuilder profileBuilder, Reporter reporter) {
+ this.profileBuilder = profileBuilder;
+ this.reporter = reporter;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public void parse(TextInputStream textInputStream, Origin origin) {
+ try {
+ try (InputStreamReader inputStreamReader =
+ new InputStreamReader(
+ textInputStream.getInputStream(), textInputStream.getCharset());
+ BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
+ int lineNumber = 1;
+ while (bufferedReader.ready()) {
+ String rule = bufferedReader.readLine();
+ if (!parseRule(rule)) {
+ parseError(rule, lineNumber, origin);
+ }
+ lineNumber++;
+ }
+ }
+ if (reporter != null) {
+ reporter.failIfPendingErrors();
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private void parseError(String rule, int lineNumber, Origin origin) {
+ if (reporter != null) {
+ reporter.error(new HumanReadableArtProfileParserErrorDiagnostic(rule, lineNumber, origin));
+ }
+ }
+
+ public boolean parseRule(String rule) {
+ ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
+ ArtProfileMethodRuleInfoImpl.builder();
+ rule = parseFlag(rule, 'H', methodRuleInfoBuilder::setHot);
+ rule = parseFlag(rule, 'S', methodRuleInfoBuilder::setStartup);
+ rule = parseFlag(rule, 'P', methodRuleInfoBuilder::setPostStartup);
+ return parseClassOrMethodDescriptor(rule, methodRuleInfoBuilder.build());
+ }
+
+ private static String parseFlag(String rule, char c, Action action) {
+ if (!rule.isEmpty() && rule.charAt(0) == c) {
+ action.execute();
+ return rule.substring(1);
+ }
+ return rule;
+ }
+
+ private boolean parseClassOrMethodDescriptor(
+ String descriptor, ArtProfileMethodRuleInfoImpl methodRuleInfo) {
+ int arrowStartIndex = descriptor.indexOf("->");
+ if (arrowStartIndex >= 0) {
+ return parseMethodRule(descriptor, methodRuleInfo, arrowStartIndex);
+ } else if (methodRuleInfo.isEmpty()) {
+ return parseClassRule(descriptor);
+ } else {
+ return false;
+ }
+ }
+
+ private boolean parseClassRule(String descriptor) {
+ ClassReference classReference = ClassReferenceUtils.parseClassDescriptor(descriptor);
+ if (classReference == null) {
+ return false;
+ }
+ profileBuilder.addClassRule(classReference, ArtProfileClassRuleInfoImpl.empty());
+ return true;
+ }
+
+ private boolean parseMethodRule(
+ String descriptor, ArtProfileMethodRuleInfoImpl methodRuleInfo, int arrowStartIndex) {
+ MethodReference methodReference =
+ MethodReferenceUtils.parseSmaliString(descriptor, arrowStartIndex);
+ if (methodReference == null) {
+ return false;
+ }
+ profileBuilder.addMethodRule(methodReference, methodRuleInfo);
+ return true;
+ }
+
+ public static class Builder {
+
+ private ArtProfileBuilder profileBuilder;
+ private Reporter reporter;
+
+ public Builder setReporter(Reporter reporter) {
+ this.reporter = reporter;
+ return this;
+ }
+
+ public Builder setProfileBuilder(ArtProfileBuilder profileBuilder) {
+ this.profileBuilder = profileBuilder;
+ return this;
+ }
+
+ public HumanReadableArtProfileParser build() {
+ return new HumanReadableArtProfileParser(profileBuilder, reporter);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParserBuilder.java b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParserBuilder.java
new file mode 100644
index 0000000..9caa97e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParserBuilder.java
@@ -0,0 +1,24 @@
+// 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.profile.art;
+
+import com.android.tools.r8.Keep;
+
+/**
+ * A builder for configuring a parser for the human-readable ART profile format.
+ *
+ * @see <a href="https://developer.android.com/topic/performance/baselineprofiles">ART Baseline
+ * Profiles</a>
+ */
+@Keep
+public interface HumanReadableArtProfileParserBuilder {
+
+ /**
+ * Only include rules from the ART profile that satisfies the given {@param rulePredicate}.
+ *
+ * <p>By default, all rules from the ART profile are included.
+ */
+ HumanReadableArtProfileParserBuilder setRulePredicate(ArtProfileRulePredicate rulePredicate);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnostic.java b/src/main/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnostic.java
new file mode 100644
index 0000000..d93b5f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnostic.java
@@ -0,0 +1,39 @@
+// 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.profile.art.diagnostic;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@Keep
+public class HumanReadableArtProfileParserErrorDiagnostic implements Diagnostic {
+
+ private final String rule;
+ private final int lineNumber;
+ private final Origin origin;
+
+ public HumanReadableArtProfileParserErrorDiagnostic(String rule, int lineNumber, Origin origin) {
+ this.rule = rule;
+ this.lineNumber = lineNumber;
+ this.origin = origin;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return "Unable to parse rule at line " + lineNumber + " from ART profile: " + rule;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 773903d..526fc5f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.shaking.ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS;
import static com.android.tools.r8.shaking.ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS;
import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
@@ -288,26 +289,23 @@
|| parseUnsupportedOptionAndErr(optionStart)) {
// Intentionally left empty.
} else if (acceptString("keepkotlinmetadata")) {
+ String source = "-keepkotlinmetadata";
ProguardKeepRule keepKotlinMetadata =
- ProguardKeepRule.builder()
- .setType(ProguardKeepRuleType.KEEP)
- .setClassType(ProguardClassType.CLASS)
- .setOrigin(origin)
- .setStart(optionStart)
- .setClassNames(
- ProguardClassNameList.builder()
- .addClassName(
- false, ProguardTypeMatcher.create(dexItemFactory.kotlinMetadataType))
- .build())
- .setMemberRules(Collections.singletonList(ProguardMemberRule.defaultKeepAllRule()))
- .setSource("-keepkotlinmetadata")
- .build();
- // Mark the rule as used to ensure we do not report any information messages if the class
+ ProguardKeepRuleUtils.keepClassAndMembersRule(
+ origin, optionStart, dexItemFactory.kotlinMetadataType, source);
+ ProguardKeepRule keepKotlinJvmNameAnnotation =
+ ProguardKeepRuleUtils.keepClassAndMembersRule(
+ origin, optionStart, dexItemFactory.kotlinJvmNameType, source);
+ // Mark the rules as used to ensure we do not report any information messages if the class
// is not present.
keepKotlinMetadata.markAsUsed();
+ keepKotlinJvmNameAnnotation.markAsUsed();
configurationBuilder.addRule(keepKotlinMetadata);
+ configurationBuilder.addRule(keepKotlinJvmNameAnnotation);
configurationBuilder.addKeepAttributePatterns(
Collections.singletonList(RUNTIME_VISIBLE_ANNOTATIONS));
+ configurationBuilder.addKeepAttributePatterns(
+ Collections.singletonList(RUNTIME_INVISIBLE_ANNOTATIONS));
} else if (acceptString("renamesourcefileattribute")) {
skipWhitespace();
if (isOptionalArgumentGiven()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleUtils.java
new file mode 100644
index 0000000..b086470
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleUtils.java
@@ -0,0 +1,29 @@
+// 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.shaking;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.Collections;
+
+public class ProguardKeepRuleUtils {
+
+ public static ProguardKeepRule keepClassAndMembersRule(
+ Origin origin, Position start, DexType type, String source) {
+ return ProguardKeepRule.builder()
+ .setType(ProguardKeepRuleType.KEEP)
+ .setClassType(ProguardClassType.CLASS)
+ .setOrigin(origin)
+ .setStart(start)
+ .setClassNames(
+ ProguardClassNameList.builder()
+ .addClassName(false, ProguardTypeMatcher.create(type))
+ .build())
+ .setMemberRules(Collections.singletonList(ProguardMemberRule.defaultKeepAllRule()))
+ .setSource(source)
+ .build();
+ }
+}
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 d3626a1..47916c1 100644
--- a/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
+++ b/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
@@ -5,6 +5,8 @@
package com.android.tools.r8.startup;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
import java.util.function.Consumer;
/** Interface for providing a startup profile to the compiler. */
@@ -40,4 +42,15 @@
*/
StartupProfileBuilder addSyntheticStartupMethod(
Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer);
+
+ /**
+ * Adds the rules from the given human-readable ART profile to the startup profile and then closes
+ * the stream.
+ *
+ * @see <a href="https://developer.android.com/topic/performance/baselineprofiles">ART Baseline
+ * Profiles</a>
+ */
+ StartupProfileBuilder addHumanReadableArtProfile(
+ TextInputStream textInputStream,
+ Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer);
}
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 4d98373..39ad8fc 100644
--- a/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java
+++ b/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java
@@ -11,10 +11,6 @@
@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/diagnostic/MissingStartupProfileItemsDiagnostic.java b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
new file mode 100644
index 0000000..5faf4b7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
@@ -0,0 +1,125 @@
+// 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.startup.diagnostic;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.experimental.startup.profile.StartupClass;
+import com.android.tools.r8.experimental.startup.profile.StartupMethod;
+import com.android.tools.r8.experimental.startup.profile.SyntheticStartupMethod;
+import com.android.tools.r8.graph.DexDefinitionSupplier;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+@Keep
+public class MissingStartupProfileItemsDiagnostic implements Diagnostic {
+
+ private final List<DexReference> missingStartupItems;
+ private final Origin origin;
+
+ MissingStartupProfileItemsDiagnostic(List<DexReference> missingStartupItems, Origin origin) {
+ assert !missingStartupItems.isEmpty();
+ this.missingStartupItems = missingStartupItems;
+ this.origin = origin;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder();
+ Iterator<DexReference> missingStartupItemIterator = missingStartupItems.iterator();
+
+ // Write first missing startup item.
+ writeMissingStartupItem(builder, missingStartupItemIterator.next());
+
+ // Write remaining missing startup items with line separator before.
+ while (missingStartupItemIterator.hasNext()) {
+ writeMissingStartupItem(
+ builder.append(System.lineSeparator()), missingStartupItemIterator.next());
+ }
+
+ return builder.toString();
+ }
+
+ private void writeMissingStartupItem(StringBuilder builder, DexReference missingStartupItem) {
+ missingStartupItem.apply(
+ missingStartupClass -> builder.append("Startup class not found: "),
+ missingStartupField -> builder.append("Startup field not found: "),
+ missingStartupMethod -> builder.append("Startup method not found: "));
+ builder.append(missingStartupItem.toSourceString());
+ }
+
+ public static class Builder {
+
+ private final DexDefinitionSupplier definitions;
+ private final Set<DexReference> missingStartupItems = Sets.newIdentityHashSet();
+
+ private Origin origin;
+
+ public Builder(DexDefinitionSupplier definitions) {
+ this.definitions = definitions;
+ }
+
+ public static Builder nop() {
+ return new Builder(null);
+ }
+
+ public boolean hasMissingStartupItems() {
+ return !missingStartupItems.isEmpty();
+ }
+
+ public boolean registerStartupClass(StartupClass startupClass) {
+ if (definitions != null && definitions.definitionFor(startupClass.getReference()) == null) {
+ missingStartupItems.add(startupClass.getReference());
+ return true;
+ }
+ return false;
+ }
+
+ public boolean registerStartupMethod(StartupMethod startupMethod) {
+ if (definitions != null && definitions.definitionFor(startupMethod.getReference()) == null) {
+ missingStartupItems.add(startupMethod.getReference());
+ return true;
+ }
+ return false;
+ }
+
+ public boolean registerSyntheticStartupMethod(SyntheticStartupMethod syntheticStartupMethod) {
+ if (definitions != null
+ && definitions.definitionFor(syntheticStartupMethod.getSyntheticContextType()) == null) {
+ missingStartupItems.add(syntheticStartupMethod.getSyntheticContextType());
+ return true;
+ }
+ return false;
+ }
+
+ public Builder setOrigin(Origin origin) {
+ this.origin = origin;
+ return this;
+ }
+
+ public MissingStartupProfileItemsDiagnostic build() {
+ assert hasMissingStartupItems();
+ List<DexReference> sortedMissingStartupItems = new ArrayList<>(missingStartupItems);
+ sortedMissingStartupItems.sort(DexReference::compareTo);
+ return new MissingStartupProfileItemsDiagnostic(sortedMissingStartupItems, origin);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
index e5af39a..fbe55a4 100644
--- a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
+++ b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfCheckCast;
-import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfGoto;
@@ -162,7 +161,7 @@
dexItemFactory.createType(
"Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
dexItemFactory.createString("getInstance")))
- .setCode(method -> createCfCode5_getInstance(dexItemFactory, method))
+ .setCode(method -> createCfCode4_getInstance(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
@@ -176,23 +175,8 @@
dexItemFactory.createProto(
dexItemFactory.createType("V"),
dexItemFactory.createType("Ljava/lang/String;")),
- dexItemFactory.createString("addNonSyntheticMethod")))
- .setCode(method -> createCfCode3_addNonSyntheticMethod(dexItemFactory, method))
- .build(),
- DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.fromCfAccessFlags(9, false))
- .setApiLevelForCode(ComputedApiLevel.unknown())
- .setApiLevelForDefinition(ComputedApiLevel.unknown())
- .setClassFileVersion(CfVersion.V1_8)
- .setMethod(
- dexItemFactory.createMethod(
- dexItemFactory.createType(
- "Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- dexItemFactory.createProto(
- dexItemFactory.createType("V"),
- dexItemFactory.createType("Ljava/lang/String;")),
- dexItemFactory.createString("addSyntheticMethod")))
- .setCode(method -> createCfCode4_addSyntheticMethod(dexItemFactory, method))
+ dexItemFactory.createString("addMethod")))
+ .setCode(method -> createCfCode3_addMethod(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(2, false))
@@ -222,7 +206,7 @@
dexItemFactory.createType("V"),
dexItemFactory.createType("Ljava/lang/String;")),
dexItemFactory.createString("writeToLogcat")))
- .setCode(method -> createCfCode7_writeToLogcat(dexItemFactory, method))
+ .setCode(method -> createCfCode6_writeToLogcat(dexItemFactory, method))
.build(),
DexEncodedMethod.syntheticBuilder()
.setAccessFlags(MethodAccessFlags.fromCfAccessFlags(8, true))
@@ -254,7 +238,7 @@
dexItemFactory.createProto(
dexItemFactory.createType("V"), dexItemFactory.createType("Ljava/io/File;")),
dexItemFactory.createString("writeToFile")))
- .setCode(method -> createCfCode6_writeToFile(dexItemFactory, method))
+ .setCode(method -> createCfCode5_writeToFile(dexItemFactory, method))
.build()
};
}
@@ -460,8 +444,7 @@
ImmutableList.of());
}
- public static CfCode createCfCode3_addNonSyntheticMethod(
- DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode3_addMethod(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -495,72 +478,7 @@
ImmutableList.of());
}
- public static CfCode createCfCode4_addSyntheticMethod(DexItemFactory factory, DexMethod method) {
- CfLabel label0 = new CfLabel();
- CfLabel label1 = new CfLabel();
- CfLabel label2 = new CfLabel();
- return new CfCode(
- method.holder,
- 3,
- 1,
- ImmutableList.of(
- label0,
- new CfInvoke(
- 184,
- factory.createMethod(
- factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- factory.createProto(
- factory.createType(
- "Lcom/android/tools/r8/startup/InstrumentationServerImpl;")),
- factory.createString("getInstance")),
- false),
- new CfNew(factory.stringBuilderType),
- new CfStackInstruction(CfStackInstruction.Opcode.Dup),
- new CfInvoke(
- 183,
- factory.createMethod(
- factory.stringBuilderType,
- factory.createProto(factory.voidType),
- factory.createString("<init>")),
- false),
- new CfConstNumber(83, ValueType.INT),
- new CfInvoke(
- 182,
- factory.createMethod(
- factory.stringBuilderType,
- factory.createProto(factory.stringBuilderType, factory.charType),
- factory.createString("append")),
- false),
- new CfLoad(ValueType.OBJECT, 0),
- new CfInvoke(
- 182,
- factory.createMethod(
- factory.stringBuilderType,
- factory.createProto(factory.stringBuilderType, factory.stringType),
- factory.createString("append")),
- false),
- new CfInvoke(
- 182,
- factory.createMethod(
- factory.stringBuilderType,
- factory.createProto(factory.stringType),
- factory.createString("toString")),
- false),
- new CfInvoke(
- 183,
- factory.createMethod(
- factory.createType("Lcom/android/tools/r8/startup/InstrumentationServerImpl;"),
- factory.createProto(factory.voidType, factory.stringType),
- factory.createString("addLine")),
- false),
- label1,
- new CfReturnVoid(),
- label2),
- ImmutableList.of(),
- ImmutableList.of());
- }
-
- public static CfCode createCfCode5_getInstance(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode4_getInstance(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
return new CfCode(
method.holder,
@@ -578,7 +496,7 @@
ImmutableList.of());
}
- public static CfCode createCfCode6_writeToFile(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode5_writeToFile(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
@@ -805,7 +723,7 @@
ImmutableList.of());
}
- public static CfCode createCfCode7_writeToLogcat(DexItemFactory factory, DexMethod method) {
+ public static CfCode createCfCode6_writeToLogcat(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
CfLabel label2 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 2de90be..e1ac8ba 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -38,7 +38,7 @@
generator.forFixedClass("$EnumUnboxingLocalUtility");
public final SyntheticKind ENUM_UNBOXING_SHARED_UTILITY_CLASS =
generator.forFixedClass("$EnumUnboxingSharedUtility");
- public final SyntheticKind COMPANION_CLASS = generator.forFixedClass("$-CC");
+ public final SyntheticKind COMPANION_CLASS = generator.forFixedClass(COMPANION_CLASS_SUFFIX);
public final SyntheticKind EMULATED_INTERFACE_CLASS =
generator.forFixedClass(InterfaceDesugaringForTesting.EMULATED_INTERFACE_CLASS_SUFFIX);
public final SyntheticKind RETARGET_CLASS = generator.forFixedClass("RetargetClass");
@@ -348,6 +348,7 @@
}
}
+ public static final String COMPANION_CLASS_SUFFIX = "$-CC";
private static final String SYNTHETIC_CLASS_SEPARATOR = "$$";
/**
* The internal synthetic class separator is only used for representing synthetic items during
@@ -360,7 +361,7 @@
* The external synthetic class separator is used when writing classes. It may appear in types
* during compilation as the output of a compilation may be the input to another.
*/
- private static final String EXTERNAL_SYNTHETIC_CLASS_SEPARATOR =
+ public static final String EXTERNAL_SYNTHETIC_CLASS_SEPARATOR =
SYNTHETIC_CLASS_SEPARATOR + "ExternalSynthetic";
/** Method name when generating synthetic methods in a class. */
static final String INTERNAL_SYNTHETIC_METHOD_NAME = "m";
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 14b4d67..679e3c7 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfileProviderUtils;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.DexType;
@@ -42,6 +43,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -524,6 +526,9 @@
StringUtils.joinLines(dumpOptions.getMainDexKeepRules()).getBytes(),
ZipEntry.DEFLATED);
}
+ if (dumpOptions.hasStartupProfileProviders()) {
+ dumpStartupProfileProviders(dumpOptions.getStartupProfileProviders(), options, out);
+ }
nextDexIndex =
dumpProgramResources(
dumpProgramFileName,
@@ -553,6 +558,24 @@
return nextDexIndex;
}
+ private void dumpStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders,
+ InternalOptions options,
+ ZipOutputStream out)
+ throws IOException {
+ int startupProfileProviderIndex = 1;
+ for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
+ String startupProfileProviderFileName =
+ "startup-profile-" + startupProfileProviderIndex + ".txt";
+ writeToZipStream(
+ out,
+ startupProfileProviderFileName,
+ StartupProfileProviderUtils.serializeToString(options, startupProfileProvider).getBytes(),
+ ZipEntry.DEFLATED);
+ startupProfileProviderIndex++;
+ }
+ }
+
private static ClassFileResourceProvider createClassFileResourceProvider(
Map<String, ProgramResource> classPathResources) {
return new ClassFileResourceProvider() {
diff --git a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
index 30754f0..072022d 100644
--- a/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ClassReferenceUtils.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
import java.util.Comparator;
public class ClassReferenceUtils {
@@ -36,6 +37,14 @@
return COMPARATOR;
}
+ public static ClassReference parseClassDescriptor(String classDescriptor) {
+ if (DescriptorUtils.isClassDescriptor(classDescriptor)) {
+ return Reference.classFromDescriptor(classDescriptor);
+ } else {
+ return null;
+ }
+ }
+
public static DexType toDexType(ClassReference classReference, DexItemFactory dexItemFactory) {
return dexItemFactory.createType(classReference.getDescriptor());
}
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index 3c2605a..b30c961 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -18,12 +18,14 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Wrapper to make it easy to call R8 in compat mode when compiling a dump file.
@@ -58,7 +60,8 @@
"--pg-conf",
"--pg-map-output",
"--desugared-lib",
- "--threads");
+ "--threads",
+ "--startup-profile");
private static final List<String> VALID_OPTIONS_WITH_TWO_OPERANDS =
Arrays.asList("--feature-jar");
@@ -84,6 +87,7 @@
List<Path> classpath = new ArrayList<>();
List<Path> config = new ArrayList<>();
List<Path> mainDexRulesFiles = new ArrayList<>();
+ List<Path> startupProfileFiles = new ArrayList<>();
int minApi = 1;
int threads = -1;
boolean enableMissingLibraryApiModeling = false;
@@ -169,6 +173,11 @@
mainDexRulesFiles.add(Paths.get(operand));
break;
}
+ case "--startup-profile":
+ {
+ startupProfileFiles.add(Paths.get(operand));
+ break;
+ }
default:
throw new IllegalArgumentException("Unimplemented option: " + option);
}
@@ -208,6 +217,8 @@
.accept(new Object[] {enableMissingLibraryApiModeling});
getReflectiveBuilderMethod(commandBuilder, "setAndroidPlatformBuild", boolean.class)
.accept(new Object[] {androidPlatformBuild});
+ getReflectiveBuilderMethod(commandBuilder, "addStartupProfileProviders", Collection.class)
+ .accept(new Object[] {createStartupProfileProviders(startupProfileFiles)});
if (desugaredLibJson != null) {
commandBuilder.addDesugaredLibraryConfiguration(readAllBytesJava7(desugaredLibJson));
}
@@ -238,6 +249,17 @@
}
}
+ private static Collection<?> createStartupProfileProviders(List<Path> startupProfileFiles) {
+ List<Object> startupProfileProviders = new ArrayList<>();
+ for (Path startupProfileFile : startupProfileFiles) {
+ callReflectiveUtilsMethod(
+ "createStartupProfileProviderFromDumpFile",
+ new Class<?>[] {Path.class},
+ fn -> startupProfileProviders.add(fn.apply(new Object[] {startupProfileFile})));
+ }
+ return startupProfileProviders;
+ }
+
private static Consumer<Object[]> getReflectiveBuilderMethod(
Builder builder, String setter, Class<?>... parameters) {
try {
@@ -256,6 +278,32 @@
}
}
+ private static void callReflectiveUtilsMethod(
+ String methodName, Class<?>[] parameters, Consumer<Function<Object[], Object>> fnConsumer) {
+ Class<?> utilsClass;
+ try {
+ utilsClass = Class.forName("com.android.tools.r8.utils.CompileDumpUtils");
+ } catch (ClassNotFoundException e) {
+ return;
+ }
+
+ Method declaredMethod;
+ try {
+ declaredMethod = utilsClass.getMethod(methodName, parameters);
+ } catch (NoSuchMethodException e) {
+ return;
+ }
+
+ fnConsumer.accept(
+ args -> {
+ try {
+ return declaredMethod.invoke(null, args);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
// We cannot use StringResource since this class is added to the class path and has access only
// to the public APIs.
private static String readAllBytesJava7(Path filePath) {
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
index b2c4269..32892d7 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
@@ -15,10 +15,12 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
/**
* Wrapper to make it easy to call D8 mode when compiling a dump file.
@@ -50,7 +52,8 @@
"--main-dex-list",
"--main-dex-list-output",
"--desugared-lib",
- "--threads");
+ "--threads",
+ "--startup-profile");
public static void main(String[] args) throws CompilationFailedException {
OutputMode outputMode = OutputMode.DexIndexed;
@@ -61,6 +64,7 @@
List<Path> library = new ArrayList<>();
List<Path> classpath = new ArrayList<>();
List<Path> mainDexRulesFiles = new ArrayList<>();
+ List<Path> startupProfileFiles = new ArrayList<>();
int minApi = 1;
int threads = -1;
boolean enableMissingLibraryApiModeling = false;
@@ -131,6 +135,11 @@
mainDexRulesFiles.add(Paths.get(operand));
break;
}
+ case "--startup-profile":
+ {
+ startupProfileFiles.add(Paths.get(operand));
+ break;
+ }
default:
throw new IllegalArgumentException("Unimplemented option: " + option);
}
@@ -151,6 +160,13 @@
.accept(new Object[] {enableMissingLibraryApiModeling});
getReflectiveBuilderMethod(commandBuilder, "setAndroidPlatformBuild", boolean.class)
.accept(new Object[] {androidPlatformBuild});
+ getReflectiveBuilderMethod(commandBuilder, "addStartupProfileProviders", Collection.class)
+ .accept(
+ new Object[] {
+ startupProfileFiles.stream()
+ .map(CompileDumpUtils::createStartupProfileProviderFromDumpFile)
+ .collect(Collectors.toList())
+ });
if (desugaredLibJson != null) {
commandBuilder.addDesugaredLibraryConfiguration(readAllBytesJava7(desugaredLibJson));
}
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java b/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
new file mode 100644
index 0000000..14acc11
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
@@ -0,0 +1,66 @@
+// 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.utils;
+
+import com.android.tools.r8.KeepMethodForCompileDump;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+class CompileDumpUtils {
+
+ @KeepMethodForCompileDump
+ static StartupProfileProvider createStartupProfileProviderFromDumpFile(Path path) {
+ return new StartupProfileProvider() {
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ try {
+ try (BufferedReader bufferedReader = Files.newBufferedReader(path)) {
+ while (bufferedReader.ready()) {
+ String rule = bufferedReader.readLine();
+ if (rule.charAt(0) == 'S') {
+ String classDescriptor = rule.substring(1);
+ assert DescriptorUtils.isClassDescriptor(classDescriptor);
+ startupProfileBuilder.addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ Reference.classFromDescriptor(classDescriptor)));
+ } else {
+ MethodReference methodReference = MethodReferenceUtils.parseSmaliString(rule);
+ if (methodReference != null) {
+ startupProfileBuilder.addStartupMethod(
+ startupMethodBuilder ->
+ startupMethodBuilder.setMethodReference(methodReference));
+ } else {
+ assert DescriptorUtils.isClassDescriptor(rule);
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder ->
+ startupClassBuilder.setClassReference(
+ Reference.classFromDescriptor(rule)));
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return new PathOrigin(path);
+ }
+ };
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index 7cce06b..99645fa 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -404,6 +404,17 @@
}
/**
+ * Get package java name from a class type name.
+ *
+ * @param typeName a class descriptor i.e. "java.lang.Object"
+ * @return java package name i.e. "java.lang"
+ */
+ public static String getPackageNameFromTypeName(String typeName) {
+ return getPackageNameFromBinaryName(
+ getClassBinaryNameFromDescriptor(javaTypeToDescriptor(typeName)));
+ }
+
+ /**
* Convert package name to a binary name.
*
* @param packageName a package name i.e., "java.lang"
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 f02405c..07322dc 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
-import static com.android.tools.r8.utils.AndroidApiLevel.ANDROID_PLATFORM;
import static com.android.tools.r8.utils.AndroidApiLevel.B;
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
@@ -269,13 +268,15 @@
}
public void configureAndroidPlatformBuild(boolean isAndroidPlatformBuild) {
+ assert !androidPlatformBuild;
if (!isAndroidPlatformBuild) {
return;
}
+ androidPlatformBuild = isAndroidPlatformBuild;
// Configure options according to platform build assumptions.
// See go/r8platformflag and b/232073181.
- minApiLevel = ANDROID_PLATFORM;
apiModelingOptions().disableMissingApiModeling();
+ enableBackportMethods = false;
}
public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
@@ -465,6 +466,9 @@
if (tool == Tool.R8) {
marker.setR8Mode(forceProguardCompatibility ? "compatibility" : "full");
}
+ if (androidPlatformBuild) {
+ marker.setAndroidPlatformBuild();
+ }
return marker;
}
@@ -500,10 +504,6 @@
this.globalSyntheticsConsumer = globalSyntheticsConsumer;
}
- public boolean isAndroidPlatform() {
- return minApiLevel == ANDROID_PLATFORM;
- }
-
public boolean isDesugaredLibraryCompilation() {
return machineDesugaredLibrarySpecification.isLibraryCompilation();
}
@@ -512,10 +512,6 @@
return relocatorCompilation;
}
- public boolean shouldBackportMethods() {
- return !hasConsumer() || isGeneratingDex() || isCfDesugaring();
- }
-
public boolean shouldKeepStackMapTable() {
assert isRelocatorCompilation() || getProguardConfiguration() != null;
return isRelocatorCompilation() || getProguardConfiguration().getKeepAttributes().stackMapTable;
@@ -600,6 +596,7 @@
// Skipping min_api check and compiling an intermediate result intended for later merging.
// Intermediate builds also emits or update synthesized classes mapping.
public boolean intermediate = false;
+ private boolean androidPlatformBuild = false;
public boolean retainCompileTimeAnnotations = true;
public boolean ignoreBootClasspathEnumsForMaindexTracing =
System.getProperty("com.android.tools.r8.ignoreBootClasspathEnumsForMaindexTracing") != null;
@@ -611,6 +608,8 @@
public boolean enableLoadStoreOptimization = true;
// Flag to turn on/off desugaring in D8/R8.
public DesugarState desugarState = DesugarState.ON;
+ // Flag to turn on/off backport methods.
+ public boolean enableBackportMethods = true;
// Flag to turn on/off reduction of nest to improve class merging optimizations.
public boolean enableNestReduction = true;
// Defines interface method rewriter behavior.
@@ -816,7 +815,7 @@
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
- private final StartupOptions startupOptions = new StartupOptions();
+ private final StartupOptions startupOptions = new StartupOptions(this);
private final StartupInstrumentationOptions startupInstrumentationOptions =
new StartupInstrumentationOptions();
public final TestingOptions testing = new TestingOptions();
@@ -2205,7 +2204,10 @@
// the highest known API level when the compiler is built. This ensures that when this is used
// by the Android Platform build (which normally use an API level of 10000) there will be
// no rewriting of backported methods. See b/147480264.
- return desugarState.isOn() && getMinApiLevel().isLessThanOrEqualTo(AndroidApiLevel.LATEST);
+ return enableBackportMethods
+ && desugarState.isOn()
+ // TODO(b/232073181): This platform check should rather be controlled via the platform flag.
+ && getMinApiLevel().isLessThanOrEqualTo(AndroidApiLevel.LATEST);
}
public boolean enableTryWithResourcesDesugaring() {
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 8e240d7..251edcc 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -16,9 +16,11 @@
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.List;
public class MethodReferenceUtils {
@@ -106,6 +108,46 @@
}
}
+ public static MethodReference parseSmaliString(String classAndMethodDescriptor) {
+ int arrowStartIndex = classAndMethodDescriptor.indexOf("->");
+ if (arrowStartIndex >= 0) {
+ return parseSmaliString(classAndMethodDescriptor, arrowStartIndex);
+ }
+ return null;
+ }
+
+ public static MethodReference parseSmaliString(
+ String classAndMethodDescriptor, int arrowStartIndex) {
+ String classDescriptor = classAndMethodDescriptor.substring(0, arrowStartIndex);
+ ClassReference methodHolder = ClassReferenceUtils.parseClassDescriptor(classDescriptor);
+ if (methodHolder == null) {
+ return null;
+ }
+
+ int methodNameStartIndex = arrowStartIndex + 2;
+ String protoWithNameDescriptor = classAndMethodDescriptor.substring(methodNameStartIndex);
+ int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
+ if (methodNameEndIndex <= 0) {
+ return null;
+ }
+ String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
+
+ String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
+ return parseMethodProto(methodHolder, methodName, protoDescriptor);
+ }
+
+ private static MethodReference parseMethodProto(
+ ClassReference methodHolder, String methodName, String protoDescriptor) {
+ List<TypeReference> parameterTypes = new ArrayList<>();
+ for (String parameterTypeDescriptor :
+ DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
+ parameterTypes.add(Reference.typeFromDescriptor(parameterTypeDescriptor));
+ }
+ String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
+ TypeReference returnType = Reference.returnTypeFromDescriptor(returnTypeDescriptor);
+ return Reference.method(methodHolder, methodName, parameterTypes, returnType);
+ }
+
public static DexMethod toDexMethod(
MethodReference methodReference, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(
@@ -115,6 +157,13 @@
methodReference.getMethodName());
}
+ public static String toSmaliString(MethodReference methodReference) {
+ return methodReference.getHolderClass().getDescriptor()
+ + "->"
+ + methodReference.getMethodName()
+ + methodReference.getMethodDescriptor();
+ }
+
public static String toSourceStringWithoutHolderAndReturnType(MethodReference methodReference) {
return toSourceString(methodReference, false, false);
}
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
index 920b83c..aca16a9 100644
--- a/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
+++ b/src/main/java/com/android/tools/r8/utils/ProgramClassCollection.java
@@ -3,14 +3,17 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import com.android.tools.r8.ClassConflictResolver;
import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
import com.android.tools.r8.errors.DuplicateTypesDiagnostic;
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramProvider.GlobalsEntryOrigin;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -61,7 +64,31 @@
public static ProgramClassConflictResolver defaultConflictResolver(Reporter reporter) {
// The default conflict resolver only merges synthetic classes generated by D8 correctly.
// All other conflicts are reported as a fatal error.
- return (DexProgramClass a, DexProgramClass b) -> mergeClasses(reporter, a, b);
+ return wrappedConflictResolver(null, reporter);
+ }
+
+ public static ProgramClassConflictResolver wrappedConflictResolver(
+ ClassConflictResolver clientResolver, Reporter reporter) {
+ return (a, b) -> {
+ DexProgramClass clazz = mergeClasses(a, b);
+ if (clazz != null) {
+ return clazz;
+ }
+ if (clientResolver != null) {
+ List<Origin> origins = new ArrayList<>();
+ origins.add(a.getOrigin());
+ origins.add(b.getOrigin());
+ Origin origin =
+ clientResolver.resolveDuplicateClass(a.getClassReference(), origins, reporter);
+ if (origin == a.getOrigin()) {
+ return a;
+ }
+ if (origin == b.getOrigin()) {
+ return b;
+ }
+ }
+ throw reportDuplicateTypes(reporter, a, b);
+ };
}
private static RuntimeException reportDuplicateTypes(
@@ -72,38 +99,36 @@
ImmutableList.of(a.getOrigin(), b.getOrigin())));
}
- private static DexProgramClass mergeClasses(
- Reporter reporter, DexProgramClass a, DexProgramClass b) {
+ private static DexProgramClass mergeClasses(DexProgramClass a, DexProgramClass b) {
assert a.type == b.type;
boolean syntheticA = a.accessFlags.isSynthetic();
boolean syntheticB = b.accessFlags.isSynthetic();
if (syntheticA && syntheticB) {
- return mergeIfLegacySynthetics(reporter, a, b);
+ return mergeIfLegacySynthetics(a, b);
} else if (syntheticA) {
- return mergeIfGlobalSynthetic(reporter, a, b);
+ return mergeIfGlobalSynthetic(a, b);
} else if (syntheticB) {
- return mergeIfGlobalSynthetic(reporter, b, a);
+ return mergeIfGlobalSynthetic(b, a);
}
- throw reportDuplicateTypes(reporter, a, b);
+ return null;
}
private static DexProgramClass mergeIfGlobalSynthetic(
- Reporter reporter, DexProgramClass synthetic, DexProgramClass nonSynthetic) {
+ DexProgramClass synthetic, DexProgramClass nonSynthetic) {
assert synthetic.accessFlags.isSynthetic();
assert !nonSynthetic.accessFlags.isSynthetic();
if (synthetic.getOrigin() instanceof GlobalsEntryOrigin) {
return nonSynthetic;
}
- throw reportDuplicateTypes(reporter, nonSynthetic, synthetic);
+ return null;
}
- private static DexProgramClass mergeIfLegacySynthetics(
- Reporter reporter, DexProgramClass a, DexProgramClass b) {
+ private static DexProgramClass mergeIfLegacySynthetics(DexProgramClass a, DexProgramClass b) {
if (a.type.isLegacySynthesizedTypeAllowedDuplication()) {
assert assertEqualClasses(a, b);
return a;
}
- throw reportDuplicateTypes(reporter, a, b);
+ return null;
}
private static boolean assertEqualClasses(DexProgramClass a, DexProgramClass b) {
diff --git a/src/main/java/com/android/tools/r8/utils/UTF8TextInputStream.java b/src/main/java/com/android/tools/r8/utils/UTF8TextInputStream.java
new file mode 100644
index 0000000..20252e7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/UTF8TextInputStream.java
@@ -0,0 +1,36 @@
+// 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.utils;
+
+import com.android.tools.r8.TextInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+public class UTF8TextInputStream implements TextInputStream {
+
+ private final InputStream inputStream;
+
+ public UTF8TextInputStream(Path path) throws IOException {
+ this(Files.newInputStream(path));
+ }
+
+ public UTF8TextInputStream(InputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+
+ @Override
+ public Charset getCharset() {
+ return StandardCharsets.UTF_8;
+ }
+}
diff --git a/src/main/keep.txt b/src/main/keep.txt
index ced5b4a..689e5b5 100644
--- a/src/main/keep.txt
+++ b/src/main/keep.txt
@@ -5,6 +5,7 @@
# TODO(b/204058761): Remove when we can use test proguard options.
-keep @com.android.tools.r8.Keep class * { public *; }
-keep @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
+-keepclasseswithmembers class * { @com.android.tools.r8.KeepMethodForCompileDump <methods>; }
# Keep all things that can be reached from the retrace api and keep the annotation
-keep @com.android.tools.r8.KeepForRetraceApi class * { public *; }
diff --git a/src/test/examplesJava9/collectionof/CollectionOfMain.java b/src/test/examplesJava9/collectionof/CollectionOfMain.java
new file mode 100644
index 0000000..fc8384e
--- /dev/null
+++ b/src/test/examplesJava9/collectionof/CollectionOfMain.java
@@ -0,0 +1,24 @@
+// 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 collectionof;
+
+import java.util.List;
+import java.util.Set;
+
+public class CollectionOfMain {
+
+ public static void main(String[] args) {
+ try {
+ System.out.println(Set.of("one").contains(null));
+ } catch (NullPointerException npe) {
+ System.out.println("npe");
+ }
+ try {
+ System.out.println(List.of("one").contains(null));
+ } catch (NullPointerException npe) {
+ System.out.println("npe");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
index dcf816e..b8a76cc 100644
--- a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
+++ b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
@@ -98,8 +98,10 @@
backports.contains("java/lang/StrictMath#multiplyExact(JI)J"));
// Java 9, 10 and 11 method added at API level S.
// The method is not backported in desugared library JDK 11 (already present).
+ // TODO(b/243679691): Should no use backport but retargeting in between 24 and 33,
assertEquals(
- apiLevel < AndroidApiLevel.S.getLevel(),
+ apiLevel < AndroidApiLevel.S.getLevel()
+ && (mode != Mode.LIBRARY_DESUGAR_11 || apiLevel >= AndroidApiLevel.N.getLevel()),
backports.contains("java/util/List#copyOf(Ljava/util/Collection;)Ljava/util/List;"));
// Java 9, 10 and 11 methods not yet added.
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index aebb5a6..e33cb93 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -767,6 +767,12 @@
numThreadsOptionInvalid("two");
}
+ @Test
+ public void androidPlatformBuildFlag() throws Exception {
+ assertFalse(parse().getAndroidPlatformBuild());
+ assertTrue(parse("--android-platform-build").getAndroidPlatformBuild());
+ }
+
@Override
String[] requiredArgsForTest() {
return new String[0];
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index d14b01f..8f465ff 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import java.nio.file.Path;
@@ -30,6 +31,16 @@
private StringBuilder proguardMapOutputBuilder = null;
@Override
+ public boolean isD8TestBuilder() {
+ return true;
+ }
+
+ @Override
+ public D8TestBuilder asD8TestBuilder() {
+ return this;
+ }
+
+ @Override
D8TestBuilder self() {
return this;
}
@@ -118,4 +129,16 @@
getBuilder().setProguardMapConsumer((s, h) -> proguardMapOutputBuilder.append(s));
return self();
}
+
+ public D8TestBuilder addStartupProfileProviders(
+ StartupProfileProvider... startupProfileProviders) {
+ builder.addStartupProfileProviders(startupProfileProviders);
+ return self();
+ }
+
+ public D8TestBuilder addStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders) {
+ builder.addStartupProfileProviders(startupProfileProviders);
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
index abce6c3..0002260 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -44,6 +44,10 @@
return kotlinc.is(compilerVersion);
}
+ public boolean isKotlinDev() {
+ return kotlinc.is(KotlinCompilerVersion.KOTLIN_DEV);
+ }
+
public boolean is(KotlinCompilerVersion compilerVersion, KotlinTargetVersion targetVersion) {
return is(compilerVersion) && this.targetVersion == targetVersion;
}
diff --git a/src/test/java/com/android/tools/r8/MarkerMatcher.java b/src/test/java/com/android/tools/r8/MarkerMatcher.java
index 22610d3..bbd1952 100644
--- a/src/test/java/com/android/tools/r8/MarkerMatcher.java
+++ b/src/test/java/com/android/tools/r8/MarkerMatcher.java
@@ -95,6 +95,20 @@
};
}
+ public static Matcher<Marker> markerAndroidPlatformBuild() {
+ return new MarkerMatcher() {
+ @Override
+ protected boolean eval(Marker marker) {
+ return marker.isAndroidPlatformBuild();
+ }
+
+ @Override
+ protected void explain(Description description) {
+ description.appendText("platform");
+ }
+ };
+ }
+
public static Matcher<Marker> markerCompilationMode(CompilationMode compilationMode) {
return new MarkerMatcher() {
@Override
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 8b951c9..1c7a5ea 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -910,6 +910,12 @@
assertTrue(options.enableStubbingOfClasses);
}
+ @Test
+ public void androidPlatformBuildFlag() throws Exception {
+ assertFalse(parse().getAndroidPlatformBuild());
+ assertTrue(parse("--android-platform-build").getAndroidPlatformBuild());
+ }
+
@Override
String[] requiredArgsForTest() {
return new String[0];
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 824203a..53ff395 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -72,6 +73,16 @@
private boolean createDefaultProguardMapConsumer = true;
@Override
+ public boolean isR8TestBuilder() {
+ return true;
+ }
+
+ @Override
+ public R8TestBuilder<?> asR8TestBuilder() {
+ return this;
+ }
+
+ @Override
R8TestCompileResult internalCompile(
Builder builder,
Consumer<InternalOptions> optionsConsumer,
@@ -766,4 +777,14 @@
createDefaultProguardMapConsumer = false;
return self();
}
+
+ public T addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+ builder.addStartupProfileProviders(startupProfileProviders);
+ return self();
+ }
+
+ public T addStartupProfileProviders(Collection<StartupProfileProvider> startupProfileProviders) {
+ builder.addStartupProfileProviders(startupProfileProviders);
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 5d9d7f5..141f879 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -91,6 +91,22 @@
LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration =
LibraryDesugaringTestConfiguration.DISABLED;
+ public boolean isD8TestBuilder() {
+ return false;
+ }
+
+ public D8TestBuilder asD8TestBuilder() {
+ return null;
+ }
+
+ public boolean isR8TestBuilder() {
+ return false;
+ }
+
+ public R8TestBuilder<?> asR8TestBuilder() {
+ return null;
+ }
+
public boolean isTestShrinkerBuilder() {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
index c53ec8c..a15b915 100644
--- a/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
+++ b/src/test/java/com/android/tools/r8/TestDiagnosticMessages.java
@@ -126,8 +126,9 @@
public abstract TestDiagnosticMessages assertWarningsMatch(
Collection<Matcher<Diagnostic>> matchers);
- public final TestDiagnosticMessages assertErrorsMatch(Matcher<Diagnostic> matcher) {
- return assertErrorsMatch(Collections.singletonList(matcher));
+ @SafeVarargs
+ public final TestDiagnosticMessages assertErrorsMatch(Matcher<Diagnostic>... matchers) {
+ return assertErrorsMatch(Arrays.asList(matchers));
}
public abstract TestDiagnosticMessages assertErrorsMatch(
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index d28d969..8d78785 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -173,6 +173,14 @@
return addDontWarn("javax.annotation.Nullable");
}
+ public T addDontWarnJavaLangReflectAnnotatedType() {
+ return addDontWarn("java.lang.reflect.AnnotatedType");
+ }
+
+ public T addDontWarnJavaLangInvokeLambdaMetadataFactory() {
+ return addDontWarn("java.lang.invoke.LambdaMetafactory");
+ }
+
public T addIgnoreWarnings() {
return addIgnoreWarnings(true);
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
new file mode 100644
index 0000000..ea08c59
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
@@ -0,0 +1,179 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelOutlineInstanceInitializerTest extends TestBase {
+
+ private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+
+ private static final String[] EXPECTED =
+ new String[] {"LibraryClass::<clinit>", "Argument::<clinit>", "Hello World!"};
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addLibraryClasses(LibraryClass.class, Argument.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(Argument.class, classApiLevel))
+ .apply(
+ setMockApiLevelForMethod(
+ Argument.class.getDeclaredConstructor(String.class), classApiLevel))
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(
+ setMockApiLevelForMethod(
+ LibraryClass.class.getDeclaredConstructor(Argument.class), classApiLevel))
+ .apply(setMockApiLevelForMethod(LibraryClass.class.getMethod("print"), classApiLevel))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(
+ addToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, Argument.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(
+ addToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, Argument.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
+ .compile()
+ .inspect(inspector -> inspect(inspector, true))
+ .applyIf(
+ addToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, Argument.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
+ Method mainMethod = Main.class.getMethod("main", String[].class);
+ verifyThat(inspector, parameters, Argument.class.getDeclaredConstructor(String.class))
+ .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+ verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(Argument.class))
+ .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+ verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
+ .isOutlinedFromUntil(mainMethod, isR8 ? AndroidApiLevel.B : classApiLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel)) {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ runResult.assertSuccessWithOutputLines("Not calling API");
+ }
+ }
+
+ public static class Argument {
+
+ private final String string;
+
+ static {
+ System.out.println("Argument::<clinit>");
+ }
+
+ public Argument(String string) {
+ this.string = string;
+ }
+
+ @Override
+ public String toString() {
+ return string;
+ }
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ private final Argument argument;
+
+ static {
+ System.out.println("LibraryClass::<clinit>");
+ }
+
+ public LibraryClass(Argument argument) {
+ this.argument = argument;
+ }
+
+ public void print() {
+ System.out.println(argument.toString());
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ LibraryClass libraryClass = new LibraryClass(new Argument("Hello World!"));
+ libraryClass.print();
+ } else {
+ System.out.println("Not calling API");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 4166e5e..178c115 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -196,6 +196,12 @@
inspector, parameters, Reference.methodFromMethod(method));
}
+ static ApiModelingMethodVerificationHelper verifyThat(
+ CodeInspector inspector, TestParameters parameters, Constructor method) {
+ return new ApiModelingMethodVerificationHelper(
+ inspector, parameters, Reference.methodFromMethod(method));
+ }
+
static ApiModelingFieldVerificationHelper verifyThat(
CodeInspector inspector, TestParameters parameters, Field field) {
return new ApiModelingFieldVerificationHelper(
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 28f7855..8546374 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,11 +7,9 @@
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.references.ClassReference;
+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.BooleanUtils;
@@ -50,41 +48,6 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepClassAndMembersRules(Main.class)
- .addOptionsModification(
- options -> {
- DexItemFactory dexItemFactory = options.dexItemFactory();
- StartupProfile startupProfile =
- StartupProfile.builder()
- .apply(
- builder ->
- getStartupClasses()
- .forEach(
- startupClass ->
- builder.addStartupClass(
- StartupClass.dexBuilder()
- .setClassReference(
- toDexType(startupClass, dexItemFactory))
- .build())))
- .build();
- 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(
inspector ->
inspector
@@ -101,6 +64,24 @@
.assertIsCompleteMergeGroup(
OnClickHandlerA.class, OnClickHandlerB.class))
.assertNoOtherClassesMerged())
+ .addStartupProfileProviders(
+ new StartupProfileProvider() {
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ for (Class<?> startupClass : getStartupClasses()) {
+ ClassReference startupClassReference = Reference.classFromClass(startupClass);
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder ->
+ startupClassBuilder.setClassReference(startupClassReference));
+ }
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ })
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
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 6d12c20..092d2ad 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.compilerapi.androidplatformbuild.AndroidPlatformBuildApiTest;
import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
+import com.android.tools.r8.compilerapi.classconflictresolver.ClassConflictResolverTest;
import com.android.tools.r8.compilerapi.desugardependencies.DesugarDependenciesTest;
import com.android.tools.r8.compilerapi.diagnostics.UnsupportedFeaturesDiagnosticApiTest;
import com.android.tools.r8.compilerapi.globalsynthetics.GlobalSyntheticsTest;
@@ -51,7 +52,8 @@
UnsupportedFeaturesDiagnosticApiTest.ApiTest.class);
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(StartupProfileApiTest.ApiTest.class);
+ ImmutableList.of(
+ StartupProfileApiTest.ApiTest.class, ClassConflictResolverTest.ApiTest.class);
private final TemporaryFolder temp;
diff --git a/src/test/java/com/android/tools/r8/compilerapi/androidplatformbuild/AndroidPlatformBuildApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/androidplatformbuild/AndroidPlatformBuildApiTest.java
index 07d01cc..489c091 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/androidplatformbuild/AndroidPlatformBuildApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/androidplatformbuild/AndroidPlatformBuildApiTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.compilerapi.androidplatformbuild;
+import static com.android.tools.r8.MarkerMatcher.markerAndroidPlatformBuild;
import static com.android.tools.r8.MarkerMatcher.markerMinApi;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -25,6 +26,8 @@
public class AndroidPlatformBuildApiTest extends CompilerApiTestRunner {
+ public static final int MIN_API_LEVEL = 31;
+
public AndroidPlatformBuildApiTest(TestParameters parameters) {
super(parameters);
}
@@ -51,7 +54,10 @@
test.accept(new DexIndexedConsumer.ArchiveConsumer(output));
assertThat(
new CodeInspector(output).getMarkers(),
- CoreMatchers.everyItem(markerMinApi(AndroidApiLevel.ANDROID_PLATFORM)));
+ CoreMatchers.everyItem(
+ CoreMatchers.allOf(
+ markerMinApi(AndroidApiLevel.getAndroidApiLevel(MIN_API_LEVEL)),
+ markerAndroidPlatformBuild())));
}
public static class ApiTest extends CompilerApiTest {
@@ -67,6 +73,7 @@
.addLibraryFiles(getJava8RuntimeJar())
.setProgramConsumer(programConsumer)
.setAndroidPlatformBuild(true)
+ .setMinApiLevel(MIN_API_LEVEL)
.build());
}
@@ -78,6 +85,7 @@
.addLibraryFiles(getJava8RuntimeJar())
.setProgramConsumer(programConsumer)
.setAndroidPlatformBuild(true)
+ .setMinApiLevel(MIN_API_LEVEL)
.build());
}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/classconflictresolver/ClassConflictResolverTest.java b/src/test/java/com/android/tools/r8/compilerapi/classconflictresolver/ClassConflictResolverTest.java
new file mode 100644
index 0000000..d328392
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/classconflictresolver/ClassConflictResolverTest.java
@@ -0,0 +1,116 @@
+// 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.classconflictresolver;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ClassConflictResolver;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+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.origin.PathOrigin;
+import com.android.tools.r8.utils.BooleanBox;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Paths;
+import org.junit.Test;
+
+public class ClassConflictResolverTest extends CompilerApiTestRunner {
+
+ public ClassConflictResolverTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ Origin originA = new PathOrigin(Paths.get("SourceA"));
+ Origin originB = new PathOrigin(Paths.get("SourceB"));
+ BooleanBox called = new BooleanBox(false);
+ new ApiTest(ApiTest.PARAMETERS)
+ .runD8(
+ originA,
+ originB,
+ (reference, origins, handler) -> {
+ called.set(true);
+ assertEquals(ImmutableSet.of(originA, originB), ImmutableSet.copyOf(origins));
+ return originA;
+ });
+ assertTrue(called.get());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Origin originA = new PathOrigin(Paths.get("SourceA"));
+ Origin originB = new PathOrigin(Paths.get("SourceB"));
+ BooleanBox called = new BooleanBox(false);
+ new ApiTest(ApiTest.PARAMETERS)
+ .runR8(
+ originA,
+ originB,
+ (reference, origins, handler) -> {
+ called.set(true);
+ assertEquals(ImmutableSet.of(originA, originB), ImmutableSet.copyOf(origins));
+ return originA;
+ });
+ assertTrue(called.get());
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ public void runD8(Origin originA, Origin originB, ClassConflictResolver resolver)
+ throws Exception {
+ D8.run(
+ D8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), originA)
+ .addClassProgramData(getBytesForClass(getMockClass()), originB)
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .setClassConflictResolver(resolver)
+ .build());
+ }
+
+ public void runR8(Origin originA, Origin originB, ClassConflictResolver resolver)
+ throws Exception {
+ R8.run(
+ R8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), originA)
+ .addClassProgramData(getBytesForClass(getMockClass()), originB)
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setDisableTreeShaking(true)
+ .setDisableMinification(true)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .setClassConflictResolver(resolver)
+ .build());
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ Origin originA = new PathOrigin(Paths.get("SourceA"));
+ Origin originB = new PathOrigin(Paths.get("SourceB"));
+ runD8(originA, originB, (reference, origins, handler) -> originA);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Origin originA = new PathOrigin(Paths.get("SourceA"));
+ Origin originB = new PathOrigin(Paths.get("SourceB"));
+ runR8(originA, originB, (reference, origins, handler) -> originA);
+ }
+ }
+}
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
index f8bc404..808a767 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java
@@ -5,6 +5,8 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
@@ -13,17 +15,30 @@
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TextInputStream;
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.profile.art.ArtProfileClassRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileRulePredicate;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
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.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.function.BiConsumer;
import org.junit.Test;
@@ -82,9 +97,8 @@
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()),
+ new CodeInspector(output.resolve("classes2.dex")).clazz(test.getPostStartupMockClass()),
isPresent());
}
@@ -96,17 +110,50 @@
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())));
+ // Create human-readable ART startup profile.
+ ClassReference mockClassReference = Reference.classFromClass(getMockClass());
+ ClosableByteArrayInputStream inputStream =
+ new ClosableByteArrayInputStream(mockClassReference.getDescriptor().getBytes());
+
+ // Create parser and parse ART profile.
+ List<ClassReference> seenClasses = new ArrayList<>();
+ startupProfileBuilder.addHumanReadableArtProfile(
+ new TextInputStream() {
+
+ @Override
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+
+ @Override
+ public Charset getCharset() {
+ return StandardCharsets.UTF_8;
+ }
+ },
+ parserBuilder ->
+ parserBuilder.setRulePredicate(
+ new ArtProfileRulePredicate() {
+ @Override
+ public boolean testClassRule(
+ ClassReference reference, ArtProfileClassRuleInfo classRuleInfo) {
+ seenClasses.add(reference);
+ return true;
+ }
+
+ @Override
+ public boolean testMethodRule(
+ MethodReference reference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ return true;
+ }
+ }));
+
+ // Verify rule predicate has been used and input stream is closed.
+ assertEquals(1, seenClasses.size());
+ assertEquals(mockClassReference, seenClasses.get(0));
+ assertTrue(inputStream.isClosed());
}
@Override
@@ -197,5 +244,24 @@
Collections.singleton(startupProfileProvider);
commandBuilder.addStartupProfileProviders(startupProfileProviders);
}
+
+ private static class ClosableByteArrayInputStream extends ByteArrayInputStream {
+
+ private boolean closed;
+
+ public ClosableByteArrayInputStream(byte[] buf) {
+ super(buf);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ closed = true;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index 0f0a782..7c3bb9d 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.List;
@@ -61,6 +62,12 @@
.noTreeShaking()
.setMode(CompilationMode.DEBUG)
.setMinApi(parameters.getApiLevel())
+ .applyIf(
+ kotlinTestParameters.isKotlinDev(),
+ TestShrinkerBuilder::addDontWarnJavaLangReflectAnnotatedType)
+ .applyIf(
+ parameters.isCfRuntime() && kotlinTestParameters.isKotlinDev(),
+ TestShrinkerBuilder::addDontWarnJavaLangInvokeLambdaMetadataFactory)
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."));
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
new file mode 100644
index 0000000..c9f3f99
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CollectionOfTest.java
@@ -0,0 +1,92 @@
+// 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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Assume;
+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 CollectionOfTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final Path INPUT_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "collectionof.jar");
+ private static final String EXPECTED_OUTPUT_BACKPORT = StringUtils.lines("false", "false");
+ private static final String EXPECTED_OUTPUT_CORRECT = StringUtils.lines("npe", "npe");
+ private static final String MAIN_CLASS = "collectionof.CollectionOfMain";
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ getJdk8Jdk11(),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public CollectionOfTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ private String getExpectedOutput(boolean desugaredLib) {
+ if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.R)) {
+ return EXPECTED_OUTPUT_CORRECT;
+ }
+ if (desugaredLib && libraryDesugaringSpecification != JDK8) {
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ return EXPECTED_OUTPUT_CORRECT;
+ }
+ // TODO(b/243679691): This should also be correct, but is not because we use backports in
+ // partial desugaring.
+ return EXPECTED_OUTPUT_BACKPORT;
+ }
+ return EXPECTED_OUTPUT_BACKPORT;
+ }
+
+ @Test
+ public void testCollectionOf() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(getExpectedOutput(true));
+ }
+
+ @Test
+ public void testCollectionOfReference() throws Throwable {
+ Assume.assumeTrue(
+ "Run only once",
+ libraryDesugaringSpecification == JDK8 && compilationSpecification == D8_L8DEBUG);
+ testForD8()
+ .addProgramFiles(INPUT_JAR)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(getExpectedOutput(false));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
index 8c572cf..be94723 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -99,6 +99,12 @@
.allowDiagnosticMessages()
.allowUnusedDontWarnKotlinReflectJvmInternal(
kotlinParameters.getCompiler().isNot(KOTLINC_1_3_72))
+ .applyIfR8TestBuilder(
+ b -> {
+ if (kotlinParameters.isKotlinDev()) {
+ b.addDontWarnJavaLangReflectAnnotatedType();
+ }
+ })
.compile()
.inspect(
i -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
index 380f2d5..4b41588 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
@@ -218,6 +218,11 @@
return this;
}
+ public DesugaredLibraryTestBuilder<T> applyIfR8TestBuilder(Consumer<R8TestBuilder<?>> consumer) {
+ withR8TestBuilder(consumer);
+ return this;
+ }
+
public DesugaredLibraryTestBuilder<T> allowDiagnosticWarningMessages() {
withR8TestBuilder(R8TestBuilder::allowDiagnosticWarningMessages);
return this;
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
index a44f221..dbeb66b 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -144,17 +144,20 @@
R8TestCompileResult r8CompileResult =
compileApplicationWithR8(
testBuilder ->
- testBuilder.addOptionsModification(
- options -> {
- if (startupProfileProvider != null) {
- options
- .getStartupOptions()
- .setStartupProfileProvider(startupProfileProvider)
- .setEnableMinimalStartupDex(enableMinimalStartupDex)
- .setEnableStartupBoundaryOptimizations(
- enableStartupBoundaryOptimizations);
- }
- }));
+ testBuilder
+ .addOptionsModification(
+ options -> {
+ if (startupProfileProvider != null) {
+ options
+ .getStartupOptions()
+ .setEnableMinimalStartupDex(enableMinimalStartupDex)
+ .setEnableStartupBoundaryOptimizations(
+ enableStartupBoundaryOptimizations);
+ }
+ })
+ .applyIf(
+ startupProfileProvider != null,
+ b -> b.addStartupProfileProviders(startupProfileProvider)));
// Compile desugared library using cf backend (without keep rules).
L8TestCompileResult l8CompileResult = compileDesugaredLibraryWithL8();
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 c7211db..2527b81 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
@@ -13,16 +13,12 @@
import com.android.tools.r8.ArchiveProgramResourceProvider;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
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.experimental.startup.StartupProfileProviderUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -238,34 +234,19 @@
boolean enableStartupBoundaryOptimizations,
Path outDirectory)
throws Exception {
- StartupProfileProvider startupProfileProvider =
- 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(
- options ->
- options
- .getStartupOptions()
- .setEnableMinimalStartupDex(enableMinimalStartupDex)
- .setEnableStartupBoundaryOptimizations(enableStartupBoundaryOptimizations)
- .setStartupProfileProvider(startupProfileProvider)),
+ testBuilder
+ .addOptionsModification(
+ options ->
+ options
+ .getStartupOptions()
+ .setEnableMinimalStartupDex(enableMinimalStartupDex)
+ .setEnableStartupBoundaryOptimizations(
+ enableStartupBoundaryOptimizations))
+ .addStartupProfileProviders(
+ StartupProfileProviderUtils.createFromHumanReadableArtProfile(
+ chromeDirectory.resolve("startup.txt"))),
outDirectory);
}
diff --git a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
index 90771b4..a52c762 100644
--- a/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
+++ b/src/test/java/com/android/tools/r8/invalid/DuplicateDefinitionsTest.java
@@ -4,25 +4,39 @@
package com.android.tools.r8.invalid;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.jasmin.JasminTestBase;
-import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.nio.charset.Charset;
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 DuplicateDefinitionsTest extends JasminTestBase {
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ private final TestParameters parameters;
+
+ public DuplicateDefinitionsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void testDuplicateMethods() throws Exception {
JasminBuilder jasminBuilder = new JasminBuilder();
@@ -32,38 +46,37 @@
classBuilder.addVirtualMethod("method", "V", ".limit locals 1", ".limit stack 0", "return");
classBuilder.addVirtualMethod("method", "V", ".limit locals 1", ".limit stack 0", "return");
- // Run D8 and intercept warnings.
- PrintStream stderr = System.err;
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- System.setErr(new PrintStream(baos));
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(jasminBuilder.buildClasses())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertOnlyWarnings()
+ .assertWarningsMatch(
+ diagnosticMessage(
+ containsString(
+ "Ignoring an implementation of the method `void"
+ + " C.main(java.lang.String[])` because it has multiple"
+ + " definitions")),
+ diagnosticMessage(
+ containsString(
+ "Ignoring an implementation of the method `void C.method()` because"
+ + " it has multiple definitions"))))
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz("C");
+ assertThat(clazz, isPresent());
- AndroidApp app = compileWithD8(jasminBuilder.build());
+ // There are two direct methods, but only because one is <init>.
+ assertEquals(
+ 2, clazz.getDexProgramClass().getMethodCollection().numberOfDirectMethods());
+ assertThat(clazz.method("void", "<init>", ImmutableList.of()), isPresent());
- String output = new String(baos.toByteArray(), Charset.defaultCharset());
- System.setOut(stderr);
-
- // Check that warnings were emitted.
- assertThat(
- output,
- containsString(
- "Ignoring an implementation of the method `void C.main(java.lang.String[])` because "
- + "it has multiple definitions"));
- assertThat(
- output,
- containsString(
- "Ignoring an implementation of the method `void C.method()` because "
- + "it has multiple definitions"));
-
- CodeInspector inspector = new CodeInspector(app);
- ClassSubject clazz = inspector.clazz("C");
- assertThat(clazz, isPresent());
-
- // There are two direct methods, but only because one is <init>.
- assertEquals(2, clazz.getDexProgramClass().getMethodCollection().numberOfDirectMethods());
- assertThat(clazz.method("void", "<init>", ImmutableList.of()), isPresent());
-
- // There is only one virtual method.
- assertEquals(1, clazz.getDexProgramClass().getMethodCollection().numberOfVirtualMethods());
+ // There is only one virtual method.
+ assertEquals(
+ 1, clazz.getDexProgramClass().getMethodCollection().numberOfVirtualMethods());
+ });
}
@Test
@@ -75,26 +88,26 @@
classBuilder.addStaticField("staticFld", "LC;", null);
classBuilder.addStaticField("staticFld", "LC;", null);
- // Run D8 and intercept warnings.
- PrintStream stderr = System.err;
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- System.setErr(new PrintStream(baos));
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(jasminBuilder.buildClasses())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertOnlyWarnings()
+ .assertWarningsMatch(
+ diagnosticMessage(
+ containsString("Field `C C.fld` has multiple definitions")),
+ diagnosticMessage(
+ containsString("Field `C C.staticFld` has multiple definitions"))))
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz("C");
+ assertThat(clazz, isPresent());
- AndroidApp app = compileWithD8(jasminBuilder.build());
-
- String output = new String(baos.toByteArray(), Charset.defaultCharset());
- System.setOut(stderr);
-
- // Check that warnings were emitted.
- assertThat(output, containsString("Field `C C.fld` has multiple definitions"));
- assertThat(output, containsString("Field `C C.staticFld` has multiple definitions"));
-
- CodeInspector inspector = new CodeInspector(app);
- ClassSubject clazz = inspector.clazz("C");
- assertThat(clazz, isPresent());
-
- // Redundant fields have been removed.
- assertEquals(1, clazz.getDexProgramClass().instanceFields().size());
- assertEquals(1, clazz.getDexProgramClass().staticFields().size());
+ // Redundant fields have been removed.
+ assertEquals(1, clazz.getDexProgramClass().instanceFields().size());
+ assertEquals(1, clazz.getDexProgramClass().staticFields().size());
+ });
}
}
diff --git a/src/test/java/com/android/tools/r8/invalid/DuplicateProgramTypesTest.java b/src/test/java/com/android/tools/r8/invalid/DuplicateProgramTypesTest.java
index e8c8d39..1efe38f 100644
--- a/src/test/java/com/android/tools/r8/invalid/DuplicateProgramTypesTest.java
+++ b/src/test/java/com/android/tools/r8/invalid/DuplicateProgramTypesTest.java
@@ -10,10 +10,12 @@
import static org.hamcrest.CoreMatchers.hasItems;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
+import com.android.tools.r8.BaseCompilerCommand;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
@@ -21,6 +23,8 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.SetUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -32,7 +36,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withNoneRuntime().build();
+ return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build();
}
public DuplicateProgramTypesTest(TestParameters parameters) {
@@ -55,39 +59,113 @@
}
};
+ private void addDuplicateDefinitions(BaseCompilerCommand.Builder<?, ?> builder) throws Exception {
+ byte[] bytes = ToolHelper.getClassAsBytes(TestClass.class);
+ builder.addClassProgramData(bytes, originA);
+ builder.addClassProgramData(bytes, originB);
+ }
+
+ private void addResolvingHandler(BaseCompilerCommand.Builder<?, ?> builder) throws Exception {
+ builder.setClassConflictResolver(
+ (reference, origins, handler) -> {
+ assertEquals(
+ SetUtils.newIdentityHashSet(originA, originB), SetUtils.newIdentityHashSet(origins));
+ return originA;
+ });
+ }
+
+ private void addNonResolvingHandler(BaseCompilerCommand.Builder<?, ?> builder) throws Exception {
+ builder.setClassConflictResolver(
+ (reference, origins, handler) -> {
+ assertEquals(
+ SetUtils.newIdentityHashSet(originA, originB), SetUtils.newIdentityHashSet(origins));
+ return null;
+ });
+ }
+
+ private void checkErrorDiagnostic(TestDiagnosticMessages diagnostics) {
+ diagnostics.assertOnlyErrors();
+ diagnostics.assertErrorsCount(1);
+ DuplicateTypesDiagnostic diagnostic = (DuplicateTypesDiagnostic) diagnostics.getErrors().get(0);
+ assertEquals(Position.UNKNOWN, diagnostic.getPosition());
+ assertThat(diagnostic.getType(), equalTo(Reference.classFromClass(TestClass.class)));
+ assertThat(diagnostic.getOrigin(), anyOf(equalTo(originA), equalTo(originB)));
+ assertThat(diagnostic.getOrigins(), hasItems(originA, originB));
+ assertThat(
+ diagnostic.getDiagnosticMessage(),
+ allOf(
+ containsString("defined multiple"),
+ containsString("SourceA"),
+ containsString("SourceB")));
+ }
+
@Test
- public void test() throws Exception {
- try {
- byte[] bytes = ToolHelper.getClassAsBytes(TestClass.class);
- testForD8()
- .setMinApi(parameters.getRuntime())
- .apply(
- b -> {
- b.getBuilder().addClassProgramData(bytes, originA);
- b.getBuilder().addClassProgramData(bytes, originB);
- })
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertOnlyErrors();
- diagnostics.assertErrorsCount(1);
- DuplicateTypesDiagnostic diagnostic =
- (DuplicateTypesDiagnostic) diagnostics.getErrors().get(0);
- assertEquals(Position.UNKNOWN, diagnostic.getPosition());
- assertThat(
- diagnostic.getType(), equalTo(Reference.classFromClass(TestClass.class)));
- assertThat(diagnostic.getOrigin(), anyOf(equalTo(originA), equalTo(originB)));
- assertThat(diagnostic.getOrigins(), hasItems(originA, originB));
- assertThat(
- diagnostic.getDiagnosticMessage(),
- allOf(
- containsString("defined multiple"),
- containsString("SourceA"),
- containsString("SourceB")));
- });
- } catch (CompilationFailedException e) {
- return; // Success.
- }
- fail("Expected test to fail with CompilationFailedException");
+ public void testDefaultError() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForD8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .apply(b -> addDuplicateDefinitions(b.getBuilder()))
+ .compileWithExpectedDiagnostics(this::checkErrorDiagnostic));
+ }
+
+ @Test
+ public void testResolvedConflictD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .apply(
+ b -> {
+ addDuplicateDefinitions(b.getBuilder());
+ addResolvingHandler(b.getBuilder());
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
+ }
+
+ @Test
+ public void testResolvedConflictR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .apply(
+ b -> {
+ addDuplicateDefinitions(b.getBuilder());
+ addResolvingHandler(b.getBuilder());
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world");
+ }
+
+ @Test
+ public void testNonResolvedConflictD8() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForD8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .apply(
+ b -> {
+ addDuplicateDefinitions(b.getBuilder());
+ addNonResolvingHandler(b.getBuilder());
+ })
+ .compileWithExpectedDiagnostics(this::checkErrorDiagnostic));
+ }
+
+ @Test
+ public void testNonResolvedConflictR8() throws Exception {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .apply(
+ b -> {
+ addDuplicateDefinitions(b.getBuilder());
+ addNonResolvingHandler(b.getBuilder());
+ })
+ .compileWithExpectedDiagnostics(this::checkErrorDiagnostic));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
index e14d7a6..acd52f2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -34,10 +35,17 @@
}
private void test(Collection<String> rules) throws Exception {
+ boolean notShrinking = rules.contains("-dontshrink");
testForR8(parameters.getBackend())
.addProgramFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.addKeepRules(rules)
+ .applyIf(
+ notShrinking && kotlinParameters.isKotlinDev(),
+ TestShrinkerBuilder::addDontWarnJavaLangReflectAnnotatedType)
+ .applyIf(
+ notShrinking && kotlinParameters.isKotlinDev() && parameters.isCfRuntime(),
+ TestShrinkerBuilder::addDontWarnJavaLangInvokeLambdaMetadataFactory)
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeepTest.java
similarity index 90%
rename from src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
rename to src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeepTest.java
index ee16233..a862b2c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeepTest.java
@@ -22,7 +22,7 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class MetadataRewriteDependentKeep extends KotlinMetadataTestBase {
+public class MetadataRewriteDependentKeepTest extends KotlinMetadataTestBase {
@Parameterized.Parameters(name = "{0}, {1}")
public static Collection<Object[]> data() {
@@ -33,7 +33,7 @@
private final TestParameters parameters;
- public MetadataRewriteDependentKeep(
+ public MetadataRewriteDependentKeepTest(
TestParameters parameters, KotlinTestParameters kotlinParameters) {
super(kotlinParameters);
this.parameters = parameters;
@@ -56,7 +56,8 @@
// All kept classes should have their kotlin metadata.
for (FoundClassSubject clazz : inspector.allClasses()) {
if (clazz.getFinalName().startsWith("kotlin.io")
- || clazz.getFinalName().equals("kotlin.Metadata")) {
+ || clazz.getFinalName().equals("kotlin.Metadata")
+ || clazz.getFinalName().equals("kotlin.jvm.JvmName")) {
assertNotNull(clazz.getKotlinClassMetadata());
} else {
assertNull(clazz.getKotlinClassMetadata());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
index 619f4a1..99cdd63 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -15,10 +15,13 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
-import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.KmClassSubject;
@@ -28,8 +31,13 @@
import com.android.tools.r8.utils.codeinspector.KmTypeSubject;
import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject;
import com.android.tools.r8.utils.codeinspector.Matchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -184,8 +192,7 @@
@Test
public void testMetadataInExtensionFunction_renamedKotlinSources() throws Exception {
assumeTrue(kotlinc.getCompilerVersion().isGreaterThanOrEqualTo(KOTLINC_1_4_20));
- Box<String> renamedKtHolder = new Box<>();
- Path libJar =
+ R8TestCompileResult r8LibraryResult =
testForR8(parameters.getBackend())
.addClasspathFiles(kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinAnnotationJar())
.addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion))
@@ -201,21 +208,65 @@
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
- .compile()
- .inspect(
- inspector -> {
- ClassSubject clazz = inspector.clazz(PKG + ".extension_function_lib.BKt");
- assertThat(clazz, isPresentAndRenamed());
- renamedKtHolder.set(clazz.getFinalName());
- })
- .writeToZip();
+ .compile();
+ Path kotlinSourcePath = getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main");
- kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
- .addClasspathFiles(libJar)
- .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main"))
- .setOutputPath(temp.newFolder().toPath())
- // TODO(b/242289529): Expect that we can compile without errors.
- .compile(true);
+ String kotlinSource = FileUtils.readTextFile(kotlinSourcePath, StandardCharsets.UTF_8);
+
+ CodeInspector inspector = r8LibraryResult.inspector();
+
+ ClassSubject clazz = inspector.clazz(PKG + ".extension_function_lib.BKt");
+ assertThat(clazz, isPresentAndRenamed());
+
+ // Rewrite the source kotlin files that reference the four extension methods into their renamed
+ // name by changing the import statement and the actual call.
+ String[] methodNames = new String[] {"extension", "csHash", "longArrayHash", "myApply"};
+ for (String methodName : methodNames) {
+ MethodSubject method = clazz.uniqueMethodWithName(methodName);
+ assertThat(method, isPresentAndRenamed());
+ String finalMethodName = method.getFinalName();
+ kotlinSource =
+ kotlinSource.replace(
+ "import com.android.tools.r8.kotlin.metadata.extension_function_lib." + methodName,
+ "import "
+ + DescriptorUtils.getPackageNameFromTypeName(clazz.getFinalName())
+ + "."
+ + finalMethodName);
+ kotlinSource = kotlinSource.replace(")." + methodName, ")." + finalMethodName);
+ }
+
+ Path newSource = temp.newFolder().toPath().resolve("main.kt");
+ Files.write(newSource, kotlinSource.getBytes(StandardCharsets.UTF_8));
+
+ Path libJar = r8LibraryResult.writeToZip();
+ Path tempUnzipPath = temp.newFolder().toPath();
+ List<String> kotlinModuleFiles = new ArrayList<>();
+ ZipUtils.unzip(
+ libJar,
+ tempUnzipPath,
+ f -> {
+ if (f.getName().endsWith(".kotlin_module")) {
+ kotlinModuleFiles.add(f.getName());
+ }
+ return false;
+ });
+ assertEquals(Collections.singletonList("META-INF/main.kotlin_module"), kotlinModuleFiles);
+ Path output =
+ kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
+ .addClasspathFiles(libJar)
+ .addSourceFiles(newSource)
+ .setOutputPath(temp.newFolder().toPath())
+ .compile();
+
+ if (kotlinParameters.isOlderThan(KOTLINC_1_4_20)) {
+ return;
+ }
+
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectRenamed(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
index 5a3960e..11c9e7b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -15,6 +15,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -36,7 +37,7 @@
@RunWith(Parameterized.class)
public class MetadataRewriteInMultifileClassTest extends KotlinMetadataTestBase {
- private static final String EXPECTED = StringUtils.lines(", 1, 2, 3");
+ private static final String EXPECTED = StringUtils.lines(", 1, 2, 3", ", 1, 2, 3");
private final TestParameters parameters;
@@ -44,7 +45,11 @@
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withCfRuntimes().build(),
- getKotlinTestParameters().withAllCompilersAndTargetVersions().build());
+ getKotlinTestParameters()
+ .withAllCompilers()
+ .withOldCompilersStartingFrom(KotlinCompilerVersion.KOTLINC_1_4_20)
+ .withAllTargetVersions()
+ .build());
}
public MetadataRewriteInMultifileClassTest(
@@ -95,9 +100,7 @@
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/151193860): update to just .compile() once fixed.
.compileRaw();
- // TODO(b/151193860): should be able to compile!
assertNotEquals(0, kotlinTestCompileResult.exitCode);
assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
}
@@ -113,8 +116,6 @@
assertThat(joinOfInt, not(isPresent()));
inspectMetadataForFacade(inspector, util);
- // TODO(b/156290332): Seems like this test is incorrect and should never work.
- // inspectSignedKt(inspector);
}
@Test
@@ -126,6 +127,7 @@
// Keep UtilKt#comma*Join*().
.addKeepRules("-keep class **.UtilKt")
.addKeepRules("-keep,allowobfuscation class **.UtilKt__SignedKt")
+ .addKeepRules("-keep,allowobfuscation class **.UtilKt__UnsignedKt")
.addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
// Keep yet rename joinOf*(String).
.addKeepRules("-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
@@ -134,16 +136,18 @@
.inspect(this::inspectRenamed)
.writeToZip();
- ProcessResult kotlinTestCompileResult =
+ Path output =
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(libJar)
.addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main"))
.setOutputPath(temp.newFolder().toPath())
- // TODO(b/151193860): update to just .compile() once fixed.
- .compileRaw();
- // TODO(b/151193860): should be able to compile!
- assertNotEquals(0, kotlinTestCompileResult.exitCode);
- assertThat(kotlinTestCompileResult.stderr, containsString("unresolved reference: join"));
+ .compile();
+
+ testForJvm()
+ .addRunClasspathFiles(kotlinc.getKotlinStdlibJar(), libJar)
+ .addClasspath(output)
+ .run(parameters.getRuntime(), PKG + ".multifileclass_app.MainKt")
+ .assertSuccessWithOutput(EXPECTED);
}
private void inspectRenamed(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
index 16fc35d..24b3e58 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
@@ -66,7 +66,8 @@
// All kept classes should have their kotlin metadata.
for (FoundClassSubject clazz : inspector.allClasses()) {
if (clazz.getFinalName().startsWith("kotlin.io")
- || clazz.getFinalName().equals("kotlin.Metadata")) {
+ || clazz.getFinalName().equals("kotlin.Metadata")
+ || clazz.getFinalName().equals("kotlin.jvm.JvmName")) {
assertNotNull(clazz.getKotlinClassMetadata());
assertNotNull(clazz.getKotlinClassMetadata().getHeader().getData2());
} else {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
index 72e86b4..dc7d16e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/extension_function_app/main.kt
@@ -13,7 +13,7 @@
B().doStuff()
B().extension()
- "R8".csHash()
+ ("R8").csHash()
longArrayOf(42L).longArrayHash()
B().myApply { this.doStuff() }
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/multifileclass_app/main.kt b/src/test/java/com/android/tools/r8/kotlin/metadata/multifileclass_app/main.kt
index ce098bf..6d81d8c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/multifileclass_app/main.kt
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/multifileclass_app/main.kt
@@ -5,7 +5,18 @@
import com.android.tools.r8.kotlin.metadata.multifileclass_lib.join
-fun main() {
+fun signed() {
val s = sequenceOf(1, 2, 3)
println(s.join())
}
+
+@OptIn(ExperimentalUnsignedTypes::class)
+fun unsigned() {
+ val s = sequenceOf(1u, 2u, 3u)
+ println(s.join())
+}
+
+fun main() {
+ signed()
+ unsigned()
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
index 1cc87e4..a5400d6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.kotlin.metadata.KotlinMetadataTestBase;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -98,6 +99,12 @@
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.allowDiagnosticMessages()
.allowUnusedDontWarnKotlinReflectJvmInternal(kotlinc.isNot(KOTLINC_1_3_72))
+ .applyIf(
+ parameters.isCfRuntime() && kotlinParameters.isKotlinDev(),
+ TestShrinkerBuilder::addDontWarnJavaLangInvokeLambdaMetadataFactory)
+ .applyIf(
+ kotlinParameters.isKotlinDev(),
+ TestShrinkerBuilder::addDontWarnJavaLangReflectAnnotatedType)
.compile()
.assertNoErrorMessages()
// -keepattributes Signature is added in kotlin-reflect from version 1.4.20.
diff --git a/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticTest.java b/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticTest.java
new file mode 100644
index 0000000..aee021b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticTest.java
@@ -0,0 +1,82 @@
+// 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.profile.art.diagnostic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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.ConsumerUtils;
+import com.android.tools.r8.utils.UTF8TextInputStream;
+import java.io.ByteArrayInputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class HumanReadableArtProfileParserErrorDiagnosticTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void test() throws Exception {
+ testForD8()
+ .addProgramClasses(Main.class)
+ .addStartupProfileProviders(
+ new StartupProfileProvider() {
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+
+ startupProfileBuilder.addHumanReadableArtProfile(
+ new UTF8TextInputStream(
+ new ByteArrayInputStream("INVALID1\nINVALID2".getBytes())),
+ ConsumerUtils.emptyConsumer());
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ })
+ .release()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticType(HumanReadableArtProfileParserErrorDiagnostic.class),
+ diagnosticMessage(
+ equalTo("Unable to parse rule at line 1 from ART profile: INVALID1"))),
+ allOf(
+ diagnosticType(HumanReadableArtProfileParserErrorDiagnostic.class),
+ diagnosticMessage(
+ equalTo("Unable to parse rule at line 2 from ART profile: INVALID2"))));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 9c3bbea..2b8d68b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -911,7 +911,8 @@
verifyParserEndsCleanly();
ProguardConfiguration config = parser.getConfig();
assertEquals(
- "-keepattributes RuntimeVisibleAnnotations", config.getKeepAttributes().toString());
+ "-keepattributes RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations",
+ config.getKeepAttributes().toString());
assertEquals(
StringUtils.joinLines("-keep class kotlin.Metadata {", " *;", "}"),
config.getRules().get(0).toString());
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfRuleWithFieldAnnotation.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfRuleWithFieldAnnotation.java
index 3349571..8105b81 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfRuleWithFieldAnnotation.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfRuleWithFieldAnnotation.java
@@ -3,9 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking.ifrule;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -19,6 +22,15 @@
public class IfRuleWithFieldAnnotation extends TestBase {
static final String EXPECTED = "foobar";
+ public static final String CONDITIONAL_KEEP_RULE =
+ "-if class * {"
+ + " @com.android.tools.r8.shaking.ifrule.IfRuleWithFieldAnnotation$SerializedName"
+ + " <fields>; }\n"
+ + "-keep,allowobfuscation class <1> {\n"
+ + " <init>(...);\n"
+ + " @com.android.tools.r8.shaking.ifrule.IfRuleWithFieldAnnotation$SerializedNamed"
+ + " <fields>;\n"
+ + "}";
private final TestParameters parameters;
@@ -36,15 +48,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(Foo.class, Bar.class, SerializedName.class)
.addKeepMainRule(Foo.class)
- .addKeepRules(
- "-if class * {"
- + " @com.android.tools.r8.shaking.ifrule.IfRuleWithFieldAnnotation$SerializedName"
- + " <fields>; }\n"
- + "-keep,allowobfuscation class <1> {\n"
- + " <init>(...);\n"
- + " @com.android.tools.r8.shaking.ifrule.IfRuleWithFieldAnnotation$SerializedNamed"
- + " <fields>;\n"
- + "}")
+ .addKeepRules(CONDITIONAL_KEEP_RULE)
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
@@ -54,13 +58,75 @@
})
.run(parameters.getRuntime(), Foo.class)
.assertSuccessWithOutputLines(EXPECTED);
+ // We should remove the class if the usage of the field is not live.
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Foo.class, Bar.class, SerializedName.class, FooNotCallingBar.class)
+ .addKeepMainRule(FooNotCallingBar.class)
+ .addKeepRules(CONDITIONAL_KEEP_RULE)
+ .allowUnusedProguardConfigurationRules()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Bar.class), isAbsent());
+ })
+ .run(parameters.getRuntime(), FooNotCallingBar.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testProguard() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForProguard(ProguardVersion.V7_0_0)
+ .addProgramClasses(Foo.class, Bar.class, SerializedName.class)
+ .addDontWarn(getClass())
+ .addKeepMainRule(Foo.class)
+ .addKeepRules(CONDITIONAL_KEEP_RULE)
+ .compile()
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Bar.class).field("int", "value"), isPresent());
+ assertThat(codeInspector.clazz(Bar.class).init("int"), isPresent());
+ })
+ .run(parameters.getRuntime(), Foo.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ testForProguard(ProguardVersion.V7_0_0)
+ .addProgramClasses(Foo.class, Bar.class, SerializedName.class, FooNotCallingBar.class)
+ .addDontWarn(getClass())
+ .addKeepMainRule(FooNotCallingBar.class)
+ .noMinification()
+ .addKeepRules(CONDITIONAL_KEEP_RULE)
+ .compile()
+ .inspect(
+ codeInspector -> {
+ // The if rule above will make proguard keep the class and the constructor, but not
+ // the field. If we don't have the rule, proguard will remove the class, see test
+ // below.
+ assertThat(codeInspector.clazz(Bar.class), isPresent());
+ assertThat(codeInspector.clazz(Bar.class).init("int"), isPresent());
+ assertThat(codeInspector.clazz(Bar.class).field("int", "value"), isAbsent());
+ })
+ .run(parameters.getRuntime(), FooNotCallingBar.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ // Test that without the conditional keep rule proguard correctly removes the class.
+ testForProguard(ProguardVersion.V7_0_0)
+ .addProgramClasses(Foo.class, Bar.class, SerializedName.class, FooNotCallingBar.class)
+ .addDontWarn(getClass())
+ .addKeepMainRule(FooNotCallingBar.class)
+ .noMinification()
+ .compile()
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Bar.class), isAbsent());
+ })
+ .run(parameters.getRuntime(), FooNotCallingBar.class)
+ .assertSuccessWithOutputLines(EXPECTED);
}
@Retention(RetentionPolicy.RUNTIME)
public @interface SerializedName {}
public static class Foo {
- public static Object object;
public static void main(String[] args) {
callOnBar(args);
@@ -84,6 +150,12 @@
}
}
+ public static class FooNotCallingBar {
+ public static void main(String[] args) {
+ System.out.println("foobar");
+ }
+ }
+
public static class Bar {
@SerializedName public int value;
diff --git a/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java b/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java
index 6eff67b..23e8577 100644
--- a/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java
+++ b/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java
@@ -11,12 +11,10 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupMethod;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
+import com.android.tools.r8.startup.profile.ExternalStartupMethod;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -41,12 +39,12 @@
@Test
public void test() throws Exception {
- List<StartupItem<ClassReference, MethodReference, ?>> startupItems =
+ List<ExternalStartupItem> startupItems =
ImmutableList.of(
- StartupClass.referenceBuilder()
+ ExternalStartupClass.builder()
.setClassReference(Reference.classFromClass(Main.class))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
.build());
testForR8(parameters.getBackend())
@@ -60,12 +58,14 @@
inspector -> {
ClassSubject mainClassSubject = inspector.clazz(Main.class);
assertThat(mainClassSubject, isPresent());
+ // The postStartupMethod() should be inlined into PostStartupClass.runPostStartup().
assertThat(mainClassSubject.uniqueMethodWithName("postStartupMethod"), isAbsent());
ClassSubject postStartupClassSubject = inspector.clazz(PostStartupClass.class);
- // TODO(b/242815611): Should be present since inlining increases the size of the
- // startup.
- assertThat(postStartupClassSubject, isAbsent());
+ assertThat(postStartupClassSubject, isPresent());
+ // The runPostStartup() method must not be inlined into Main.main().
+ assertThat(
+ postStartupClassSubject.uniqueMethodWithName("runPostStartup"), isPresent());
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello, world!");
diff --git a/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java b/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
index 328b485..1f19f3c 100644
--- a/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
+++ b/src/test/java/com/android/tools/r8/startup/InstrumentationServerImpl.java
@@ -25,14 +25,10 @@
return InstrumentationServerImpl.INSTANCE;
}
- public static void addNonSyntheticMethod(String descriptor) {
+ public static void addMethod(String descriptor) {
getInstance().addLine(descriptor);
}
- public static void addSyntheticMethod(String descriptor) {
- getInstance().addLine('S' + descriptor);
- }
-
private void addLine(String line) {
synchronized (lines) {
if (!lines.add(line)) {
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
index 35df0b6..db55acb 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -12,17 +12,17 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -45,7 +45,7 @@
@Test
public void test() throws Exception {
- List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+ Set<ExternalStartupItem> startupList = new LinkedHashSet<>();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
.apply(
@@ -55,7 +55,9 @@
.compile()
.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
.run(parameters.getRuntime(), Main.class)
- .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+ .apply(
+ StartupTestingUtils.removeStartupListFromStdout(
+ startupList::add, SyntheticToSyntheticContextGeneralization.createForR8()))
.assertSuccessWithOutputLines(getExpectedOutput());
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
index bc0292f..3709e7a 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -9,21 +9,22 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupMethod;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
+import com.android.tools.r8.startup.profile.ExternalStartupMethod;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -48,7 +49,9 @@
@Test
public void test() throws Exception {
Path out = temp.newFolder().toPath().resolve("out.txt").toAbsolutePath();
- List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+ Set<ExternalStartupItem> startupList = new LinkedHashSet<>();
+ SyntheticToSyntheticContextGeneralization syntheticGeneralization =
+ SyntheticToSyntheticContextGeneralization.createForD8();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
.applyIf(
@@ -65,8 +68,11 @@
.run(parameters.getRuntime(), Main.class, Boolean.toString(logcat), out.toString())
.applyIf(
logcat,
- StartupTestingUtils.removeStartupListFromStdout(startupList::add),
- runResult -> StartupTestingUtils.readStartupListFromFile(out, startupList::add))
+ StartupTestingUtils.removeStartupListFromStdout(
+ startupList::add, syntheticGeneralization),
+ runResult ->
+ StartupTestingUtils.readStartupListFromFile(
+ out, startupList::add, syntheticGeneralization))
.assertSuccessWithOutputLines(getExpectedOutput());
assertEquals(getExpectedStartupList(), startupList);
}
@@ -75,19 +81,18 @@
return ImmutableList.of("foo");
}
- private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
- throws NoSuchMethodException {
- return ImmutableList.of(
- StartupClass.referenceBuilder()
+ private Set<ExternalStartupItem> getExpectedStartupList() throws NoSuchMethodException {
+ return ImmutableSet.of(
+ ExternalStartupClass.builder()
.setClassReference(Reference.classFromClass(Main.class))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
.build(),
- StartupClass.referenceBuilder()
+ ExternalStartupClass.builder()
.setClassReference(Reference.classFromClass(AStartupClass.class))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(
Reference.methodFromMethod(AStartupClass.class.getDeclaredMethod("foo")))
.build());
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 179810b..42ff835 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -16,14 +16,15 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.desugar.LambdaClass;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
+import com.android.tools.r8.startup.profile.ExternalStartupMethod;
+import com.android.tools.r8.startup.profile.ExternalSyntheticStartupMethod;
import com.android.tools.r8.startup.utils.MixedSectionLayoutInspector;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
@@ -34,10 +35,12 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Collection;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -90,7 +93,7 @@
Path optimizedApp = r8CompileResult.writeToZip();
// Then instrument the app to generate a startup list for the minified app.
- List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+ Set<ExternalStartupItem> startupList = new LinkedHashSet<>();
testForD8(parameters.getBackend())
.addProgramFiles(optimizedApp)
.apply(
@@ -100,7 +103,9 @@
.compile()
.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
.run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
- .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+ .apply(
+ StartupTestingUtils.removeStartupListFromStdout(
+ startupList::add, SyntheticToSyntheticContextGeneralization.createForD8()))
.assertSuccessWithOutputLines(getExpectedOutput())
.apply(
runResult ->
@@ -125,7 +130,7 @@
@Test
public void testLayoutUsingR8() throws Exception {
// First generate a startup list for the original app.
- List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+ Set<ExternalStartupItem> startupList = new LinkedHashSet<>();
D8TestCompileResult instrumentationCompileResult =
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
@@ -139,7 +144,9 @@
instrumentationCompileResult
.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
.run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
- .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+ .apply(
+ StartupTestingUtils.removeStartupListFromStdout(
+ startupList::add, SyntheticToSyntheticContextGeneralization.createForR8()))
.assertSuccessWithOutputLines(getExpectedOutput())
.apply(
runResult ->
@@ -167,7 +174,7 @@
private void configureStartupOptions(
TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
CodeInspector inspector,
- List<StartupItem<ClassReference, MethodReference, ?>> startupList) {
+ Collection<ExternalStartupItem> startupList) {
testBuilder
.addOptionsModification(
options -> {
@@ -188,36 +195,30 @@
}
@SuppressWarnings("unchecked")
- private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList(
+ private Set<ExternalStartupItem> getExpectedStartupList(
CodeInspector inspector, boolean isStartupListForOriginalApp) throws NoSuchMethodException {
- ImmutableList.Builder<StartupItem<ClassReference, MethodReference, ?>> builder =
- ImmutableList.builder();
+ ImmutableSet.Builder<ExternalStartupItem> builder = ImmutableSet.builder();
builder.add(
- StartupClass.referenceBuilder()
+ ExternalStartupClass.builder()
.setClassReference(Reference.classFromClass(Main.class))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
.build(),
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(A.class))
- .build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupClass.builder().setClassReference(Reference.classFromClass(A.class)).build(),
+ ExternalStartupMethod.builder()
.setMethodReference(Reference.methodFromMethod(A.class.getDeclaredMethod("a")))
.build(),
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(B.class))
- .build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupClass.builder().setClassReference(Reference.classFromClass(B.class)).build(),
+ ExternalStartupMethod.builder()
.setMethodReference(
Reference.methodFromMethod(B.class.getDeclaredMethod("b", boolean.class)))
.build());
if (useLambda) {
if (isStartupListForOriginalApp) {
builder.add(
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(B.class))
- .setSynthetic()
+ ExternalSyntheticStartupMethod.builder()
+ .setSyntheticContextReference(Reference.classFromClass(B.class))
.build());
} else {
ClassSubject bClassSubject = inspector.clazz(B.class);
@@ -238,14 +239,14 @@
externalSyntheticLambdaClassSubject.getFinalReference();
builder.add(
- StartupClass.referenceBuilder()
+ ExternalStartupClass.builder()
.setClassReference(externalSyntheticLambdaClassReference)
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(
MethodReferenceUtils.instanceConstructor(externalSyntheticLambdaClassReference))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(
Reference.method(
externalSyntheticLambdaClassReference,
@@ -253,7 +254,7 @@
ImmutableList.of(Reference.classFromClass(Object.class)),
null))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(
Reference.method(
Reference.classFromClass(B.class),
@@ -263,16 +264,14 @@
.build());
}
builder.add(
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(
Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0", Object.class)))
.build());
}
builder.add(
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(C.class))
- .build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupClass.builder().setClassReference(Reference.classFromClass(C.class)).build(),
+ ExternalStartupMethod.builder()
.setMethodReference(Reference.methodFromMethod(C.class.getDeclaredMethod("c")))
.build());
return builder.build();
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index b9ceea2..2b47d1d 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -14,13 +14,14 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
+import com.android.tools.r8.startup.profile.ExternalStartupMethod;
+import com.android.tools.r8.startup.profile.ExternalSyntheticStartupMethod;
import com.android.tools.r8.startup.utils.MixedSectionLayoutInspector;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
@@ -29,9 +30,11 @@
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
+import com.google.common.collect.ImmutableSet;
import java.util.Collection;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -61,7 +64,7 @@
@Test
public void test() throws Exception {
- List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+ LinkedHashSet<ExternalStartupItem> startupList = new LinkedHashSet<>();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
.apply(
@@ -71,7 +74,9 @@
.compile()
.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
.run(parameters.getRuntime(), Main.class)
- .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+ .apply(
+ StartupTestingUtils.removeStartupListFromStdout(
+ startupList::add, SyntheticToSyntheticContextGeneralization.createForR8()))
.assertSuccessWithOutputLines(getExpectedOutput());
assertEquals(getExpectedStartupList(), startupList);
@@ -101,38 +106,30 @@
return ImmutableList.of("A", "B", "C");
}
- private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
- throws NoSuchMethodException {
- return ImmutableList.of(
- StartupClass.referenceBuilder()
+ private Set<ExternalStartupItem> getExpectedStartupList() throws NoSuchMethodException {
+ return ImmutableSet.of(
+ ExternalStartupClass.builder()
.setClassReference(Reference.classFromClass(Main.class))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
.build(),
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(A.class))
- .build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupClass.builder().setClassReference(Reference.classFromClass(A.class)).build(),
+ ExternalStartupMethod.builder()
.setMethodReference(Reference.methodFromMethod(A.class.getDeclaredMethod("a")))
.build(),
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(B.class))
- .build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupClass.builder().setClassReference(Reference.classFromClass(B.class)).build(),
+ ExternalStartupMethod.builder()
.setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("b")))
.build(),
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(B.class))
- .setSynthetic()
+ ExternalSyntheticStartupMethod.builder()
+ .setSyntheticContextReference(Reference.classFromClass(B.class))
.build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupMethod.builder()
.setMethodReference(Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0")))
.build(),
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(C.class))
- .build(),
- StartupMethod.referenceBuilder()
+ ExternalStartupClass.builder().setClassReference(Reference.classFromClass(C.class)).build(),
+ ExternalStartupMethod.builder()
.setMethodReference(Reference.methodFromMethod(C.class.getDeclaredMethod("c")))
.build());
}
diff --git a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
new file mode 100644
index 0000000..60cf897
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
@@ -0,0 +1,110 @@
+// 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.startup.diagnostic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+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.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MissingStartupProfileItemsDiagnosticTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(Backend.DEX)
+ .addProgramClasses(Main.class)
+ .addStartupProfileProviders(getStartupProfileProviders())
+ .release()
+ .setIntermediate(true)
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(Backend.DEX)
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .addStartupProfileProviders(getStartupProfileProviders())
+ .allowDiagnosticWarningMessages()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+ }
+
+ private static Collection<StartupProfileProvider> getStartupProfileProviders() {
+ StartupProfileProvider startupProfileProvider =
+ new StartupProfileProvider() {
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ ClassReference fooClassReference = Reference.classFromTypeName("Foo");
+ ClassReference barClassReference = Reference.classFromTypeName("Bar");
+ ClassReference bazClassReference = Reference.classFromTypeName("Baz");
+ startupProfileBuilder
+ .addStartupClass(
+ startupClassBuilder -> startupClassBuilder.setClassReference(fooClassReference))
+ .addStartupMethod(
+ startupMethodBuilder ->
+ startupMethodBuilder.setMethodReference(
+ MethodReferenceUtils.mainMethod(barClassReference)))
+ .addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ bazClassReference));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+ return Collections.singleton(startupProfileProvider);
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(MissingStartupProfileItemsDiagnostic.class),
+ diagnosticMessage(
+ equalTo(
+ StringUtils.joinLines(
+ "Startup method not found: void Bar.main(java.lang.String[])",
+ "Startup class not found: Baz",
+ "Startup class not found: Foo")))));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/dump/DumpStartupProfileProvidersTest.java b/src/test/java/com/android/tools/r8/startup/dump/DumpStartupProfileProvidersTest.java
new file mode 100644
index 0000000..edec3b4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/dump/DumpStartupProfileProvidersTest.java
@@ -0,0 +1,166 @@
+// 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.startup.dump;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+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.AndroidApiLevel;
+import com.android.tools.r8.utils.DumpInputFlags;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DumpStartupProfileProvidersTest extends TestBase {
+
+ private enum DumpStrategy {
+ DIRECTORY,
+ FILE;
+
+ DumpInputFlags createDumpInputFlags(Path dump) {
+ if (this == DIRECTORY) {
+ return DumpInputFlags.dumpToDirectory(dump);
+ }
+ assert this == FILE;
+ return DumpInputFlags.dumpToFile(dump);
+ }
+
+ Path createDumpPath(TemporaryFolder temp) throws IOException {
+ if (this == DIRECTORY) {
+ return temp.newFolder().toPath();
+ }
+ assert this == FILE;
+ return temp.newFile("dump.zip").toPath();
+ }
+ }
+
+ @Parameter(0)
+ public DumpStrategy dumpStrategy;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, {0}")
+ public static List<Object[]> data() {
+ return buildParameters(DumpStrategy.values(), getTestParameters().withNoneRuntime().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path dump = dumpStrategy.createDumpPath(temp);
+ DumpInputFlags dumpInputFlags = dumpStrategy.createDumpInputFlags(dump);
+ try {
+ testForR8(Backend.DEX)
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(options -> options.setDumpInputFlags(dumpInputFlags))
+ .addStartupProfileProviders(getStartupProfileProviders())
+ .allowDiagnosticInfoMessages()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (dumpInputFlags.shouldFailCompilation()) {
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(containsString("Dumped compilation inputs to:")));
+ } else {
+ diagnostics.assertInfosMatch(
+ diagnosticMessage(containsString("Dumped compilation inputs to:")));
+ }
+ });
+ assertFalse("Expected compilation to fail", dumpInputFlags.shouldFailCompilation());
+ } catch (CompilationFailedException e) {
+ assertTrue("Expected compilation to succeed", dumpInputFlags.shouldFailCompilation());
+ }
+ verifyDump(dump);
+ }
+
+ private Collection<StartupProfileProvider> getStartupProfileProviders() {
+ return ImmutableList.of(
+ new StartupProfileProvider() {
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder ->
+ startupClassBuilder.setClassReference(Reference.classFromClass(Main.class)));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ },
+ new StartupProfileProvider() {
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ startupProfileBuilder.addStartupMethod(
+ startupMethodBuilder ->
+ startupMethodBuilder.setMethodReference(
+ MethodReferenceUtils.mainMethod(Main.class)));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ });
+ }
+
+ private void verifyDump(Path dump) throws IOException {
+ if (dumpStrategy == DumpStrategy.DIRECTORY) {
+ List<Path> dumps =
+ Files.walk(dump, 1).filter(path -> path.toFile().isFile()).collect(Collectors.toList());
+ assertEquals(1, dumps.size());
+ dump = dumps.get(0);
+ }
+
+ assertTrue(Files.exists(dump));
+ Path unzipped = temp.newFolder().toPath();
+ ZipUtils.unzip(dump.toString(), unzipped.toFile());
+
+ Path startupProfile1 = unzipped.resolve("startup-profile-1.txt");
+ assertTrue(Files.exists(startupProfile1));
+ assertEquals(
+ Lists.newArrayList(Reference.classFromClass(Main.class).getDescriptor()),
+ FileUtils.readAllLines(startupProfile1));
+
+ Path startupProfile2 = unzipped.resolve("startup-profile-2.txt");
+ assertTrue(Files.exists(startupProfile2));
+ assertEquals(
+ Lists.newArrayList(
+ MethodReferenceUtils.toSmaliString(MethodReferenceUtils.mainMethod(Main.class))),
+ FileUtils.readAllLines(startupProfile2));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupClass.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupClass.java
new file mode 100644
index 0000000..4eee018
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupClass.java
@@ -0,0 +1,71 @@
+// 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.startup.profile;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.startup.StartupClassBuilder;
+import java.util.function.Function;
+
+public class ExternalStartupClass extends ExternalStartupItem {
+
+ private final ClassReference classReference;
+
+ ExternalStartupClass(ClassReference classReference) {
+ this.classReference = classReference;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public <T> T apply(
+ Function<ExternalStartupClass, T> classFunction,
+ Function<ExternalStartupMethod, T> methodFunction,
+ Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction) {
+ return classFunction.apply(this);
+ }
+
+ public ClassReference getReference() {
+ return classReference;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ExternalStartupClass that = (ExternalStartupClass) o;
+ return classReference.equals(that.classReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return classReference.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return classReference.getTypeName();
+ }
+
+ public static class Builder implements StartupClassBuilder {
+
+ private ClassReference classReference;
+
+ @Override
+ public Builder setClassReference(ClassReference classReference) {
+ this.classReference = classReference;
+ return this;
+ }
+
+ public ExternalStartupClass build() {
+ return new ExternalStartupClass(classReference);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupItem.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupItem.java
new file mode 100644
index 0000000..e3aa745
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupItem.java
@@ -0,0 +1,15 @@
+// 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.startup.profile;
+
+import java.util.function.Function;
+
+public abstract class ExternalStartupItem {
+
+ public abstract <T> T apply(
+ Function<ExternalStartupClass, T> classFunction,
+ Function<ExternalStartupMethod, T> methodFunction,
+ Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction);
+}
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupMethod.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupMethod.java
new file mode 100644
index 0000000..cda7ff7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/profile/ExternalStartupMethod.java
@@ -0,0 +1,73 @@
+// 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.startup.profile;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.startup.StartupMethodBuilder;
+import com.android.tools.r8.startup.profile.ExternalStartupClass.Builder;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import java.util.function.Function;
+
+public class ExternalStartupMethod extends ExternalStartupItem {
+
+ private final MethodReference methodReference;
+
+ ExternalStartupMethod(MethodReference methodReference) {
+ this.methodReference = methodReference;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public <T> T apply(
+ Function<ExternalStartupClass, T> classFunction,
+ Function<ExternalStartupMethod, T> methodFunction,
+ Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction) {
+ return methodFunction.apply(this);
+ }
+
+ public MethodReference getReference() {
+ return methodReference;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ExternalStartupMethod that = (ExternalStartupMethod) o;
+ return methodReference.equals(that.methodReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return methodReference.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return MethodReferenceUtils.toSourceString(methodReference);
+ }
+
+ public static class Builder implements StartupMethodBuilder {
+
+ private MethodReference methodReference;
+
+ @Override
+ public Builder setMethodReference(MethodReference methodReference) {
+ this.methodReference = methodReference;
+ return this;
+ }
+
+ public ExternalStartupMethod build() {
+ return new ExternalStartupMethod(methodReference);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/profile/ExternalSyntheticStartupMethod.java b/src/test/java/com/android/tools/r8/startup/profile/ExternalSyntheticStartupMethod.java
new file mode 100644
index 0000000..6d07638
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/profile/ExternalSyntheticStartupMethod.java
@@ -0,0 +1,71 @@
+// 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.startup.profile;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
+import java.util.function.Function;
+
+public class ExternalSyntheticStartupMethod extends ExternalStartupItem {
+
+ private final ClassReference syntheticContextReference;
+
+ ExternalSyntheticStartupMethod(ClassReference syntheticContextReference) {
+ this.syntheticContextReference = syntheticContextReference;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public <T> T apply(
+ Function<ExternalStartupClass, T> classFunction,
+ Function<ExternalStartupMethod, T> methodFunction,
+ Function<ExternalSyntheticStartupMethod, T> syntheticMethodFunction) {
+ return syntheticMethodFunction.apply(this);
+ }
+
+ public ClassReference getSyntheticContextReference() {
+ return syntheticContextReference;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ExternalSyntheticStartupMethod that = (ExternalSyntheticStartupMethod) o;
+ return syntheticContextReference.equals(that.syntheticContextReference);
+ }
+
+ @Override
+ public int hashCode() {
+ return syntheticContextReference.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "S(" + syntheticContextReference.getTypeName() + ")";
+ }
+
+ public static class Builder implements SyntheticStartupMethodBuilder {
+
+ private ClassReference syntheticContextReference;
+
+ @Override
+ public Builder setSyntheticContextReference(ClassReference syntheticContextReference) {
+ this.syntheticContextReference = syntheticContextReference;
+ return this;
+ }
+
+ public ExternalSyntheticStartupMethod build() {
+ return new ExternalSyntheticStartupMethod(syntheticContextReference);
+ }
+ }
+}
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 ad43216..691a33d 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
@@ -5,38 +5,43 @@
package com.android.tools.r8.startup.utils;
import static com.android.tools.r8.TestBase.transformer;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TextInputStream;
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;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
-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.profile.art.AlwaysTrueArtProfileRulePredicate;
+import com.android.tools.r8.profile.art.ArtProfileBuilder;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
+import com.android.tools.r8.profile.art.HumanReadableArtProfileParser;
+import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
+import com.android.tools.r8.startup.StartupClassBuilder;
+import com.android.tools.r8.startup.StartupMethodBuilder;
import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
+import com.android.tools.r8.startup.profile.ExternalStartupMethod;
+import com.android.tools.r8.startup.profile.ExternalSyntheticStartupMethod;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.ClassReferenceUtils;
-import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.UTF8TextInputStream;
import java.io.IOException;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.List;
+import java.util.Collection;
import java.util.function.Consumer;
import org.junit.rules.TemporaryFolder;
@@ -44,51 +49,74 @@
private static String startupInstrumentationTag = "startup";
- private enum AppVariant {
- ORIGINAL,
- OPTIMIZED;
+ private static ArtProfileBuilder createStartupItemFactory(
+ Consumer<ExternalStartupItem> startupItemConsumer,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ StartupProfileBuilder startupProfileBuilder =
+ new StartupProfileBuilder() {
+ @Override
+ public StartupProfileBuilder addStartupClass(
+ Consumer<StartupClassBuilder> startupClassBuilderConsumer) {
+ ExternalStartupClass.Builder startupClassBuilder = ExternalStartupClass.builder();
+ startupClassBuilderConsumer.accept(startupClassBuilder);
+ startupItemConsumer.accept(startupClassBuilder.build());
+ return this;
+ }
- boolean isOriginal() {
- return this == ORIGINAL;
- }
+ @Override
+ public StartupProfileBuilder addStartupMethod(
+ Consumer<StartupMethodBuilder> startupMethodBuilderConsumer) {
+ ExternalStartupMethod.Builder startupMethodBuilder = ExternalStartupMethod.builder();
+ startupMethodBuilderConsumer.accept(startupMethodBuilder);
+ startupItemConsumer.accept(startupMethodBuilder.build());
+ return this;
+ }
+
+ @Override
+ public StartupProfileBuilder addSyntheticStartupMethod(
+ Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer) {
+ ExternalSyntheticStartupMethod.Builder syntheticStartupMethodBuilder =
+ ExternalSyntheticStartupMethod.builder();
+ syntheticStartupMethodBuilderConsumer.accept(syntheticStartupMethodBuilder);
+ startupItemConsumer.accept(syntheticStartupMethodBuilder.build());
+ return this;
+ }
+
+ @Override
+ public StartupProfileBuilder addHumanReadableArtProfile(
+ TextInputStream textInputStream,
+ Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
+ throw new Unreachable();
+ }
+ };
+ return ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
+ startupProfileBuilder,
+ new AlwaysTrueArtProfileRulePredicate(),
+ syntheticToSyntheticContextGeneralization);
}
public static ThrowableConsumer<D8TestBuilder>
enableStartupInstrumentationForOriginalAppUsingFile(TestParameters parameters) {
- return testBuilder ->
- enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, false);
+ return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, false);
}
public static ThrowableConsumer<D8TestBuilder>
enableStartupInstrumentationForOriginalAppUsingLogcat(TestParameters parameters) {
- return testBuilder ->
- enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, true);
- }
-
- public static ThrowableConsumer<D8TestBuilder>
- enableStartupInstrumentationForOptimizedAppUsingFile(TestParameters parameters) {
- return testBuilder ->
- enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, false);
+ return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, true);
}
public static ThrowableConsumer<D8TestBuilder>
enableStartupInstrumentationForOptimizedAppUsingLogcat(TestParameters parameters) {
- return testBuilder ->
- enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, true);
+ return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, true);
}
private static void enableStartupInstrumentation(
- D8TestBuilder testBuilder, TestParameters parameters, AppVariant appVariant, boolean logcat)
- throws IOException {
+ D8TestBuilder testBuilder, TestParameters parameters, boolean logcat) throws IOException {
testBuilder
.addOptionsModification(
options -> {
StartupInstrumentationOptions startupInstrumentationOptions =
- options
- .getStartupInstrumentationOptions()
- .setEnableStartupInstrumentation()
- .setEnableGeneralizationOfSyntheticsToSyntheticContext(
- appVariant.isOriginal());
+ options.getStartupInstrumentationOptions().setEnableStartupInstrumentation();
if (logcat) {
startupInstrumentationOptions.setStartupInstrumentationTag(
startupInstrumentationTag);
@@ -108,94 +136,93 @@
}
public static void readStartupListFromFile(
- Path path, Consumer<StartupItem<ClassReference, MethodReference, ?>> startupItemConsumer)
+ Path path,
+ Consumer<ExternalStartupItem> startupItemConsumer,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization)
throws IOException {
- StartupConfigurationParser.createReferenceParser()
- .parseLines(
- Files.readAllLines(path),
- startupItemConsumer,
- startupItemConsumer,
- error -> fail("Unexpected parse error: " + error));
+ TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
+ HumanReadableArtProfileParser parser =
+ HumanReadableArtProfileParser.builder()
+ .setReporter(new Reporter(diagnostics))
+ .setProfileBuilder(
+ createStartupItemFactory(
+ startupItemConsumer, syntheticToSyntheticContextGeneralization))
+ .build();
+ parser.parse(new UTF8TextInputStream(path), Origin.unknown());
+ diagnostics.assertNoMessages();
}
public static ThrowingConsumer<D8TestRunResult, RuntimeException> removeStartupListFromStdout(
- Consumer<StartupItem<ClassReference, MethodReference, ?>> startupItemConsumer) {
- return runResult -> removeStartupListFromStdout(runResult, startupItemConsumer);
+ Consumer<ExternalStartupItem> startupItemConsumer,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ return runResult ->
+ removeStartupListFromStdout(
+ runResult, startupItemConsumer, syntheticToSyntheticContextGeneralization);
}
public static void removeStartupListFromStdout(
D8TestRunResult runResult,
- Consumer<StartupItem<ClassReference, MethodReference, ?>> startupItemConsumer) {
- StartupConfigurationParser<ClassReference, MethodReference, TypeReference> parser =
- StartupConfigurationParser.createReferenceParser();
+ Consumer<ExternalStartupItem> startupItemConsumer,
+ SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
+ TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
+ HumanReadableArtProfileParser parser =
+ HumanReadableArtProfileParser.builder()
+ .setReporter(new Reporter(diagnostics))
+ .setProfileBuilder(
+ createStartupItemFactory(
+ startupItemConsumer, syntheticToSyntheticContextGeneralization))
+ .build();
StringBuilder stdoutBuilder = new StringBuilder();
String startupDescriptorPrefix = "[" + startupInstrumentationTag + "] ";
for (String line : StringUtils.splitLines(runResult.getStdOut(), true)) {
if (line.startsWith(startupDescriptorPrefix)) {
String message = line.substring(startupDescriptorPrefix.length());
- parser.parseLine(
- message,
- startupItemConsumer,
- startupItemConsumer,
- error -> fail("Unexpected parse error: " + error));
+ assertTrue(parser.parseRule(message));
} else {
stdoutBuilder.append(line).append(System.lineSeparator());
}
}
+ diagnostics.assertNoMessages();
runResult.getResult().setStdout(stdoutBuilder.toString());
}
public static void setStartupConfiguration(
TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
- List<StartupItem<ClassReference, MethodReference, ?>> startupItems) {
- testBuilder.addOptionsModification(
- options -> {
- DexItemFactory dexItemFactory = options.dexItemFactory();
- StartupProfile startupProfile =
- StartupProfile.builder()
- .apply(
- builder ->
- startupItems.forEach(
- startupItem ->
- builder.addStartupItem(
- convertStartupItemToDex(startupItem, dexItemFactory))))
- .build();
- StartupProfileProvider startupProfileProvider =
- new StartupProfileProvider() {
- @Override
- public String get() {
- return startupProfile.serializeToString();
- }
+ Collection<ExternalStartupItem> startupItems) {
+ StartupProfileProvider startupProfileProvider =
+ new StartupProfileProvider() {
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ for (ExternalStartupItem startupItem : startupItems) {
+ startupItem.apply(
+ startupClass ->
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder ->
+ startupClassBuilder.setClassReference(startupClass.getReference())),
+ startupMethod ->
+ startupProfileBuilder.addStartupMethod(
+ startupMethodBuilder ->
+ startupMethodBuilder.setMethodReference(
+ startupMethod.getReference())),
+ syntheticStartupMethod ->
+ startupProfileBuilder.addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ syntheticStartupMethod.getSyntheticContextReference())));
+ }
+ }
- @Override
- public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
- throw new Unimplemented();
- }
-
- @Override
- public Origin getOrigin() {
- return Origin.unknown();
- }
- };
- options.getStartupOptions().setStartupProfileProvider(startupProfileProvider);
- });
- }
-
- private static StartupItem<DexType, DexMethod, ?> convertStartupItemToDex(
- StartupItem<ClassReference, MethodReference, ?> startupItem, DexItemFactory dexItemFactory) {
- return StartupItem.dexBuilder()
- .applyIf(
- startupItem.isStartupClass(),
- builder ->
- builder.setClassReference(
- ClassReferenceUtils.toDexType(
- startupItem.asStartupClass().getReference(), dexItemFactory)),
- builder ->
- builder.setMethodReference(
- MethodReferenceUtils.toDexMethod(
- startupItem.asStartupMethod().getReference(), dexItemFactory)))
- .setFlags(startupItem.getFlags())
- .build();
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+ if (testBuilder.isD8TestBuilder()) {
+ testBuilder.asD8TestBuilder().addStartupProfileProviders(startupProfileProvider);
+ } else {
+ assertTrue(testBuilder.isR8TestBuilder());
+ testBuilder.asR8TestBuilder().addStartupProfileProviders(startupProfileProvider);
+ }
}
private static byte[] getTransformedAndroidUtilLog() throws IOException {
diff --git a/third_party/android_jar/lib-v33.tar.gz.sha1 b/third_party/android_jar/lib-v33.tar.gz.sha1
index 2e88eb6..b4d9b45 100644
--- a/third_party/android_jar/lib-v33.tar.gz.sha1
+++ b/third_party/android_jar/lib-v33.tar.gz.sha1
@@ -1 +1 @@
-ecd236f896f9a19eeb7d46eb983cbaf94fd31d76
\ No newline at end of file
+53e8c839d3ec4de175784dddb64731c3a68f9579
\ No newline at end of file
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 040d696..dc12e2c 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 @@
-98dc48c246bd0855133e138c2cd2b5835cf862cf
\ No newline at end of file
+35ed1547b79e22eb4b933b6deb0f929d7ff8aebc
\ No newline at end of file
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 1762c2c..004450b 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -135,6 +135,7 @@
git_utils.GitClone(
'https://github.com/'
+ github_account + '/' + GITHUB_REPRO, checkout_dir)
+ git_utils.GitCheckout('3a970cd008e944845a7b3d29a3b5a13123df11fe', checkout_dir)
def GetJavaEnv():
java_env = dict(os.environ, JAVA_HOME = jdk.GetJdk11Home())
@@ -207,7 +208,7 @@
file.write(hexdigest)
def Undesugar(variant, maven_zip, version, undesugared_maven_zip):
- gradle.RunGradle(['testJar', 'repackageTestDeps'])
+ gradle.RunGradle(['testJar', 'repackageTestDeps', '-Pno_internal'])
with utils.TempDir() as tmp:
with zipfile.ZipFile(maven_zip, 'r') as zip_ref:
zip_ref.extractall(tmp)
@@ -290,8 +291,8 @@
# Upload the jar file for accessing GCS as a maven repro.
maven_destination = archive.GetUploadDestination(
- utils.get_maven_path('desugar_jdk_libs', version),
- 'desugar_jdk_libs-%s.jar' % version,
+ utils.get_maven_path(LIBRARY_NAME_MAP[variant], version),
+ '%s-%s.jar' % (LIBRARY_NAME_MAP[variant], version),
is_main)
if options.dry_run:
print('Dry run, not actually creating maven repo')
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 6baaada..8149313 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -189,12 +189,21 @@
print("Unimplemented: proguard_input configuration.")
def main_dex_list_resource(self):
- if self.if_exists('main-dex-list.txt'):
- print("Unimplemented: main-dex-list.")
+ return self.if_exists('main-dex-list.txt')
def main_dex_rules_resource(self):
return self.if_exists('main-dex-rules.txt')
+ def startup_profile_resources(self):
+ startup_profile_resources = []
+ while True:
+ current_startup_profile_index = len(startup_profile_resources) + 1
+ startup_profile_resource = self.if_exists(
+ 'startup-profile-%s.txt' % current_startup_profile_index)
+ if startup_profile_resource is None:
+ return startup_profile_resources
+ startup_profile_resources.append(startup_profile_resource)
+
def build_properties_file(self):
return self.if_exists('build.properties')
@@ -470,8 +479,12 @@
# -print{mapping,usage}
clean_config(dump.config_file(), args)
cmd.extend(['--pg-conf', dump.config_file()])
+ if dump.main_dex_list_resource():
+ cmd.extend(['--main-dex-list', dump.main_dex_list_resource()])
if dump.main_dex_rules_resource():
cmd.extend(['--main-dex-rules', dump.main_dex_rules_resource()])
+ for startup_profile_resource in dump.startup_profile_resources():
+ cmd.extend(['--startup-profile', startup_profile_resource])
if compiler == 'l8':
if dump.config_file():
cmd.extend(['--pg-map-output', '%s.map' % out])
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index fc7eafb..7d73a93 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -391,13 +391,15 @@
make_archive(destination, 'zip', tmp_dir)
move(destination + '.zip', destination)
-def convert_desugar_configuration(configuration, machine_configuration):
+def convert_desugar_configuration(
+ configuration, conversions, implementation, machine_configuration):
cmd = [jdk.GetJavaExecutable(),
'-cp',
utils.R8_JAR,
'com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter',
configuration,
- utils.DESUGAR_IMPLEMENTATION_JDK11,
+ implementation,
+ conversions,
utils.get_android_jar(33),
machine_configuration]
subprocess.check_call(cmd)
@@ -410,7 +412,7 @@
if (not version.startswith("1.")):
machine_configuration = join(tmp_dir, "machine.json")
- convert_desugar_configuration(configuration, machine_configuration)
+ convert_desugar_configuration(configuration, conversions, implementation, machine_configuration)
configuration = machine_configuration
# Generate the pom file.
diff --git a/tools/desugar_jdk_libs_repository.py b/tools/desugar_jdk_libs_repository.py
index 4417255..7cb7a19 100755
--- a/tools/desugar_jdk_libs_repository.py
+++ b/tools/desugar_jdk_libs_repository.py
@@ -11,8 +11,10 @@
import subprocess
import sys
+import gradle
import utils
import create_maven_release
+import archive_desugar_jdk_libs
class Variant(Enum):
jdk8 = 'jdk8'
@@ -114,6 +116,7 @@
version_file = 'VERSION_JDK11_NIO.txt'
implementation_build_target = ':maven_release_jdk11_nio'
implementation_build_output = join('bazel-bin', 'desugar_jdk_libs_jdk11_nio.zip')
+ gradle.RunGradle([utils.R8])
with utils.TempDir(delete=False) as tmp_dir:
(name, version) = utils.desugar_configuration_name_and_version(configuration, False)
# Checkout desugar_jdk_libs from GitHub
@@ -167,11 +170,24 @@
'--spawn_strategy=local',
'--verbose_failures',
implementation_build_target])
+
+ # Undesugar desugared library if needed.
+ undesugared_if_needed = join(checkout_dir, implementation_build_output)
+ if (args.variant == Variant.jdk11_minimal
+ or args.variant == Variant.jdk11
+ or args.variant == Variant.jdk11_nio):
+ undesugared_if_needed = join(tmp_dir, 'undesugared.zip')
+ archive_desugar_jdk_libs.Undesugar(
+ str(args.variant),
+ join(checkout_dir, implementation_build_output),
+ version,
+ undesugared_if_needed)
+
unzip_dir = join(tmp_dir, 'desugar_jdk_libs_unzipped')
cmd = [
'unzip',
'-q',
- join(checkout_dir, implementation_build_output),
+ undesugared_if_needed,
'-d',
unzip_dir]
subprocess.check_call(cmd)
@@ -204,7 +220,7 @@
print(" changing = true")
print(" }")
print()
- print('If not using the !changing" propertyRemember to run gradle with '
+ print('If not using the "changing" propertyRemember to run gradle with '
+ " --refresh-dependencies (./gradlew --refresh-dependencies ...) "
+ "to ensure the cache is not used when the same version is published."
+ "multiple times.")
diff --git a/tools/git_utils.py b/tools/git_utils.py
index 1ce0a0a..542947a 100644
--- a/tools/git_utils.py
+++ b/tools/git_utils.py
@@ -11,6 +11,12 @@
utils.PrintCmd(cmd)
return subprocess.check_call(cmd)
+def GitCheckout(revision, checkout_dir):
+ with utils.ChangedWorkingDirectory(checkout_dir):
+ cmd = ['git', 'checkout', revision]
+ utils.PrintCmd(cmd)
+ return subprocess.check_call(cmd)
+
def GetHeadRevision(checkout_dir, use_main=False):
revision_from = 'origin/main' if use_main else 'HEAD'
cmd = ['git', 'rev-parse', revision_from]
diff --git a/tools/r8_release.py b/tools/r8_release.py
index f7f0c15..867d312 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -15,7 +15,7 @@
import utils
-R8_DEV_BRANCH = '4.0'
+R8_DEV_BRANCH = '8.0'
R8_VERSION_FILE = os.path.join(
'src', 'main', 'java', 'com', 'android', 'tools', 'r8', 'Version.java')
THIS_FILE_RELATIVE = os.path.join('tools', 'r8_release.py')
@@ -567,12 +567,10 @@
% library_version)
sys.exit(1)
- library_archive = DESUGAR_JDK_LIBS + '.zip'
- library_jar = DESUGAR_JDK_LIBS + '.jar'
- library_artifact_id = \
- '%s:%s:%s' % (ANDROID_TOOLS_PACKAGE, DESUGAR_JDK_LIBS, library_version)
-
postfix = "" if library_version.startswith('1.1') else '_jdk11_legacy'
+ library_archive = DESUGAR_JDK_LIBS + postfix + '.zip'
+ library_jar = DESUGAR_JDK_LIBS + postfix + '.jar'
+
configuration_archive = DESUGAR_JDK_LIBS_CONFIGURATION + postfix + '.zip'
with utils.TempDir() as temp:
@@ -590,9 +588,11 @@
args, [library_gfile, configuration_gfile])
print("Staged Release ID " + release_id + ".\n")
+ library_artifact_id = \
+ '%s:%s:%s' % (ANDROID_TOOLS_PACKAGE, DESUGAR_JDK_LIBS, library_version)
gmaven_publisher_stage_redir_test_info(
release_id,
- "com.android.tools:%s:%s" % (DESUGAR_JDK_LIBS, library_version),
+ library_artifact_id,
library_jar)
print("")
@@ -893,6 +893,10 @@
default=[],
action='append',
help='List of bugs for release version')
+ result.add_argument('--no-bugs',
+ default=False,
+ action='store_true',
+ help='Allow Studio release without specifying any bugs')
result.add_argument('--studio',
metavar=('<path>'),
help='Release for studio by setting the path to a studio '
@@ -938,10 +942,15 @@
metavar=('<path>'),
help='Location for dry run output.')
args = result.parse_args()
+ if (len(args.bug) > 0 and args.no_bugs):
+ print("Use of '--bug' and '--no-bugs' are mutually exclusive")
+ sys.exit(1)
+
if (args.studio
and args.version
and not 'dev' in args.version
- and args.bug == []):
+ and args.bug == []
+ and not args.no_bugs):
print("When releasing a release version to Android Studio add the "
+ "list of bugs by using '--bug'")
sys.exit(1)
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index ee3f27a..d0af556 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -30,7 +30,7 @@
profile_classes_and_methods, iteration, options)
current_startup_descriptors = \
profile_utils.transform_art_profile_to_r8_startup_list(
- profile_classes_and_methods)
+ profile_classes_and_methods, options.generalize_synthetics)
write_tmp_startup_descriptors(current_startup_descriptors, iteration, options)
new_startup_descriptors = add_r8_startup_descriptors(
startup_descriptors, current_startup_descriptors)
@@ -307,6 +307,11 @@
result.add_argument('--device-pin',
help='Device pin code (e.g., 1234)',
action='append')
+ result.add_argument('--generalize-synthetics',
+ help='Whether synthetics should be abstracted into their '
+ 'synthetic contexts',
+ action='store_true',
+ default=False)
result.add_argument('--logcat',
action='store_true',
default=False)
diff --git a/tools/startup/profile_utils.py b/tools/startup/profile_utils.py
index 0e69397..923326f 100755
--- a/tools/startup/profile_utils.py
+++ b/tools/startup/profile_utils.py
@@ -41,11 +41,12 @@
art_profile[descriptor] = flags
return art_profile
-def transform_art_profile_to_r8_startup_list(art_profile):
+def transform_art_profile_to_r8_startup_list(
+ art_profile, generalize_synthetics=False):
r8_startup_list = {}
for startup_descriptor, flags in art_profile.items():
transformed_startup_descriptor = transform_synthetic_descriptor(
- startup_descriptor)
+ startup_descriptor) if generalize_synthetics else startup_descriptor
r8_startup_list[transformed_startup_descriptor] = {
'conditional_startup': False,
'post_startup': flags['post_startup'],
diff --git a/tools/startup/relayout.py b/tools/startup/relayout.py
new file mode 100755
index 0000000..6220336
--- /dev/null
+++ b/tools/startup/relayout.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# 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.
+
+import argparse
+import os
+import subprocess
+import sys
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+import apk_masseur
+import toolhelper
+import utils
+
+def parse_options(argv):
+ result = argparse.ArgumentParser(
+ description='Relayout a given APK using a startup profile.')
+ result.add_argument('--apk',
+ help='Path to the .apk',
+ required=True)
+ result.add_argument('--out',
+ help='Destination of resulting apk',
+ required=True)
+ result.add_argument('--profile',
+ help='Path to the startup profile',
+ required=True)
+ options, args = result.parse_known_args(argv)
+ return options, args
+
+def get_min_api(apk):
+ aapt = os.path.join(utils.getAndroidBuildTools(), 'aapt')
+ cmd = [aapt, 'dump', 'badging', apk]
+ stdout = subprocess.check_output(cmd).decode('utf-8').strip()
+ for line in stdout.splitlines():
+ if line.startswith('sdkVersion:\''):
+ return int(line[len('sdkVersion:\''): -1])
+ raise ValueError('Unexpected stdout: %s' % stdout)
+
+def main(argv):
+ (options, args) = parse_options(argv)
+ with utils.TempDir() as temp:
+ dex = os.path.join(temp, 'dex.zip')
+ d8_args = [
+ '--min-api', str(get_min_api(options.apk)),
+ '--output', dex,
+ '--no-desugaring',
+ '--release',
+ options.apk]
+ extra_args = ['-Dcom.android.tools.r8.startup.profile=%s' % options.profile]
+ toolhelper.run(
+ 'd8',
+ d8_args,
+ extra_args=extra_args,
+ main='com.android.tools.r8.D8')
+ apk_masseur.masseur(options.apk, dex=dex, out=options.out)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/trigger.py b/tools/trigger.py
index d87cad9..d716a55 100755
--- a/tools/trigger.py
+++ b/tools/trigger.py
@@ -23,6 +23,7 @@
TRIGGERS_RE = r'^ triggers: "(\w.*)"'
DESUGAR_JDK11_BOT = 'lib_desugar-archive-jdk11'
+DESUGAR_JDK11_LEGACY_BOT = 'lib_desugar-archive-jdk11-legacy'
DESUGAR_JDK8_BOT = 'lib_desugar-archive-jdk8'
def ParseOptions():
@@ -37,6 +38,9 @@
result.add_option('--desugar-jdk11',
help='Run the jdk11 library desugar and archiving bot.',
default=False, action='store_true')
+ result.add_option('--desugar-jdk11-legacy',
+ help='Run the jdk11 legacy library desugar and archiving bot.',
+ default=False, action='store_true')
result.add_option('--desugar-jdk8',
help='Run the jdk8 library desugar and archiving bot.',
default=False, action='store_true')
@@ -66,6 +70,7 @@
assert 'release' not in builder, builder
main_builders.append(builder)
print('Desugar jdk11 builder:\n ' + DESUGAR_JDK11_BOT)
+ print('Desugar jdk11 legacy builder:\n ' + DESUGAR_JDK11_LEGACY_BOT)
print('Desugar jdk8 builder:\n ' + DESUGAR_JDK8_BOT)
print('Main builders:\n ' + '\n '.join(main_builders))
print('Release builders:\n ' + '\n '.join(release_builders))
@@ -90,7 +95,7 @@
def Main():
(options, args) = ParseOptions()
- desugar = options.desugar_jdk11 or options.desugar_jdk8
+ desugar = options.desugar_jdk11 or options.desugar_jdk11_legacy or options.desugar_jdk8
if len(args) != 1 and not options.cl and not desugar:
print('Takes exactly one argument, the commit to run')
return 1
@@ -111,8 +116,13 @@
assert builder in main_builders or builder in release_builders
builders = [options.builder]
if desugar:
- assert options.desugar_jdk8 or options.desugar_jdk11
- builders = [DESUGAR_JDK8_BOT if options.desugar_jdk8 else DESUGAR_JDK11_BOT]
+ assert options.desugar_jdk11 or options.desugar_jdk11_legacy or options.desugar_jdk8
+ if options.desugar_jdk11:
+ builders = [DESUGAR_JDK11_BOT]
+ elif options.desugar_jdk11_legacy:
+ builders = [DESUGAR_JDK11_LEGACY_BOT]
+ else:
+ builders = [DESUGAR_JDK8_BOT]
commit = git_utils.GetHeadRevision(utils.REPO_ROOT, use_main=True)
if options.cl:
trigger_cl(builders, options.cl)