Merge commit 'a2e203580aa00a36f85cd68d3d584b97aef34d59' into dev-release
diff --git a/.gitignore b/.gitignore
index ae1be52..405e837 100644
--- a/.gitignore
+++ b/.gitignore
@@ -274,8 +274,8 @@
tools/*/art-10.0.0.tar.gz
tools/*/host/art-12.0.0-beta4
tools/*/host/art-12.0.0-beta4.tar.gz
-tools/*/host/art-13-dev
-tools/*/host/art-13-dev.tar.gz
+tools/*/host/art-13.0.0
+tools/*/host/art-13.0.0.tar.gz
tools/*/host/art-master
tools/*/host/art-master.tar.gz
tools/*/art.tar.gz
diff --git a/build.gradle b/build.gradle
index 8f84e7f..90d75ef 100644
--- a/build.gradle
+++ b/build.gradle
@@ -411,7 +411,7 @@
"linux/art-9.0.0",
"linux/art-10.0.0",
"linux/host/art-12.0.0-beta4",
- "linux/host/art-13-dev",
+ "linux/host/art-13.0.0",
"linux/host/art-master",
"linux/dalvik",
"linux/dalvik-4.0.4",
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
index 2b46e36..d412e52 100644
--- a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
@@ -21,6 +21,7 @@
"j$/util/stream/Collector$Characteristics");
private static final Set<String> WRAP_CONVERT_OWNER =
ImmutableSet.of(
+ "j$/util/stream/Stream",
"j$/nio/file/spi/FileSystemProvider",
"j$/nio/file/spi/FileTypeDetector",
"j$/nio/file/Path",
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 0586776..ebe76f2 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -168,7 +168,41 @@
' "builder_group": "internal.client.r8",'
' "recipe": "rex",'
' "test_options": ['
- ' "--variant=jdk11"'
+ ' "--variant=jdk11_minimal",'
+ ' "--variant=jdk11",'
+ ' "--variant=jdk11_nio"'
+ ' ],'
+ ' "test_wrapper": "tools/archive_desugar_jdk_libs.py"'
+ '}'
+ priority: 25
+ execution_timeout_secs: 3600
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ }
+ builders {
+ name: "lib_desugar-archive-jdk11-legacy"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cores:8"
+ dimensions: "cpu:x86-64"
+ dimensions: "os:Ubuntu-16.04"
+ dimensions: "pool:luci.r8.ci"
+ exe {
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
+ cipd_version: "refs/heads/master"
+ cmd: "luciexe"
+ }
+ properties:
+ '{'
+ ' "builder_group": "internal.client.r8",'
+ ' "recipe": "rex",'
+ ' "test_options": ['
+ ' "--variant=jdk11_legacy"'
' ],'
' "test_wrapper": "tools/archive_desugar_jdk_libs.py"'
'}'
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index 9aaa005..c550e41 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -126,6 +126,11 @@
short_name: "jdk11"
}
builders {
+ name: "buildbucket/luci.r8.ci/lib_desugar-archive-jdk11-legacy"
+ category: "library_desugar"
+ short_name: "legacy"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/lib_desugar-archive-jdk8"
category: "library_desugar"
short_name: "jdk8"
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index 10dd059..b775cde 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -78,6 +78,21 @@
}
}
job {
+ id: "lib_desugar-archive-jdk11-legacy"
+ realm: "ci"
+ acl_sets: "ci"
+ triggering_policy {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 3
+ max_batch_size: 1
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "ci"
+ builder: "lib_desugar-archive-jdk11-legacy"
+ }
+}
+job {
id: "lib_desugar-archive-jdk8"
realm: "ci"
acl_sets: "ci"
diff --git a/infra/config/global/generated/project.cfg b/infra/config/global/generated/project.cfg
index 53f90a7..bb18107 100644
--- a/infra/config/global/generated/project.cfg
+++ b/infra/config/global/generated/project.cfg
@@ -7,7 +7,7 @@
name: "r8"
access: "group:all"
lucicfg {
- version: "1.30.11"
+ version: "1.32.1"
package_dir: ".."
config_dir: "generated"
entry_point: "main.star"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index f93a678..73e0802 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -240,14 +240,24 @@
dimensions = dimensions, category = category, release_trigger=release_trigger)
def archivers():
- for name in ["archive", "archive_release", "lib_desugar-archive-jdk11", "lib_desugar-archive-jdk8"]:
+ for name in [
+ "archive",
+ "archive_release",
+ "lib_desugar-archive-jdk11",
+ "lib_desugar-archive-jdk11-legacy",
+ "lib_desugar-archive-jdk8"]:
desugar = "desugar" in name
properties = {
"test_wrapper" : "tools/archive_desugar_jdk_libs.py" if desugar else "tools/archive.py",
"builder_group" : "internal.client.r8"
}
if desugar:
- properties["test_options"] = ["--variant=jdk11" if "jdk11" in name else "--variant=jdk8"]
+ if name.endswith("jdk11"):
+ properties["test_options"] = ["--variant=jdk11_minimal", "--variant=jdk11", "--variant=jdk11_nio"]
+ elif name.endswith("jdk11-legacy"):
+ properties["test_options"] = ["--variant=jdk11_legacy"]
+ else:
+ properties["test_options"] = ["--variant=jdk8"]
r8_builder(
name,
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index 1b2e6bf..6f57d4c 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.1.7",
+ "version": "1.1.8",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -10,7 +10,8 @@
{
"api_level_below_or_equal": 25,
"wrapper_conversion": [
- "java.time.Clock"
+ "java.time.Clock",
+ "java.time.temporal.ChronoUnit"
]
},
{
diff --git a/src/library_desugar/java/j$/util/stream/Stream.java b/src/library_desugar/java/j$/util/stream/Stream.java
new file mode 100644
index 0000000..70c761a
--- /dev/null
+++ b/src/library_desugar/java/j$/util/stream/Stream.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 j$.util.stream;
+
+public class Stream<T> {
+
+ public static java.util.stream.Stream<?> inverted_wrap_convert(j$.util.stream.Stream<?> stream) {
+ return null;
+ }
+
+ public static j$.util.stream.Stream<?> inverted_wrap_convert(java.util.stream.Stream<?> stream) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/java/util/stream/StackWalkerApiFlips.java b/src/library_desugar/java/java/util/stream/StackWalkerApiFlips.java
new file mode 100644
index 0000000..300c5ad
--- /dev/null
+++ b/src/library_desugar/java/java/util/stream/StackWalkerApiFlips.java
@@ -0,0 +1,44 @@
+// 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 java.util.stream;
+
+import static java.util.ConversionRuntimeException.exception;
+
+import java.util.function.Function;
+
+public class StackWalkerApiFlips {
+
+ public static Function<?, ?> flipFunctionStream(Function<?, ?> stackWalker) {
+ return new FunctionStreamWrapper<>(stackWalker);
+ }
+
+ public static class FunctionStreamWrapper<T, R> implements Function<T, R> {
+
+ public Function<T, R> function;
+
+ public FunctionStreamWrapper(Function<T, R> function) {
+ this.function = function;
+ }
+
+ private T flipStream(T maybeStream) {
+ if (maybeStream == null) {
+ return null;
+ }
+ if (maybeStream instanceof java.util.stream.Stream<?>) {
+ return (T)
+ j$.util.stream.Stream.inverted_wrap_convert((java.util.stream.Stream<?>) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.Stream<?>) {
+ return (T)
+ j$.util.stream.Stream.inverted_wrap_convert((j$.util.stream.Stream<?>) maybeStream);
+ }
+ throw exception("java.util.stream.Stream", maybeStream.getClass());
+ }
+
+ public R apply(T arg) {
+ return function.apply(flipStream(arg));
+ }
+ }
+}
diff --git a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
index b1ebcd8..6c0dd3d 100644
--- a/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/chm_only_desugar_jdk_libs.json
@@ -20,6 +20,11 @@
"rewrite_prefix": {
"java.util.concurrent.Helpers": "j$.util.concurrent.Helpers",
"sun.misc.Desugar": "j$.sun.misc.Desugar"
+ },
+ "rewrite_derived_prefix": {
+ "sun.misc.DesugarUnsafe": {
+ "jdk.internal.misc.Unsafe": "j$.sun.misc.DesugarUnsafe"
+ }
}
}
],
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index e3caa6e..103035f 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -1,5 +1,5 @@
{
- "identifier": "com.tools.android:desugar_jdk_libs:2.0.0",
+ "identifier": "com.tools.android:desugar_jdk_libs_configuration:2.0.0",
"configuration_format_version": 100,
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
@@ -20,7 +20,8 @@
"java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
},
"wrapper_conversion": [
- "java.time.Clock"
+ "java.time.Clock",
+ "java.time.temporal.ChronoUnit"
],
"custom_conversion": {
"java.time.Duration": "java.time.TimeConversions",
@@ -98,8 +99,12 @@
"java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
},
"api_generic_types_conversion": {
- "java.util.Set java.util.stream.Collector#characteristics()" : [-1, "java.util.Set java.util.stream.StreamApiFlips#flipCharacteristicSet(java.util.Set)"]
+ "java.util.Set java.util.stream.Collector#characteristics()" : [-1, "java.util.Set java.util.stream.StreamApiFlips#flipCharacteristicSet(java.util.Set)"],
+ "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.StackWalkerApiFlips#flipFunctionStream(java.util.function.Function)"]
},
+ "never_outline_api": [
+ "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)"
+ ],
"wrapper_conversion": [
"java.util.PrimitiveIterator$OfDouble",
"java.util.PrimitiveIterator$OfInt",
@@ -179,6 +184,9 @@
"rewrite_derived_prefix": {
"java.time.": {
"j$.time.": "java.time."
+ },
+ "sun.misc.DesugarUnsafe": {
+ "jdk.internal.misc.Unsafe": "j$.sun.misc.DesugarUnsafe"
}
},
"backport": {
@@ -225,6 +233,9 @@
},
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
+ },
+ "java.util.stream.Stream": {
+ "j$.util.stream.Stream": "java.util.stream.Stream"
}
}
}
@@ -246,4 +257,4 @@
"-dontwarn sun.misc.Unsafe",
"-dontwarn wrapper.**"
]
-}
\ No newline at end of file
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
index a4ddd2ba..7d17a1d 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
@@ -2,7 +2,7 @@
"configuration_format_version": 5,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.2.1",
+ "version": "1.2.2",
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -19,7 +19,8 @@
"java.util.GregorianCalendar#toZonedDateTime": "java.util.DesugarGregorianCalendar"
},
"wrapper_conversion": [
- "java.time.Clock"
+ "java.time.Clock",
+ "java.time.temporal.ChronoUnit"
],
"custom_conversion": {
"java.time.Duration": "java.time.TimeConversions",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
index 8064ed0..d13b947 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
@@ -1,5 +1,5 @@
{
- "identifier": "com.tools.android:desugar_jdk_libs_minimal:2.0.0",
+ "identifier": "com.tools.android:desugar_jdk_libs_configuration_minimal:2.0.0",
"configuration_format_version": 100,
"required_compilation_api_level": 24,
"synthesized_library_classes_package_prefix": "j$.",
@@ -22,4 +22,4 @@
"-keepattributes EnclosingMethod",
"-keepattributes InnerClasses"
]
-}
\ No newline at end of file
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
similarity index 95%
rename from src/library_desugar/jdk11/desugar_jdk_libs_path.json
rename to src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index 4128a53..a53fd3b 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -1,5 +1,5 @@
{
- "identifier": "com.tools.android:desugar_jdk_libs:2.0.0",
+ "identifier": "com.tools.android:desugar_jdk_libs_configuration_nio:2.0.0",
"configuration_format_version": 100,
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
@@ -34,7 +34,8 @@
"java.time.ZonedDateTime java.util.GregorianCalendar#toZonedDateTime()": "java.util.DesugarGregorianCalendar"
},
"wrapper_conversion": [
- "java.time.Clock"
+ "java.time.Clock",
+ "java.time.temporal.ChronoUnit"
],
"custom_conversion": {
"java.time.Duration": "java.time.TimeConversions",
@@ -235,8 +236,12 @@
"java.util.Spliterator java.util.LinkedHashSet#spliterator()": "java.util.DesugarLinkedHashSet"
},
"api_generic_types_conversion": {
- "java.util.Set java.util.stream.Collector#characteristics()" : [-1, "java.util.Set java.util.stream.StreamApiFlips#flipCharacteristicSet(java.util.Set)"]
+ "java.util.Set java.util.stream.Collector#characteristics()" : [-1, "java.util.Set java.util.stream.StreamApiFlips#flipCharacteristicSet(java.util.Set)"],
+ "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.StackWalkerApiFlips#flipFunctionStream(java.util.function.Function)"]
},
+ "never_outline_api": [
+ "java.lang.Object java.lang.StackWalker#walk(java.util.function.Function)"
+ ],
"wrapper_conversion": [
"java.nio.channels.SeekableByteChannel",
"java.util.PrimitiveIterator$OfDouble",
@@ -280,7 +285,6 @@
{
"api_level_below_or_equal": 18,
"rewrite_prefix": {
- "java.lang.DesugarCharacter": "j$.lang.DesugarCharacter",
"java.nio.charset.StandardCharsets": "j$.nio.charset.StandardCharsets"
},
"retarget_method": {
@@ -332,6 +336,12 @@
"dont_retarget": [
"android.support.multidex.MultiDexExtractor$ExtractedDex"
]
+ },
+ {
+ "api_level_below_or_equal": 18,
+ "rewrite_prefix": {
+ "java.lang.DesugarCharacter": "j$.lang.DesugarCharacter"
+ }
}
],
"library_flags": [
@@ -341,7 +351,6 @@
"desugar.": "j$.desugar.",
"libcore.": "j$.libcore.",
"java.lang.Desugar": "j$.lang.Desugar",
- "java.lang.ref.Cleaner": "j$.lang.ref.Cleaner",
"sun.security.action.": "j$.sun.security.action."
}
},
@@ -369,6 +378,15 @@
"java.util.ConversionRuntimeException": "j$.util.ConversionRuntimeException"
},
"rewrite_derived_prefix": {
+ "desugar.sun.nio.fs.DesugarDefaultFileSystemProvider": {
+ "sun.nio.fs.DefaultFileSystemProvider": "j$.adapter.HybridFileSystemProvider"
+ },
+ "desugar.sun.nio.fs.DesugarDefaultFileTypeDetector": {
+ "sun.nio.fs.DefaultFileTypeDetector": "j$.adapter.HybridFileTypeDetector"
+ },
+ "sun.misc.DesugarUnsafe": {
+ "jdk.internal.misc.Unsafe" : "j$.sun.misc.DesugarUnsafe"
+ },
"java.nio.file.attribute.": {
"j$.nio.file.attribute.": "java.nio.file.attribute."
},
@@ -435,6 +453,9 @@
},
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
+ },
+ "java.util.stream.Stream": {
+ "j$.util.stream.Stream": "java.util.stream.Stream"
}
}
},
@@ -464,4 +485,4 @@
"-dontwarn sun.misc.Unsafe",
"-dontwarn wrapper.**"
]
-}
\ No newline at end of file
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio_alternative_3.json
similarity index 100%
rename from src/library_desugar/jdk11/desugar_jdk_libs_path_alternative_3.json
rename to src/library_desugar/jdk11/desugar_jdk_libs_nio_alternative_3.json
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 08c7a84..e8e5a04 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DumpInputFlags;
@@ -21,6 +22,7 @@
import com.android.tools.r8.utils.ThreadUtils;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -55,6 +57,7 @@
private final MapIdProvider mapIdProvider;
private final SourceFileProvider sourceFileProvider;
private final boolean isAndroidPlatformBuild;
+ private final List<StartupProfileProvider> startupProfileProviders;
BaseCompilerCommand(boolean printHelp, boolean printVersion) {
super(printHelp, printVersion);
@@ -74,6 +77,7 @@
mapIdProvider = null;
sourceFileProvider = null;
isAndroidPlatformBuild = false;
+ startupProfileProviders = null;
}
BaseCompilerCommand(
@@ -93,7 +97,8 @@
DumpInputFlags dumpInputFlags,
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
- boolean isAndroidPlatformBuild) {
+ boolean isAndroidPlatformBuild,
+ List<StartupProfileProvider> startupProfileProviders) {
super(app);
assert minApiLevel > 0;
assert mode != null;
@@ -113,6 +118,7 @@
this.mapIdProvider = mapIdProvider;
this.sourceFileProvider = sourceFileProvider;
this.isAndroidPlatformBuild = isAndroidPlatformBuild;
+ this.startupProfileProviders = startupProfileProviders;
}
/**
@@ -208,6 +214,10 @@
return isAndroidPlatformBuild;
}
+ List<StartupProfileProvider> getStartupProfileProviders() {
+ return startupProfileProviders;
+ }
+
DumpInputFlags getDumpInputFlags() {
return dumpInputFlags;
}
@@ -249,6 +259,7 @@
private MapIdProvider mapIdProvider = null;
private SourceFileProvider sourceFileProvider = null;
private boolean isAndroidPlatformBuild = false;
+ private List<StartupProfileProvider> startupProfileProviders = new ArrayList<>();
abstract CompilationMode defaultCompilationMode();
@@ -664,6 +675,19 @@
return isAndroidPlatformBuild;
}
+ B addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+ return addStartupProfileProviders(Arrays.asList(startupProfileProviders));
+ }
+
+ B addStartupProfileProviders(Collection<StartupProfileProvider> startupProfileProviders) {
+ this.startupProfileProviders.addAll(startupProfileProviders);
+ return self();
+ }
+
+ List<StartupProfileProvider> getStartupProfileProviders() {
+ return startupProfileProviders;
+ }
+
/**
* Allow to skip to dump into file and dump into directory instruction, this is primarily used
* for chained compilation in L8 so there are no duplicated dumps.
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 76c2966..172bfa2 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationSource;
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
@@ -264,6 +265,35 @@
return self();
}
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
@Override
Builder self() {
return this;
@@ -426,6 +456,7 @@
proguardMapConsumer,
enableMissingLibraryApiModeling,
getAndroidPlatformBuild(),
+ getStartupProfileProviders(),
factory);
}
}
@@ -516,6 +547,7 @@
StringConsumer proguardMapConsumer,
boolean enableMissingLibraryApiModeling,
boolean isAndroidPlatformBuild,
+ List<StartupProfileProvider> startupProfileProviders,
DexItemFactory factory) {
super(
inputApp,
@@ -534,7 +566,8 @@
dumpInputFlags,
mapIdProvider,
null,
- isAndroidPlatformBuild);
+ isAndroidPlatformBuild,
+ startupProfileProviders);
this.intermediate = intermediate;
this.globalSyntheticsConsumer = globalSyntheticsConsumer;
this.desugarGraphConsumer = desugarGraphConsumer;
@@ -615,7 +648,10 @@
internal.dexClassChecksumFilter = getDexClassChecksumFilter();
internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
- internal.synthesizedClassPrefix = synthesizedClassPrefix;
+ internal.synthesizedClassPrefix =
+ synthesizedClassPrefix.isEmpty()
+ ? System.getProperty("com.android.tools.r8.synthesizedClassPrefix", "")
+ : synthesizedClassPrefix;
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
if (!enableMissingLibraryApiModeling) {
@@ -654,6 +690,11 @@
internal.configureAndroidPlatformBuild(getAndroidPlatformBuild());
+ // TODO(b/238173796): Change StartupOptions to store a Collection<StartupProfileProvider>.
+ if (getStartupProfileProviders().size() == 1) {
+ internal.getStartupOptions().setStartupProfileProvider(getStartupProfileProviders().get(0));
+ }
+
internal.setDumpInputFlags(getDumpInputFlags());
internal.dumpOptions = dumpOptions();
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index b9fe11b..4069ece 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -117,7 +117,8 @@
dumpInputFlags,
mapIdProvider,
null,
- false);
+ false,
+ null);
this.d8Command = d8Command;
this.r8Command = r8Command;
this.desugaredLibrarySpecification = desugaredLibrarySpecification;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 9818440..6adecce 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.shaking.ProguardConfigurationSourceBytes;
import com.android.tools.r8.shaking.ProguardConfigurationSourceFile;
import com.android.tools.r8.shaking.ProguardConfigurationSourceStrings;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
@@ -447,6 +448,37 @@
return super.createProgramOutputConsumer(path, mode, consumeDataResources);
}
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex. The given startup profiles are also used to disallow optimizations
+ * across the startup and post-startup boundary.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ /**
+ * Add a collection of startup profile providers that should be used for distributing the
+ * program classes in dex. The given startup profiles are also used to disallow optimizations
+ * across the startup and post-startup boundary.
+ *
+ * <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
+ * not support native multidex (API<=20).
+ *
+ * <p>NOTE: This API is experimental and may be subject to changes.
+ */
+ @Override
+ public Builder addStartupProfileProviders(
+ Collection<StartupProfileProvider> startupProfileProviders) {
+ return super.addStartupProfileProviders(startupProfileProviders);
+ }
+
@Override
void validate() {
if (isPrintHelp()) {
@@ -628,7 +660,8 @@
getMapIdProvider(),
getSourceFileProvider(),
enableMissingLibraryApiModeling,
- getAndroidPlatformBuild());
+ getAndroidPlatformBuild(),
+ getStartupProfileProviders());
if (inputDependencyGraphConsumer != null) {
inputDependencyGraphConsumer.finished();
@@ -814,7 +847,8 @@
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
boolean enableMissingLibraryApiModeling,
- boolean isAndroidPlatformBuild) {
+ boolean isAndroidPlatformBuild,
+ List<StartupProfileProvider> startupProfileProviders) {
super(
inputApp,
mode,
@@ -832,7 +866,8 @@
dumpInputFlags,
mapIdProvider,
sourceFileProvider,
- isAndroidPlatformBuild);
+ isAndroidPlatformBuild,
+ startupProfileProviders);
assert proguardConfiguration != null;
assert mainDexKeepRules != null;
this.mainDexKeepRules = mainDexKeepRules;
@@ -1011,9 +1046,12 @@
internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
- internal.synthesizedClassPrefix = synthesizedClassPrefix;
- // TODO(b/214382176): Enable all the time.
+ internal.synthesizedClassPrefix =
+ synthesizedClassPrefix.isEmpty()
+ ? System.getProperty("com.android.tools.r8.synthesizedClassPrefix", "")
+ : synthesizedClassPrefix;
boolean l8Shrinking = !synthesizedClassPrefix.isEmpty();
+ // TODO(b/214382176): Enable all the time.
internal.loadAllClassDefinitions = l8Shrinking;
if (l8Shrinking) {
internal.apiModelingOptions().disableSubbingOfClasses();
@@ -1029,6 +1067,11 @@
internal.configureAndroidPlatformBuild(getAndroidPlatformBuild());
+ // TODO(b/238173796): Change StartupOptions to store a Collection<StartupProfileProvider>.
+ if (getStartupProfileProviders().size() == 1) {
+ internal.getStartupOptions().setStartupProfileProvider(getStartupProfileProviders().get(0));
+ }
+
if (!DETERMINISTIC_DEBUGGING) {
assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
internal.threadCount = getThreadCount();
diff --git a/src/main/java/com/android/tools/r8/StartupProfileProvider.java b/src/main/java/com/android/tools/r8/StartupProfileProvider.java
deleted file mode 100644
index fc5e4b1..0000000
--- a/src/main/java/com/android/tools/r8/StartupProfileProvider.java
+++ /dev/null
@@ -1,13 +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;
-
-/** Interface for providing a startup profile to the compiler. */
-@FunctionalInterface
-public interface StartupProfileProvider {
-
- /** Return the startup profile. */
- String get();
-}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index a64b883..7db4fbd 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -276,7 +276,8 @@
content,
options.reporter,
false,
- options.testing.enableExperimentalMapFileVersion));
+ options.testing.enableExperimentalMapFileVersion,
+ false));
} catch (IOException | ResourceException e) {
throw new CompilationError("Failure to read proguard map file", e, map.getOrigin());
}
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 7d1b4dc..86df003 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -6,8 +6,11 @@
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
-import com.android.tools.r8.StartupProfileProvider;
import com.android.tools.r8.StringResource;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.SystemPropertyUtils;
import java.nio.file.Paths;
@@ -53,7 +56,23 @@
SystemPropertyUtils.applySystemProperty(
"com.android.tools.r8.startup.profile",
propertyValue ->
- StringResource.fromFile(Paths.get(propertyValue))::getStringWithRuntimeException,
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return StringResource.fromFile(Paths.get(propertyValue))
+ .getStringWithRuntimeException();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ },
() -> null);
public boolean isMinimalStartupDexEnabled() {
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
index 20cd31b..6b6076f 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfile.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.experimental.startup;
-import com.android.tools.r8.StartupProfileProvider;
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;
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 68af78a..fd710bb 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
@@ -327,11 +326,13 @@
// Static helpers to avoid verbose predicates.
- private static ClassToFeatureSplitMap getMap(AppView<AppInfoWithLiveness> appView) {
+ private static ClassToFeatureSplitMap getMap(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
return appView.appInfo().getClassToFeatureSplitMap();
}
- public static boolean isInFeature(DexProgramClass clazz, AppView<AppInfoWithLiveness> appView) {
+ public static boolean isInFeature(
+ DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
return getMap(appView)
.isInFeature(
clazz,
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
new file mode 100644
index 0000000..451ff92
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
@@ -0,0 +1,75 @@
+// 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.features;
+
+import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMember;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class FeatureSplitBoundaryOptimizationUtils {
+
+ public static ConstraintWithTarget getInliningConstraintForResolvedMember(
+ ProgramMethod method,
+ DexEncodedMember<?, ?> resolvedMember,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ // We never inline into the base from a feature (calls should never happen) and we never inline
+ // between features, so this check should be sufficient.
+ if (classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ resolvedMember.getHolderType(), method, appView)) {
+ return ConstraintWithTarget.ALWAYS;
+ }
+ return ConstraintWithTarget.NEVER;
+ }
+
+ public static FeatureSplit getMergeKeyForHorizontalClassMerging(
+ DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ return classToFeatureSplitMap.getFeatureSplit(clazz, appView);
+ }
+
+ public static boolean isSafeForAccess(
+ DexProgramClass accessedClass,
+ ProgramDefinition accessor,
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ accessedClass, accessor, options, startupOrder, syntheticItems);
+ }
+
+ public static boolean isSafeForInlining(
+ ProgramMethod caller,
+ ProgramMethod callee,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ if (classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(callee, caller, appView)) {
+ return true;
+ }
+ // Still allow inlining if we inline from the base into a feature.
+ if (classToFeatureSplitMap.isInBase(callee.getHolder(), appView)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isSafeForVerticalClassMerging(
+ DexProgramClass sourceClass,
+ DexProgramClass targetClass,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ return classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(
+ sourceClass, targetClass, appView);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 43bbfa2..9211db6 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
@@ -42,9 +43,10 @@
}
if (clazz.isProgramClass()
&& context.isProgramDefinition()
- && !classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ && !FeatureSplitBoundaryOptimizationUtils.isSafeForAccess(
clazz.asProgramClass(),
context.asProgramDefinition(),
+ classToFeatureSplitMap,
options,
startupOrder,
syntheticItems)) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
index 73dfa2a..5ad8747 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
@@ -19,7 +20,8 @@
@Override
public FeatureSplit getMergeKey(DexProgramClass clazz) {
- return appView.appInfo().getClassToFeatureSplitMap().getFeatureSplit(clazz, appView);
+ return FeatureSplitBoundaryOptimizationUtils.getMergeKeyForHorizontalClassMerging(
+ clazz, appView);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Argument.java b/src/main/java/com/android/tools/r8/ir/code/Argument.java
index edd3468..76e9d15 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Argument.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Argument.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
import java.util.Set;
/**
@@ -174,4 +175,9 @@
return this;
}
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ builder.addArgument(index, knownToBeBoolean);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index ca71070..afb3203 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
+import com.android.tools.r8.lightir.LIRBuilder;
import java.io.UTFDataFormatException;
public class ConstString extends ConstInstruction {
@@ -179,4 +180,9 @@
assert getOutType().equals(expectedType);
return true;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ builder.addConstString(value);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index e5bdb6d..1f87da5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
public class DebugPosition extends Instruction {
@@ -98,4 +99,9 @@
public boolean isAllowedAfterThrowingInstruction() {
return true;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ builder.addDebugPosition(getPosition());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 7cb6f50..55d96c5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -33,6 +33,7 @@
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOptions;
@@ -1556,6 +1557,10 @@
return false;
}
+ public void buildLIR(LIRBuilder<Value> builder) {
+ throw new Unimplemented("Missing impl for " + getClass().getSimpleName());
+ }
+
public static class SideEffectAssumption {
public static final SideEffectAssumption NONE = new SideEffectAssumption();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index ff70fed..25d775a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
@@ -215,6 +216,11 @@
return LibraryMethodReadSetModeling.getModeledReadSetOrUnknown(appView, this);
}
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ builder.addInvokeDirect(getInvokedMethod(), arguments());
+ }
+
public static class Builder extends InvokeMethod.Builder<Builder, InvokeDirect> {
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index fed5f58..f44d128 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
@@ -175,4 +176,9 @@
return this;
}
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ builder.addInvokeVirtual(getInvokedMethod(), arguments());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 752b30c..935e9df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
public class Return extends JumpInstruction {
@@ -150,4 +151,13 @@
return this;
}
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ if (hasReturnValue()) {
+ builder.addReturn(returnValue());
+ } else {
+ builder.addReturnVoid();
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index cdec1d9..19b9b27 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Set;
@@ -290,4 +291,9 @@
return this;
}
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value> builder) {
+ builder.addStaticGet(getField());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 33f3f28..c62d786 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
+import com.android.tools.r8.ir.desugar.itf.L8InnerOuterAttributeEraser;
import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -81,6 +82,9 @@
import com.android.tools.r8.ir.optimize.outliner.Outliner;
import com.android.tools.r8.ir.optimize.string.StringBuilderAppendOptimizer;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
+import com.android.tools.r8.lightir.IR2LIRConverter;
+import com.android.tools.r8.lightir.LIR2IRConverter;
+import com.android.tools.r8.lightir.LIRCode;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
@@ -365,6 +369,7 @@
if (appView.options().isDesugaredLibraryCompilation()) {
new EmulatedInterfaceApplicationRewriter(appView).rewriteApplication(builder);
+ new L8InnerOuterAttributeEraser(appView).run();
}
processCovariantReturnTypeAnnotations(builder);
@@ -1611,6 +1616,9 @@
OptimizationFeedback feedback,
BytecodeMetadataProvider bytecodeMetadataProvider,
Timing timing) {
+ if (options.testing.roundtripThroughLIR) {
+ code = roundtripThroughLIR(code, feedback, bytecodeMetadataProvider, timing);
+ }
if (options.isGeneratingClassFiles()) {
finalizeToCf(code, feedback, bytecodeMetadataProvider, timing);
} else {
@@ -1619,6 +1627,16 @@
}
}
+ private IRCode roundtripThroughLIR(
+ IRCode code,
+ OptimizationFeedback feedback,
+ BytecodeMetadataProvider bytecodeMetadataProvider,
+ Timing timing) {
+ LIRCode lirCode = IR2LIRConverter.translate(code);
+ IRCode irCode = LIR2IRConverter.translate(code.context(), lirCode, appView);
+ return irCode;
+ }
+
private void finalizeToCf(
IRCode code,
OptimizationFeedback feedback,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index fcf37fb..6a9fb76 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -201,6 +201,11 @@
}
@Override
+ public void acceptGenericApiConversionStub(DexClasspathClass dexClasspathClass) {
+ // Intentionally empty.
+ }
+
+ @Override
public void acceptAPIConversion(ProgramMethod method) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@@ -378,6 +383,11 @@
}
@Override
+ public void acceptGenericApiConversionStub(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
+
+ @Override
public void acceptAPIConversion(ProgramMethod method) {
// Intentionally empty. The method will be hit by tracing if required.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index e67d55a..f96680f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -123,6 +123,11 @@
public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
// Intentionally empty.
}
+
+ @Override
+ public void acceptGenericApiConversionStub(DexClasspathClass dexClasspathClass) {
+ // Intentionally empty.
+ }
}
public static class R8PostProcessingDesugaringEventConsumer
@@ -199,5 +204,10 @@
public void acceptEnumConversionClasspathClass(DexClasspathClass clazz) {
additions.addLiveClasspathClass(clazz);
}
+
+ @Override
+ public void acceptGenericApiConversionStub(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
index 233374b..de42de1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibrarySpecification.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
import java.util.List;
+import java.util.Set;
public interface DesugaredLibrarySpecification {
@@ -31,6 +32,8 @@
List<String> getExtraKeepRules();
+ Set<String> getMaintainTypeOrPrefixForTesting();
+
AndroidApiLevel getRequiredCompilationApiLevel();
MachineDesugaredLibrarySpecification toMachineSpecification(DexApplication app, Timing timing)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index b4ece95..eeebd32 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -148,6 +148,14 @@
if (isAlreadyDesugared(invoke, context)) {
return false;
}
+ if (appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getApiGenericConversion()
+ .get(invokedMethod.getReference())
+ != null) {
+ return true;
+ }
return appView.typeRewriter.hasRewrittenTypeInSignature(invokedMethod.getProto(), appView);
}
@@ -262,6 +270,15 @@
}
DexClassAndMethod methodForDesugaring = getMethodForDesugaring(invoke, context);
assert methodForDesugaring != null;
+ // Specific apis that we never want to outline, namely, apis for stack introspection since it
+ // confuses developers in debug mode.
+ if (appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getNeverOutlineApi()
+ .contains(methodForDesugaring.getReference())) {
+ return false;
+ }
return methodForDesugaring.getAccessFlags().isPublic();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
index 0e6bd6a..94bd06b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryConversionCfProvider.java
@@ -586,6 +586,11 @@
appView.dexItemFactory().createProto(newReturnType, newParameterTypes),
method.name);
assert convertedAPI == methodWithVivifiedTypeInSignature(method, newHolder, appView)
+ || appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getApiGenericConversion()
+ .containsKey(method)
|| invalidType(method, returnConversion, parameterConversions, appView) != null;
return convertedAPI;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index 2dcf7a4..178255b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexClass;
@@ -31,7 +32,6 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.CustomConversionDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.WrapperDescriptor;
import com.android.tools.r8.ir.synthetic.apiconverter.NullableConversionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.apiconverter.NullableConversionCfCodeProvider.ArrayConversionCfCodeProvider;
import com.android.tools.r8.ir.synthetic.apiconverter.WrapperConstructorCfCodeProvider;
@@ -168,6 +168,28 @@
return false;
}
+ private DexMethod ensureApiGenericConversion(
+ DexMethod conversion, DesugaredLibraryClasspathWrapperSynthesizeEventConsumer eventConsumer) {
+ assert !appView.options().isDesugaredLibraryCompilation();
+ ClasspathMethod classpathMethod =
+ appView
+ .getSyntheticItems()
+ .ensureFixedClasspathMethodFromType(
+ conversion.getName(),
+ conversion.getProto(),
+ kinds -> kinds.GENERIC_API_CONVERSION_STUB,
+ conversion.getHolderType(),
+ appView,
+ ignored -> {},
+ eventConsumer::acceptGenericApiConversionStub,
+ methodBuilder ->
+ methodBuilder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(null));
+ assert classpathMethod.getReference() == conversion;
+ return conversion;
+ }
+
public DexMethod ensureConversionMethod(
DexType type,
boolean destIsVivified,
@@ -176,7 +198,7 @@
Supplier<UniqueContext> contextSupplier) {
if (apiGenericTypesConversion != null) {
assert !type.isArrayType();
- return apiGenericTypesConversion;
+ return ensureApiGenericConversion(apiGenericTypesConversion, eventConsumer);
}
DexType srcType = destIsVivified ? type : vivifiedTypeFor(type);
DexType destType = destIsVivified ? vivifiedTypeFor(type) : type;
@@ -727,22 +749,8 @@
}
private DexClass getWrapperContext(DexClass context, WrapperKind kind) {
- if (kind != WrapperKind.VIVIFIED_WRAPPER) {
- return context;
- }
- WrapperDescriptor descriptor =
- appView.options().machineDesugaredLibrarySpecification.getWrappers().get(context.type);
- assert descriptor != null;
- if (descriptor.hasNonPublicAccess()) {
- return appView
- .getSyntheticItems()
- .ensureFixedClasspathClassFromType(
- kinds -> kinds.VIVIFIED,
- vivifiedTypeFor(context.type),
- appView,
- ignored -> {},
- ignored -> {});
- }
+ // A different context can be specified here, so that the wrapper is prefixed by a different
+ // class than the context.
return context;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java
index 6d91ba2..4adc11f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizerEventConsumer.java
@@ -26,6 +26,8 @@
void acceptWrapperClasspathClass(DexClasspathClass clazz);
void acceptEnumConversionClasspathClass(DexClasspathClass clazz);
+
+ void acceptGenericApiConversionStub(DexClasspathClass dexClasspathClass);
}
interface DesugaredLibraryAPIConverterEventConsumer
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
index 49cc5f6..325cdb0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecification.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Timing;
import java.util.List;
+import java.util.Set;
public class HumanDesugaredLibrarySpecification implements DesugaredLibrarySpecification {
@@ -73,6 +74,11 @@
}
@Override
+ public Set<String> getMaintainTypeOrPrefixForTesting() {
+ return rewritingFlags.getMaintainPrefix();
+ }
+
+ @Override
public List<String> getExtraKeepRules() {
return topLevelFlags.getExtraKeepRules();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
index 0be57c5..16c578b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanDesugaredLibrarySpecificationParser.java
@@ -56,6 +56,7 @@
static final String DONT_REWRITE_PREFIX_KEY = "dont_rewrite_prefix";
static final String MAINTAIN_PREFIX_KEY = "maintain_prefix";
static final String RETARGET_STATIC_FIELD_KEY = "retarget_static_field";
+ static final String NEVER_OUTLINE_API_KEY = "never_outline_api";
static final String COVARIANT_RETARGET_METHOD_KEY = "covariant_retarget_method";
static final String RETARGET_METHOD_KEY = "retarget_method";
static final String RETARGET_METHOD_EMULATED_DISPATCH_KEY =
@@ -264,6 +265,11 @@
builder.putDontRewritePrefix(dontRewritePrefix.getAsString());
}
}
+ if (jsonFlagSet.has(NEVER_OUTLINE_API_KEY)) {
+ for (JsonElement neverOutlineApi : jsonFlagSet.get(NEVER_OUTLINE_API_KEY).getAsJsonArray()) {
+ builder.neverOutlineApi(parseMethod(neverOutlineApi.getAsString()));
+ }
+ }
if (jsonFlagSet.has(API_GENERIC_TYPES_CONVERSION)) {
for (Map.Entry<String, JsonElement> methodAndDescription :
jsonFlagSet.get(API_GENERIC_TYPES_CONVERSION).getAsJsonObject().entrySet()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index a927cdf..8c4316f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -40,6 +40,7 @@
private final Set<DexMethod> dontRewriteInvocation;
private final Set<DexType> dontRetarget;
private final Map<DexType, Set<DexMethod>> wrapperConversions;
+ private final Set<DexMethod> neverOutlineApi;
private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
private final Map<DexField, FieldAccessFlags> amendLibraryField;
@@ -59,6 +60,7 @@
Set<DexMethod> dontRewriteInvocation,
Set<DexType> dontRetarget,
Map<DexType, Set<DexMethod>> wrapperConversion,
+ Set<DexMethod> neverOutlineApi,
Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
Map<DexField, FieldAccessFlags> amendLibraryField) {
this.rewritePrefix = rewritePrefix;
@@ -76,6 +78,7 @@
this.dontRewriteInvocation = dontRewriteInvocation;
this.dontRetarget = dontRetarget;
this.wrapperConversions = wrapperConversion;
+ this.neverOutlineApi = neverOutlineApi;
this.amendLibraryMethod = amendLibraryMethod;
this.amendLibraryField = amendLibraryField;
}
@@ -97,6 +100,7 @@
ImmutableSet.of(),
ImmutableSet.of(),
ImmutableMap.of(),
+ ImmutableSet.of(),
ImmutableMap.of(),
ImmutableMap.of());
}
@@ -124,6 +128,7 @@
dontRewriteInvocation,
dontRetarget,
wrapperConversions,
+ neverOutlineApi,
amendLibraryMethod,
amendLibraryField);
}
@@ -164,6 +169,10 @@
return retargetMethodEmulatedDispatch;
}
+ public Set<DexMethod> getNeverOutlineApi() {
+ return neverOutlineApi;
+ }
+
public Map<DexMethod, DexMethod[]> getApiGenericConversion() {
return apiGenericTypesConversion;
}
@@ -227,6 +236,7 @@
private final Set<DexMethod> dontRewriteInvocation;
private final Set<DexType> dontRetarget;
private final Map<DexType, Set<DexMethod>> wrapperConversions;
+ private final Set<DexMethod> neverOutlineApi;
private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
private final Map<DexField, FieldAccessFlags> amendLibraryField;
@@ -249,6 +259,7 @@
Sets.newIdentityHashSet(),
Sets.newIdentityHashSet(),
new IdentityHashMap<>(),
+ Sets.newIdentityHashSet(),
new IdentityHashMap<>(),
new IdentityHashMap<>());
}
@@ -271,6 +282,7 @@
Set<DexMethod> dontRewriteInvocation,
Set<DexType> dontRetargetLibMember,
Map<DexType, Set<DexMethod>> wrapperConversions,
+ Set<DexMethod> neverOutlineApi,
Map<DexMethod, MethodAccessFlags> amendLibraryMethod,
Map<DexField, FieldAccessFlags> amendLibraryField) {
this.reporter = reporter;
@@ -292,6 +304,8 @@
this.dontRetarget = Sets.newIdentityHashSet();
this.dontRetarget.addAll(dontRetargetLibMember);
this.wrapperConversions = new IdentityHashMap<>(wrapperConversions);
+ this.neverOutlineApi = Sets.newIdentityHashSet();
+ this.neverOutlineApi.addAll(neverOutlineApi);
this.amendLibraryMethod = new IdentityHashMap<>(amendLibraryMethod);
this.amendLibraryField = new IdentityHashMap<>(amendLibraryField);
}
@@ -439,6 +453,11 @@
return this;
}
+ public Builder neverOutlineApi(DexMethod method) {
+ neverOutlineApi.add(method);
+ return this;
+ }
+
public Builder amendLibraryField(DexField member, FieldAccessFlags flags) {
amendLibraryField.put(member, flags);
return this;
@@ -462,6 +481,7 @@
ImmutableSet.copyOf(dontRewriteInvocation),
ImmutableSet.copyOf(dontRetarget),
ImmutableMap.copyOf(wrapperConversions),
+ ImmutableSet.copyOf(neverOutlineApi),
ImmutableMap.copyOf(amendLibraryMethod),
ImmutableMap.copyOf(amendLibraryField));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
index cd236c0..3507445 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/legacyspecification/LegacyDesugaredLibrarySpecification.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -111,6 +112,11 @@
}
@Override
+ public Set<String> getMaintainTypeOrPrefixForTesting() {
+ return ImmutableSet.of();
+ }
+
+ @Override
public String getJsonSource() {
return topLevelFlags.getJsonSource();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
index 3de3ac0..e93c6c6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
@@ -143,7 +143,10 @@
}
private static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
- String jar = String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
+ String jar =
+ apiLevel == AndroidApiLevel.MASTER
+ ? "third_party/android_jar/lib-master/android.jar"
+ : String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
return Paths.get(jar);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index 1b83bf8..dc24bff 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -21,6 +21,7 @@
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
public class MachineDesugaredLibrarySpecification implements DesugaredLibrarySpecification {
@@ -103,6 +104,13 @@
return topLevelFlags.getExtraKeepRules();
}
+ @Override
+ public Set<String> getMaintainTypeOrPrefixForTesting() {
+ return rewritingFlags.getMaintainType().stream()
+ .map(DexType::toString)
+ .collect(Collectors.toSet());
+ }
+
public Map<DexType, DexType> getRewriteType() {
return rewritingFlags.getRewriteType();
}
@@ -180,6 +188,10 @@
return rewritingFlags.getCustomConversions();
}
+ public Set<DexMethod> getNeverOutlineApi() {
+ return rewritingFlags.getNeverOutlineApi();
+ }
+
public Map<DexMethod, MethodAccessFlags> getAmendLibraryMethods() {
return rewritingFlags.getAmendLibraryMethod();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index 523042f..6d3f489 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -42,6 +42,7 @@
Map<DexType, DexType> legacyBackport,
Set<DexType> dontRetarget,
Map<DexType, CustomConversionDescriptor> customConversions,
+ Set<DexMethod> neverOutlineApi,
Map<DexMethod, MethodAccessFlags> amendLibraryMethods,
Map<DexField, FieldAccessFlags> amendLibraryFields) {
this.rewriteType = rewriteType;
@@ -60,6 +61,7 @@
this.legacyBackport = legacyBackport;
this.dontRetarget = dontRetarget;
this.customConversions = customConversions;
+ this.neverOutlineApi = neverOutlineApi;
this.amendLibraryMethod = amendLibraryMethods;
this.amendLibraryField = amendLibraryFields;
}
@@ -106,6 +108,7 @@
private final Map<DexType, DexType> legacyBackport;
private final Set<DexType> dontRetarget;
private final Map<DexType, CustomConversionDescriptor> customConversions;
+ private final Set<DexMethod> neverOutlineApi;
private final Map<DexMethod, MethodAccessFlags> amendLibraryMethod;
private final Map<DexField, FieldAccessFlags> amendLibraryField;
@@ -183,6 +186,10 @@
return customConversions;
}
+ public Set<DexMethod> getNeverOutlineApi() {
+ return neverOutlineApi;
+ }
+
public Map<DexMethod, MethodAccessFlags> getAmendLibraryMethod() {
return amendLibraryMethod;
}
@@ -253,6 +260,7 @@
private final ImmutableSet.Builder<DexType> dontRetarget = ImmutableSet.builder();
private final ImmutableMap.Builder<DexType, CustomConversionDescriptor> customConversions =
ImmutableMap.builder();
+ private final ImmutableSet.Builder<DexMethod> neverOutlineApi = ImmutableSet.builder();
private final ImmutableMap.Builder<DexMethod, MethodAccessFlags> amendLibraryMethod =
ImmutableMap.builder();
private final ImmutableMap.Builder<DexField, FieldAccessFlags> amendLibraryField =
@@ -311,6 +319,10 @@
this.wrappers.put(type, descriptor);
}
+ public void neverOutlineApi(DexMethod method) {
+ neverOutlineApi.add(method);
+ }
+
public void putLegacyBackport(DexType src, DexType target) {
legacyBackport.put(src, target);
}
@@ -370,6 +382,7 @@
legacyBackport.build(),
dontRetarget.build(),
customConversions.build(),
+ neverOutlineApi.build(),
amendLibraryMethod.build(),
amendLibraryField.build());
}
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
new file mode 100644
index 0000000..afddbae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
@@ -0,0 +1,138 @@
+// 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.ir.desugar.desugaredlibrary.specificationconversion;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isHumanSpecification;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.isMachineSpecification;
+
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.StringResource.FileResource;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.MultiAPILevelHumanDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.MultiAPILevelLegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.MultiAPILevelLegacyDesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
+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.ImmutableSet;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+public class DesugaredLibraryConverter {
+
+ 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]);
+ convertMultiLevelAnythingToMachineSpecification(
+ jsonFile, ImmutableSet.of(desugaredLibraryJar), ImmutableSet.of(androidJar), output);
+ }
+
+ public static void convertMultiLevelAnythingToMachineSpecification(
+ Path jsonSpec, Set<Path> desugaredLibraryFiles, Set<Path> libraryFiles, Path output)
+ throws IOException {
+
+ InternalOptions options = new InternalOptions();
+
+ FileResource jsonResource = StringResource.fromFile(jsonSpec);
+ JsonObject jsonConfig = parseJsonConfig(options, jsonResource);
+
+ if (isMachineSpecification(jsonConfig, options.reporter, jsonResource.getOrigin())) {
+ // Nothing to convert;
+ Files.copy(jsonSpec, output);
+ return;
+ }
+
+ DexApplication appForConversion =
+ getAppForConversion(options, libraryFiles, desugaredLibraryFiles);
+ MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
+ getInputAsHumanSpecification(options, jsonResource, jsonConfig, appForConversion);
+ String outputString = convertToMachineSpecification(options, appForConversion, humanSpec);
+
+ Files.write(output, Collections.singleton(outputString));
+ }
+
+ private static String convertToMachineSpecification(
+ InternalOptions options,
+ DexApplication appForConversion,
+ MultiAPILevelHumanDesugaredLibrarySpecification humanSpec)
+ throws IOException {
+ HumanToMachineSpecificationConverter converter =
+ new HumanToMachineSpecificationConverter(Timing.empty());
+ MultiAPILevelMachineDesugaredLibrarySpecification machineSpec =
+ converter.convertAllAPILevels(humanSpec, appForConversion);
+ Box<String> machineJson = new Box<>();
+ MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter.export(
+ machineSpec, (string, handler) -> machineJson.set(string), options.dexItemFactory());
+ return machineJson.get();
+ }
+
+ private static JsonObject parseJsonConfig(InternalOptions options, FileResource jsonResource) {
+ JsonObject jsonConfig;
+ try {
+ String jsonConfigString = jsonResource.getString();
+ JsonParser parser = new JsonParser();
+ jsonConfig = parser.parse(jsonConfigString).getAsJsonObject();
+ } catch (Exception e) {
+ throw options.reporter.fatalError(new ExceptionDiagnostic(e, jsonResource.getOrigin()));
+ }
+ return jsonConfig;
+ }
+
+ /**
+ * Parse the human specification, or parse and convert the legacy specification into human
+ * specification.
+ */
+ private static MultiAPILevelHumanDesugaredLibrarySpecification getInputAsHumanSpecification(
+ InternalOptions options,
+ FileResource jsonResource,
+ JsonObject jsonConfig,
+ DexApplication appForConversion)
+ throws IOException {
+ if (!isHumanSpecification(jsonConfig, options.reporter, jsonResource.getOrigin())) {
+ MultiAPILevelLegacyDesugaredLibrarySpecification legacySpec =
+ new MultiAPILevelLegacyDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(), options.reporter)
+ .parseMultiLevelConfiguration(jsonResource);
+
+ LegacyToHumanSpecificationConverter converter =
+ new LegacyToHumanSpecificationConverter(Timing.empty());
+
+ return converter.convertAllAPILevels(legacySpec, appForConversion);
+ }
+ return new MultiAPILevelHumanDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(), options.reporter)
+ .parseMultiLevelConfiguration(jsonResource);
+ }
+
+ public static DexApplication getAppForConversion(
+ InternalOptions options, Set<Path> androidJar, Set<Path> desugaredlibJar) throws IOException {
+ AndroidApp.Builder builder = AndroidApp.builder();
+ builder.addProgramFiles(desugaredlibJar);
+ AndroidApp inputApp = builder.addLibraryFiles(androidJar).build();
+ ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
+ ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ assert !options.ignoreJavaLibraryOverride;
+ options.ignoreJavaLibraryOverride = true;
+ DexApplication app = applicationReader.read(executorService);
+ options.ignoreJavaLibraryOverride = false;
+ return app;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
index 51262ef..520533b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachinePrefixConverter.java
@@ -55,6 +55,7 @@
rewriteValues(rewritingFlags.getCustomConversions());
rewriteEmulatedInterface(rewritingFlags.getEmulatedInterfaces());
rewriteRetargetKeys(rewritingFlags.getRetargetMethodEmulatedDispatch());
+ rewriteApiConversions(rewritingFlags.getApiGenericConversion());
warnIfUnusedPrefix(warnConsumer);
}
@@ -85,6 +86,17 @@
}
}
+ private void rewriteApiConversions(Map<DexMethod, DexMethod[]> apiGenericConversions) {
+ apiGenericConversions.forEach(
+ (k, v) -> {
+ for (DexMethod dexMethod : v) {
+ if (dexMethod != null) {
+ registerClassType(dexMethod.getHolderType());
+ }
+ }
+ });
+ }
+
private void rewriteEmulatedInterface(Map<DexType, DexType> emulateLibraryInterface) {
emulateLibraryInterface.forEach(builder::rewriteDerivedTypeOnly);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
index eb333a7..2773de6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -129,6 +129,7 @@
rewritingFlags.getAmendLibraryMethod().forEach(builder::amendLibraryMethod);
rewritingFlags.getAmendLibraryField().forEach(builder::amendLibraryField);
rewritingFlags.getApiGenericConversion().forEach(builder::addApiGenericTypesConversion);
+ rewritingFlags.getNeverOutlineApi().forEach(builder::neverOutlineApi);
new HumanToMachineRetargetConverter(appInfo)
.convertRetargetFlags(rewritingFlags, builder, this::warnMissingReferences);
new HumanToMachineEmulatedInterfaceConverter(appInfo)
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/L8InnerOuterAttributeEraser.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/L8InnerOuterAttributeEraser.java
new file mode 100644
index 0000000..4030b3b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/L8InnerOuterAttributeEraser.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.ir.desugar.itf;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import java.util.ArrayList;
+
+/**
+ * If D8/R8 rewrites a type but not its enclosing/inner types, then the package does not match
+ * between the types and the inner/outer class attributes cannot be maintained nor be valid anymore.
+ * This overrides keep rules regarding inner/outer attributes, and should be applied only to the L8
+ * compilation itself.
+ */
+public class L8InnerOuterAttributeEraser {
+
+ private final AppView<?> appView;
+
+ public L8InnerOuterAttributeEraser(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ public void run() {
+ assert appView.options().isDesugaredLibraryCompilation();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ eraseInvalidAttributes(clazz);
+ }
+ }
+
+ private void eraseInvalidAttributes(DexProgramClass clazz) {
+ boolean rewritten = hasRewrittenType(clazz.type);
+
+ if (clazz.getEnclosingMethodAttribute() != null) {
+ DexType outerType = clazz.getEnclosingMethodAttribute().getEnclosingClass();
+ if (outerType != null && hasRewrittenType(outerType) != rewritten) {
+ clazz.clearEnclosingMethodAttribute();
+ }
+ // Erasing enclosing method attributes (method, not class) does not seem to make any
+ // difference at this point. It can be added here if relevant.
+ }
+
+ if (!clazz.getInnerClasses().isEmpty()) {
+ ArrayList<InnerClassAttribute> innerClasses = new ArrayList<>();
+ for (InnerClassAttribute innerClass : clazz.getInnerClasses()) {
+ if (hasRewrittenType(innerClass.getInner()) == rewritten) {
+ innerClasses.add(innerClass);
+ }
+ }
+ if (innerClasses.size() != clazz.getInnerClasses().size()) {
+ clazz.setInnerClasses(innerClasses);
+ }
+ }
+ }
+
+ private boolean hasRewrittenType(DexType type) {
+ return appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getRewriteType()
+ .containsKey(type);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index d3d0ece..ac4c0ef 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.dex.code.DexMoveResultObject;
import com.android.tools.r8.dex.code.DexMoveResultWide;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedField;
@@ -163,13 +163,9 @@
return false;
}
- ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
- if (!classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(singleTarget, method, appView)) {
- // Still allow inlining if we inline from the base into a feature.
- if (!classToFeatureSplitMap.isInBase(singleTarget.getHolder(), appView)) {
- whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
- return false;
- }
+ if (!FeatureSplitBoundaryOptimizationUtils.isSafeForInlining(method, singleTarget, appView)) {
+ whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
+ return false;
}
Set<Reason> validInliningReasons = appView.testing().validInliningReasons;
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 96b79b1..6dc28aa 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
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -382,14 +383,13 @@
// This will fail at runtime.
return ConstraintWithTarget.NEVER;
}
- if (!appView
- .appInfo()
- .getClassToFeatureSplitMap()
- .isInBaseOrSameFeatureAs(
- resolvedMember.getHolderType(), context.asProgramMethod(), appView)) {
- // We never inline into the base from a feature (calls should never happen) and we
- // never inline between features, so this check should be sufficient.
- return ConstraintWithTarget.NEVER;
+ ConstraintWithTarget featureSplitInliningConstraint =
+ FeatureSplitBoundaryOptimizationUtils.getInliningConstraintForResolvedMember(
+ context, resolvedMember, appView);
+ assert featureSplitInliningConstraint == ConstraintWithTarget.ALWAYS
+ || featureSplitInliningConstraint == ConstraintWithTarget.NEVER;
+ if (featureSplitInliningConstraint == ConstraintWithTarget.NEVER) {
+ return featureSplitInliningConstraint;
}
DexType resolvedHolder = graphLens.lookupType(resolvedMember.getHolderType());
assert initialResolutionHolder != null;
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
index a4a4bdf..5c51959 100644
--- a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
+++ b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import it.unimi.dsi.fastutil.bytes.ByteIterator;
+
/** Simple utilities for byte encodings. */
public class ByteUtils {
@@ -36,6 +38,15 @@
writer.put(truncateToU1(value));
}
+ public static int readEncodedInt(ByteIterator iterator) {
+ assert 4 == intEncodingSize(0);
+ int value = ensureU1(iterator.nextByte()) << 24;
+ value |= ensureU1(iterator.nextByte()) << 16;
+ value |= ensureU1(iterator.nextByte()) << 8;
+ value |= ensureU1(iterator.nextByte());
+ return value;
+ }
+
public static boolean isU2(int value) {
return (value >= 0) && (value <= 0xFFFF);
}
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
new file mode 100644
index 0000000..e91fd8a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -0,0 +1,45 @@
+// 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.lightir;
+
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionIterator;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+
+public class IR2LIRConverter {
+
+ private IR2LIRConverter() {}
+
+ public static LIRCode translate(IRCode irCode) {
+ Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
+ int index = 0;
+ for (Instruction instruction : irCode.instructions()) {
+ if (instruction.hasOutValue()) {
+ values.put(instruction.outValue(), index);
+ }
+ index++;
+ }
+ LIRBuilder<Value> builder =
+ new LIRBuilder<Value>(irCode.context().getReference(), values::getInt)
+ .setMetadata(irCode.metadata());
+ BasicBlockIterator blockIt = irCode.listIterator();
+ while (blockIt.hasNext()) {
+ BasicBlock block = blockIt.next();
+ // TODO(b/225838009): Support control flow.
+ assert !block.hasPhis();
+ InstructionIterator it = block.iterator();
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ builder.setCurrentPosition(instruction.getPosition());
+ instruction.buildLIR(builder);
+ }
+ }
+ return builder.build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
new file mode 100644
index 0000000..75a372b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
@@ -0,0 +1,237 @@
+// 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.lightir;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.NumberGenerator;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.lightir.LIRCode.PositionEntry;
+import it.unimi.dsi.fastutil.ints.IntList;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class LIR2IRConverter {
+
+ private LIR2IRConverter() {}
+
+ public static IRCode translate(ProgramMethod method, LIRCode lirCode, AppView<?> appView) {
+ Parser parser = new Parser(lirCode, method.getReference(), appView);
+ parser.parseArguments(method);
+ lirCode.forEach(view -> view.accept(parser));
+ return parser.getIRCode(method);
+ }
+
+ /**
+ * When building IR the structured LIR parser is used to obtain the decoded operand indexes. The
+ * below parser subclass handles translation of indexes to SSA values.
+ */
+ private static class Parser extends LIRParsedInstructionCallback {
+
+ private final AppView<?> appView;
+ private final LIRCode code;
+ private final NumberGenerator valueNumberGenerator = new NumberGenerator();
+ private final NumberGenerator basicBlockNumberGenerator = new NumberGenerator();
+
+ private final Value[] values;
+ private final LinkedList<BasicBlock> blocks = new LinkedList<>();
+
+ private BasicBlock currentBlock = null;
+ private int nextInstructionIndex = 0;
+
+ private Position currentPosition;
+ private PositionEntry nextPositionEntry = null;
+ private int nextIndexInPositionsTable = 0;
+
+ public Parser(LIRCode code, DexMethod method, AppView<?> appView) {
+ super(code);
+ assert code.getPositionTable().length > 0;
+ assert code.getPositionTable()[0].fromInstructionIndex == 0;
+ this.appView = appView;
+ this.code = code;
+ values = new Value[code.getArgumentCount() + code.getInstructionCount()];
+ // Recreate the preamble position. This is active for arguments and code with no positions.
+ currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+ }
+
+ private void ensureCurrentPosition() {
+ if (nextPositionEntry != null
+ && nextPositionEntry.fromInstructionIndex < nextInstructionIndex) {
+ currentPosition = nextPositionEntry.position;
+ advanceNextPositionEntry();
+ }
+ }
+
+ private void advanceNextPositionEntry() {
+ nextPositionEntry =
+ nextIndexInPositionsTable < code.getPositionTable().length
+ ? code.getPositionTable()[nextIndexInPositionsTable++]
+ : null;
+ }
+
+ public void parseArguments(ProgramMethod method) {
+ currentBlock = new BasicBlock();
+ currentBlock.setNumber(basicBlockNumberGenerator.next());
+ boolean hasReceiverArgument = !method.getDefinition().isStatic();
+ assert code.getArgumentCount()
+ == method.getParameters().size() + (hasReceiverArgument ? 1 : 0);
+ if (hasReceiverArgument) {
+ addThisArgument(method.getHolderType());
+ }
+ method.getParameters().forEach(this::addArgument);
+ // Set up position state after adding arguments.
+ advanceNextPositionEntry();
+ }
+
+ public IRCode getIRCode(ProgramMethod method) {
+ // TODO(b/225838009): Support control flow.
+ currentBlock.setFilled();
+ blocks.add(currentBlock);
+ return new IRCode(
+ appView.options(),
+ method,
+ Position.syntheticNone(),
+ blocks,
+ valueNumberGenerator,
+ basicBlockNumberGenerator,
+ code.getMetadata(),
+ method.getOrigin(),
+ new MutableMethodConversionOptions(appView.options()));
+ }
+
+ public Value getSsaValue(int index) {
+ Value value = values[index];
+ if (value == null) {
+ value = new Value(valueNumberGenerator.next(), TypeElement.getBottom(), null);
+ values[index] = value;
+ }
+ return value;
+ }
+
+ public List<Value> getSsaValues(IntList indices) {
+ List<Value> arguments = new ArrayList<>(indices.size());
+ for (int i = 0; i < indices.size(); i++) {
+ arguments.add(getSsaValue(indices.getInt(i)));
+ }
+ return arguments;
+ }
+
+ public int peekNextInstructionIndex() {
+ return nextInstructionIndex;
+ }
+
+ public Value getOutValueForNextInstruction(TypeElement type) {
+ // TODO(b/225838009): Support debug locals.
+ DebugLocalInfo localInfo = null;
+ int index = peekNextInstructionIndex();
+ Value value = values[index];
+ if (value == null) {
+ value = new Value(valueNumberGenerator.next(), type, localInfo);
+ values[index] = value;
+ } else {
+ value.setType(type);
+ if (localInfo != null) {
+ value.setLocalInfo(localInfo);
+ }
+ }
+ return value;
+ }
+
+ private void addInstruction(Instruction instruction) {
+ ensureCurrentPosition();
+ instruction.setPosition(currentPosition);
+ currentBlock.getInstructions().add(instruction);
+ instruction.setBlock(currentBlock);
+ ++nextInstructionIndex;
+ }
+
+ private void addThisArgument(DexType type) {
+ Argument argument = addArgument(type);
+ argument.outValue().markAsThis();
+ }
+
+ private Argument addArgument(DexType type) {
+ Argument instruction =
+ new Argument(
+ getOutValueForNextInstruction(type.toTypeElement(appView)),
+ peekNextInstructionIndex(),
+ type.isBooleanType());
+ addInstruction(instruction);
+ return instruction;
+ }
+
+ @Override
+ public void onConstNull() {
+ Value dest = getOutValueForNextInstruction(TypeElement.getNull());
+ addInstruction(new ConstNumber(dest, 0));
+ }
+
+ @Override
+ public void onConstString(DexString string) {
+ Value dest = getOutValueForNextInstruction(TypeElement.stringClassType(appView));
+ addInstruction(new ConstString(dest, string));
+ }
+
+ @Override
+ public void onInvokeDirect(DexMethod target, IntList arguments) {
+ // TODO(b/225838009): Maintain is-interface bit.
+ Value dest = getInvokeInstructionOutputValue(target);
+ List<Value> ssaArgumentValues = getSsaValues(arguments);
+ InvokeDirect instruction = new InvokeDirect(target, dest, ssaArgumentValues);
+ addInstruction(instruction);
+ }
+
+ @Override
+ public void onInvokeVirtual(DexMethod target, IntList arguments) {
+ // TODO(b/225838009): Maintain is-interface bit.
+ Value dest = getInvokeInstructionOutputValue(target);
+ List<Value> ssaArgumentValues = getSsaValues(arguments);
+ InvokeVirtual instruction = new InvokeVirtual(target, dest, ssaArgumentValues);
+ addInstruction(instruction);
+ }
+
+ private Value getInvokeInstructionOutputValue(DexMethod target) {
+ return target.getReturnType().isVoidType()
+ ? null
+ : getOutValueForNextInstruction(target.getReturnType().toTypeElement(appView));
+ }
+
+ @Override
+ public void onStaticGet(DexField field) {
+ Value dest = getOutValueForNextInstruction(field.getTypeElement(appView));
+ addInstruction(new StaticGet(dest, field));
+ }
+
+ @Override
+ public void onReturnVoid() {
+ addInstruction(new Return());
+ }
+
+ @Override
+ public void onDebugPosition() {
+ addInstruction(new DebugPosition());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBasicInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRBasicInstructionCallback.java
deleted file mode 100644
index a08c036..0000000
--- a/src/main/java/com/android/tools/r8/lightir/LIRBasicInstructionCallback.java
+++ /dev/null
@@ -1,18 +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.lightir;
-
-public interface LIRBasicInstructionCallback {
-
- /**
- * Most basic callback for interpreting LIR.
- *
- * @param opcode The opcode of the instruction (See {@code LIROpcodes} for values).
- * @param operandsOffsetInBytes The offset into the byte stream at which the instruction's payload
- * starts.
- * @param operandsSizeInBytes The total size of the instruction's payload (excluding the opcode
- * itself an any payload size encoding).
- */
- void onInstruction(int opcode, int operandsOffsetInBytes, int operandsSizeInBytes);
-}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
index 7ced9ef..fe9e5b2 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -3,18 +3,64 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.lightir.LIRCode.PositionEntry;
+import com.android.tools.r8.utils.ListUtils;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import java.util.ArrayList;
+import java.util.List;
-public class LIRBuilder {
+/**
+ * Builder for constructing LIR code from IR.
+ *
+ * @param <V> Type of SSA values. This is abstract to ensure that value internals are not used in
+ * building.
+ */
+public class LIRBuilder<V> {
+
+ public interface ValueIndexGetter<V> {
+ int getValueIndex(V value);
+ }
private final ByteArrayWriter byteWriter = new ByteArrayWriter();
private final LIRWriter writer = new LIRWriter(byteWriter);
private final Reference2IntMap<DexItem> constants;
+ private final ValueIndexGetter<V> valueIndexGetter;
+ private final List<PositionEntry> positionTable;
+ private int argumentCount = 0;
+ private int instructionCount = 0;
+ private IRMetadata metadata = null;
- public LIRBuilder() {
+ private Position currentPosition;
+ private Position flushedPosition;
+
+ public LIRBuilder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
constants = new Reference2IntOpenHashMap<>();
+ positionTable = new ArrayList<>();
+ this.valueIndexGetter = valueIndexGetter;
+ currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+ flushedPosition = currentPosition;
+ }
+
+ public LIRBuilder<V> setCurrentPosition(Position position) {
+ assert position != null;
+ assert position != Position.none();
+ currentPosition = position;
+ return this;
+ }
+
+ private void setPositionIndex(int instructionIndex, Position position) {
+ assert positionTable.isEmpty()
+ || ListUtils.last(positionTable).fromInstructionIndex < instructionIndex;
+ positionTable.add(new PositionEntry(instructionIndex, position));
}
private int getConstantIndex(DexItem item) {
@@ -23,17 +69,61 @@
return oldIndex != null ? oldIndex : nextIndex;
}
- public LIRBuilder addNop() {
- writer.writeOneByteInstruction(LIROpcodes.NOP);
+ private int constantIndexSize(DexItem item) {
+ return 4;
+ }
+
+ private void writeConstantIndex(DexItem item) {
+ int index = getConstantIndex(item);
+ ByteUtils.writeEncodedInt(index, writer::writeOperand);
+ }
+
+ private int getValueIndex(V value) {
+ return valueIndexGetter.getValueIndex(value);
+ }
+
+ private int valueIndexSize(int index) {
+ return ByteUtils.intEncodingSize(index);
+ }
+
+ private void writeValueIndex(int index) {
+ ByteUtils.writeEncodedInt(index, writer::writeOperand);
+ }
+
+ public LIRBuilder<V> setMetadata(IRMetadata metadata) {
+ this.metadata = metadata;
return this;
}
- public LIRBuilder addConstNull() {
+ public LIRBuilder<V> writeConstantReferencingInstruction(int opcode, DexItem item) {
+ writer.writeInstruction(opcode, constantIndexSize(item));
+ writeConstantIndex(item);
+ return this;
+ }
+
+ public LIRBuilder<V> addArgument(int index, boolean knownToBeBoolean) {
+ // Arguments are implicitly given by method descriptor and not an actual instruction.
+ assert argumentCount == index;
+ argumentCount++;
+ return this;
+ }
+
+ private void addInstruction() {
+ if (!currentPosition.equals(flushedPosition)) {
+ setPositionIndex(instructionCount, currentPosition);
+ flushedPosition = currentPosition;
+ }
+ ++instructionCount;
+ }
+
+ public LIRBuilder<V> addConstNull() {
+ addInstruction();
writer.writeOneByteInstruction(LIROpcodes.ACONST_NULL);
return this;
}
- public LIRBuilder addConstInt(int value) {
+ public LIRBuilder<V> addConstInt(int value) {
+ addInstruction();
if (0 <= value && value <= 5) {
writer.writeOneByteInstruction(LIROpcodes.ICONST_0 + value);
} else {
@@ -43,10 +133,70 @@
return this;
}
+ public LIRBuilder<V> addConstString(DexString string) {
+ addInstruction();
+ return writeConstantReferencingInstruction(LIROpcodes.LDC, string);
+ }
+
+ public LIRBuilder<V> addStaticGet(DexField field) {
+ addInstruction();
+ return writeConstantReferencingInstruction(LIROpcodes.GETSTATIC, field);
+ }
+
+ public LIRBuilder<V> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
+ addInstruction();
+ int argumentOprandSize = constantIndexSize(method);
+ int[] argumentIndexes = new int[arguments.size()];
+ int i = 0;
+ for (V argument : arguments) {
+ int argumentIndex = getValueIndex(argument);
+ argumentIndexes[i++] = argumentIndex;
+ argumentOprandSize += valueIndexSize(argumentIndex);
+ }
+ writer.writeInstruction(opcode, argumentOprandSize);
+ writeConstantIndex(method);
+ for (int argumentIndex : argumentIndexes) {
+ writeValueIndex(argumentIndex);
+ }
+ return this;
+ }
+
+ public LIRBuilder<V> addInvokeDirect(DexMethod method, List<V> arguments) {
+ return addInvokeInstruction(LIROpcodes.INVOKEDIRECT, method, arguments);
+ }
+
+ public LIRBuilder<V> addInvokeVirtual(DexMethod method, List<V> arguments) {
+ return addInvokeInstruction(LIROpcodes.INVOKEVIRTUAL, method, arguments);
+ }
+
+ public LIRBuilder<V> addReturn(V value) {
+ throw new Unimplemented();
+ }
+
+ public LIRBuilder<V> addReturnVoid() {
+ addInstruction();
+ writer.writeOneByteInstruction(LIROpcodes.RETURN);
+ return this;
+ }
+
+ public LIRBuilder<V> addDebugPosition(Position position) {
+ assert currentPosition == position;
+ addInstruction();
+ writer.writeOneByteInstruction(LIROpcodes.DEBUGPOS);
+ return this;
+ }
+
public LIRCode build() {
+ assert metadata != null;
int constantsCount = constants.size();
DexItem[] constantTable = new DexItem[constantsCount];
constants.forEach((item, index) -> constantTable[index] = item);
- return new LIRCode(constantTable, byteWriter.toByteArray());
+ return new LIRCode(
+ metadata,
+ constantTable,
+ positionTable.toArray(new PositionEntry[positionTable.size()]),
+ argumentCount,
+ byteWriter.toByteArray(),
+ instructionCount);
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRCode.java b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
index c91dbd9..04dffd4 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
@@ -4,24 +4,115 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.lightir.LIRBuilder.ValueIndexGetter;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.StringUtils.BraceType;
+import java.util.Arrays;
public class LIRCode implements Iterable<LIRInstructionView> {
+ public static class PositionEntry {
+ final int fromInstructionIndex;
+ final Position position;
+
+ public PositionEntry(int fromInstructionIndex, Position position) {
+ this.fromInstructionIndex = fromInstructionIndex;
+ this.position = position;
+ }
+ }
+
+ private final IRMetadata metadata;
+
+ /** Constant pool of items. */
private final DexItem[] constants;
+
+ private final PositionEntry[] positionTable;
+
+ /** Full number of arguments (including receiver for non-static methods). */
+ private final int argumentCount;
+
+ /** Byte encoding of the instructions (including phis). */
private final byte[] instructions;
- public static LIRBuilder builder() {
- return new LIRBuilder();
+ /** Cached value for the number of logical instructions (including phis). */
+ private final int instructionCount;
+
+ public static <V> LIRBuilder<V> builder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
+ return new LIRBuilder<V>(method, valueIndexGetter);
}
// Should be constructed using LIRBuilder.
- LIRCode(DexItem[] constants, byte[] instructions) {
+ LIRCode(
+ IRMetadata metadata,
+ DexItem[] constants,
+ PositionEntry[] positions,
+ int argumentCount,
+ byte[] instructions,
+ int instructionCount) {
+ this.metadata = metadata;
this.constants = constants;
+ this.positionTable = positions;
+ this.argumentCount = argumentCount;
this.instructions = instructions;
+ this.instructionCount = instructionCount;
+ }
+
+ public int getArgumentCount() {
+ return argumentCount;
+ }
+
+ public byte[] getInstructionBytes() {
+ return instructions;
+ }
+
+ public int getInstructionCount() {
+ return instructionCount;
+ }
+
+ public IRMetadata getMetadata() {
+ return metadata;
+ }
+
+ public DexItem getConstantItem(int index) {
+ return constants[index];
+ }
+
+ public PositionEntry[] getPositionTable() {
+ return positionTable;
}
@Override
public LIRIterator iterator() {
return new LIRIterator(new ByteArrayIterator(instructions));
}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("LIRCode{");
+ builder
+ .append("args:")
+ .append(argumentCount)
+ .append(", insn(num:")
+ .append(instructionCount)
+ .append(", size:")
+ .append(instructions.length)
+ .append("):{");
+ int index = 0;
+ for (LIRInstructionView view : this) {
+ builder.append(LIROpcodes.toString(view.getOpcode()));
+ if (view.getRemainingOperandSizeInBytes() > 0) {
+ builder.append("(size:").append(1 + view.getRemainingOperandSizeInBytes()).append(")");
+ }
+ if (++index < instructionCount) {
+ builder.append(", ");
+ }
+ }
+ builder.append("}, pool(size:").append(constants.length).append("):");
+ StringUtils.append(builder, Arrays.asList(constants), ", ", BraceType.TUBORG);
+ builder.append("}");
+ return builder.toString();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRInstructionCallback.java
new file mode 100644
index 0000000..42a0955
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/LIRInstructionCallback.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.lightir;
+
+/** Convenience interface to accept a LIR instruction view. */
+public interface LIRInstructionCallback {
+
+ void onInstructionView(LIRInstructionView view);
+}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java b/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
index 59051e3..f42abdb 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
@@ -12,5 +12,21 @@
*/
public interface LIRInstructionView {
- void accept(LIRBasicInstructionCallback eventCallback);
+ /** Convenience method to forward control to a callback. */
+ void accept(LIRInstructionCallback eventCallback);
+
+ /** The opcode of the instruction (See {@code LIROpcodes} for values). */
+ int getOpcode();
+
+ /** The remaining size of the instruction's payload. */
+ int getRemainingOperandSizeInBytes();
+
+ /** True if the instruction has any operands that have not yet been parsed. */
+ boolean hasMoreOperands();
+
+ /** Get the next operand as a constant-pool index. */
+ int getNextConstantOperand();
+
+ /** Get the next operand as an SSA value index. */
+ int getNextValueOperand();
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java b/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
index e05d521..9659aa5 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
@@ -18,35 +18,69 @@
private int currentByteIndex = 0;
private int currentOpcode = -1;
- private int currentOperandSize = 0;
+ private int endOfCurrentInstruction = 0;
public LIRIterator(ByteIterator iterator) {
this.iterator = iterator;
}
+ private void skipRemainingOperands() {
+ if (hasMoreOperands()) {
+ skip(getRemainingOperandSizeInBytes());
+ }
+ }
+
@Override
public boolean hasNext() {
+ skipRemainingOperands();
return iterator.hasNext();
}
@Override
public LIRInstructionView next() {
+ skipRemainingOperands();
currentOpcode = u1();
if (LIROpcodes.isOneByteInstruction(currentOpcode)) {
- currentOperandSize = 0;
+ endOfCurrentInstruction = currentByteIndex;
} else {
// Any instruction that is not a single byte has a two-byte header. The second byte is the
// size of the variable width operand payload.
- currentOperandSize = u1();
- skip(currentOperandSize);
+ int operandSize = u1();
+ endOfCurrentInstruction = currentByteIndex + operandSize;
}
return this;
}
@Override
- public void accept(LIRBasicInstructionCallback eventCallback) {
- int operandsOffset = currentByteIndex - currentOperandSize;
- eventCallback.onInstruction(currentOpcode, operandsOffset, currentOperandSize);
+ public void accept(LIRInstructionCallback eventCallback) {
+ eventCallback.onInstructionView(this);
+ }
+
+ @Override
+ public int getOpcode() {
+ return currentOpcode;
+ }
+
+ @Override
+ public int getRemainingOperandSizeInBytes() {
+ return endOfCurrentInstruction - currentByteIndex;
+ }
+
+ @Override
+ public boolean hasMoreOperands() {
+ return currentByteIndex < endOfCurrentInstruction;
+ }
+
+ @Override
+ public int getNextConstantOperand() {
+ assert hasMoreOperands();
+ return u4();
+ }
+
+ @Override
+ public int getNextValueOperand() {
+ assert hasMoreOperands();
+ return u4();
}
private void skip(int i) {
@@ -58,4 +92,9 @@
++currentByteIndex;
return ByteUtils.fromU1(iterator.nextByte());
}
+
+ private int u4() {
+ currentByteIndex += 4;
+ return ByteUtils.readEncodedInt(iterator);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
index 85cc9f1..089469f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.errors.Unreachable;
+
/**
* Constants related to LIR.
*
@@ -11,12 +13,12 @@
public interface LIROpcodes {
static boolean isOneByteInstruction(int opcode) {
- assert opcode >= NOP;
- return opcode <= DCONST_1;
+ assert opcode >= ACONST_NULL;
+ return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS;
}
// Instructions maintaining the same opcode as defined in CF.
- int NOP = 0;
+ // int NOP = 0;
int ACONST_NULL = 1;
int ICONST_M1 = 2;
int ICONST_0 = 3;
@@ -179,4 +181,317 @@
int LCONST = 201;
int FCONST = 202;
int DCONST = 203;
+ int INVOKEDIRECT = 204;
+ int DEBUGPOS = 205;
+
+ static String toString(int opcode) {
+ switch (opcode) {
+ // case NOP: return "NOP";
+ case ACONST_NULL:
+ return "ACONST_NULL";
+ case ICONST_M1:
+ return "ICONST_M1";
+ case ICONST_0:
+ return "ICONST_0";
+ case ICONST_1:
+ return "ICONST_1";
+ case ICONST_2:
+ return "ICONST_2";
+ case ICONST_3:
+ return "ICONST_3";
+ case ICONST_4:
+ return "ICONST_4";
+ case ICONST_5:
+ return "ICONST_5";
+ case LCONST_0:
+ return "LCONST_0";
+ case LCONST_1:
+ return "LCONST_1";
+ case FCONST_0:
+ return "FCONST_0";
+ case FCONST_1:
+ return "FCONST_1";
+ case FCONST_2:
+ return "FCONST_2";
+ case DCONST_0:
+ return "DCONST_0";
+ case DCONST_1:
+ return "DCONST_1";
+ // case BIPUSH: return "BIPUSH";
+ // case SIPUSH: return "SIPUSH";
+ case LDC:
+ return "LDC";
+ // case ILOAD: return "ILOAD";
+ // case LLOAD: return "LLOAD";
+ // case FLOAD: return "FLOAD";
+ // case DLOAD: return "DLOAD";
+ // case ALOAD: return "ALOAD";
+ case IALOAD:
+ return "IALOAD";
+ case LALOAD:
+ return "LALOAD";
+ case FALOAD:
+ return "FALOAD";
+ case DALOAD:
+ return "DALOAD";
+ case AALOAD:
+ return "AALOAD";
+ case BALOAD:
+ return "BALOAD";
+ case CALOAD:
+ return "CALOAD";
+ case SALOAD:
+ return "SALOAD";
+ // case ISTORE: return "ISTORE";
+ // case LSTORE: return "LSTORE";
+ // case FSTORE: return "FSTORE";
+ // case DSTORE: return "DSTORE";
+ // case ASTORE: return "ASTORE";
+ case IASTORE:
+ return "IASTORE";
+ case LASTORE:
+ return "LASTORE";
+ case FASTORE:
+ return "FASTORE";
+ case DASTORE:
+ return "DASTORE";
+ case AASTORE:
+ return "AASTORE";
+ case BASTORE:
+ return "BASTORE";
+ case CASTORE:
+ return "CASTORE";
+ case SASTORE:
+ return "SASTORE";
+ // case POP: return "POP";
+ // case POP2: return "POP2";
+ // case DUP: return "DUP";
+ // case DUP_X1: return "DUP_X1";
+ // case DUP_X2: return "DUP_X2";
+ // case DUP2: return "DUP2";
+ // case DUP2_X1: return "DUP2_X1";
+ // case DUP2_X2: return "DUP2_X2";
+ // case SWAP: return "SWAP";
+ case IADD:
+ return "IADD";
+ case LADD:
+ return "LADD";
+ case FADD:
+ return "FADD";
+ case DADD:
+ return "DADD";
+ case ISUB:
+ return "ISUB";
+ case LSUB:
+ return "LSUB";
+ case FSUB:
+ return "FSUB";
+ case DSUB:
+ return "DSUB";
+ case IMUL:
+ return "IMUL";
+ case LMUL:
+ return "LMUL";
+ case FMUL:
+ return "FMUL";
+ case DMUL:
+ return "DMUL";
+ case IDIV:
+ return "IDIV";
+ case LDIV:
+ return "LDIV";
+ case FDIV:
+ return "FDIV";
+ case DDIV:
+ return "DDIV";
+ case IREM:
+ return "IREM";
+ case LREM:
+ return "LREM";
+ case FREM:
+ return "FREM";
+ case DREM:
+ return "DREM";
+ case INEG:
+ return "INEG";
+ case LNEG:
+ return "LNEG";
+ case FNEG:
+ return "FNEG";
+ case DNEG:
+ return "DNEG";
+ case ISHL:
+ return "ISHL";
+ case LSHL:
+ return "LSHL";
+ case ISHR:
+ return "ISHR";
+ case LSHR:
+ return "LSHR";
+ case IUSHR:
+ return "IUSHR";
+ case LUSHR:
+ return "LUSHR";
+ case IAND:
+ return "IAND";
+ case LAND:
+ return "LAND";
+ case IOR:
+ return "IOR";
+ case LOR:
+ return "LOR";
+ case IXOR:
+ return "IXOR";
+ case LXOR:
+ return "LXOR";
+ // case IINC: return "IINC";
+ case I2L:
+ return "I2L";
+ case I2F:
+ return "I2F";
+ case I2D:
+ return "I2D";
+ case L2I:
+ return "L2I";
+ case L2F:
+ return "L2F";
+ case L2D:
+ return "L2D";
+ case F2I:
+ return "F2I";
+ case F2L:
+ return "F2L";
+ case F2D:
+ return "F2D";
+ case D2I:
+ return "D2I";
+ case D2L:
+ return "D2L";
+ case D2F:
+ return "D2F";
+ case I2B:
+ return "I2B";
+ case I2C:
+ return "I2C";
+ case I2S:
+ return "I2S";
+ case LCMP:
+ return "LCMP";
+ case FCMPL:
+ return "FCMPL";
+ case FCMPG:
+ return "FCMPG";
+ case DCMPL:
+ return "DCMPL";
+ case DCMPG:
+ return "DCMPG";
+ case IFEQ:
+ return "IFEQ";
+ case IFNE:
+ return "IFNE";
+ case IFLT:
+ return "IFLT";
+ case IFGE:
+ return "IFGE";
+ case IFGT:
+ return "IFGT";
+ case IFLE:
+ return "IFLE";
+ case IF_ICMPEQ:
+ return "IF_ICMPEQ";
+ case IF_ICMPNE:
+ return "IF_ICMPNE";
+ case IF_ICMPLT:
+ return "IF_ICMPLT";
+ case IF_ICMPGE:
+ return "IF_ICMPGE";
+ case IF_ICMPGT:
+ return "IF_ICMPGT";
+ case IF_ICMPLE:
+ return "IF_ICMPLE";
+ case IF_ACMPEQ:
+ return "IF_ACMPEQ";
+ case IF_ACMPNE:
+ return "IF_ACMPNE";
+ case GOTO:
+ return "GOTO";
+ // case JSR: return "JSR";
+ // case RET: return "RET";
+ case TABLESWITCH:
+ return "TABLESWITCH";
+ case LOOKUPSWITCH:
+ return "LOOKUPSWITCH";
+ case IRETURN:
+ return "IRETURN";
+ case LRETURN:
+ return "LRETURN";
+ case FRETURN:
+ return "FRETURN";
+ case DRETURN:
+ return "DRETURN";
+ case ARETURN:
+ return "ARETURN";
+ case RETURN:
+ return "RETURN";
+ case GETSTATIC:
+ return "GETSTATIC";
+ case PUTSTATIC:
+ return "PUTSTATIC";
+ case GETFIELD:
+ return "GETFIELD";
+ case PUTFIELD:
+ return "PUTFIELD";
+ case INVOKEVIRTUAL:
+ return "INVOKEVIRTUAL";
+ case INVOKESPECIAL:
+ return "INVOKESPECIAL";
+ case INVOKESTATIC:
+ return "INVOKESTATIC";
+ case INVOKEINTERFACE:
+ return "INVOKEINTERFACE";
+ case INVOKEDYNAMIC:
+ return "INVOKEDYNAMIC";
+ case NEW:
+ return "NEW";
+ case NEWARRAY:
+ return "NEWARRAY";
+ case ANEWARRAY:
+ return "ANEWARRAY";
+ case ARRAYLENGTH:
+ return "ARRAYLENGTH";
+ case ATHROW:
+ return "ATHROW";
+ case CHECKCAST:
+ return "CHECKCAST";
+ case INSTANCEOF:
+ return "INSTANCEOF";
+ case MONITORENTER:
+ return "MONITORENTER";
+ case MONITOREXIT:
+ return "MONITOREXIT";
+ case MULTIANEWARRAY:
+ return "MULTIANEWARRAY";
+ case IFNULL:
+ return "IFNULL";
+ case IFNONNULL:
+ return "IFNONNULL";
+
+ // Non-CF instructions.
+ case ICONST:
+ return "ICONST";
+ case LCONST:
+ return "LCONST";
+ case FCONST:
+ return "FCONST";
+ case DCONST:
+ return "DCONST";
+ case INVOKEDIRECT:
+ return "INVOKEDIRECT";
+ case DEBUGPOS:
+ return "DEBUGPOS";
+
+ default:
+ throw new Unreachable("Unexpected LIR opcode: " + opcode);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
new file mode 100644
index 0000000..317eeee
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.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.lightir;
+
+import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+/**
+ * Structured callbacks for interpreting LIR.
+ *
+ * <p>This callback parses the actual instructions and dispatches to instruction specific methods
+ * where the parsed data is provided as arguments. Instructions that are part of a family of
+ * instructions have a default implementation that will call the "instruction family" methods (e.g.,
+ * onInvokeVirtual will default dispatch to onInvokedMethodInstruction).
+ *
+ * <p>Due to the parsing of the individual instructions, this parser has a higher overhead than
+ * using the basic {@code LIRInstructionView}.
+ */
+public class LIRParsedInstructionCallback implements LIRInstructionCallback {
+
+ private final LIRCode code;
+
+ public LIRParsedInstructionCallback(LIRCode code) {
+ this.code = code;
+ }
+
+ public void onConstNull() {}
+
+ public void onConstString(DexString string) {}
+
+ public void onInvokeMethodInstruction(DexMethod method, IntList arguments) {}
+
+ public void onInvokeDirect(DexMethod method, IntList arguments) {
+ onInvokeMethodInstruction(method, arguments);
+ }
+
+ public void onInvokeVirtual(DexMethod method, IntList arguments) {
+ onInvokeMethodInstruction(method, arguments);
+ }
+
+ public void onFieldInstruction(DexField field) {
+ onFieldInstruction(field);
+ }
+
+ public void onStaticGet(DexField field) {
+ onFieldInstruction(field);
+ }
+
+ public void onReturnVoid() {}
+
+ public void onDebugPosition() {}
+
+ private DexItem getConstantItem(int index) {
+ return code.getConstantItem(index);
+ }
+
+ @Override
+ public final void onInstructionView(LIRInstructionView view) {
+ switch (view.getOpcode()) {
+ case LIROpcodes.ACONST_NULL:
+ {
+ onConstNull();
+ break;
+ }
+ case LIROpcodes.LDC:
+ {
+ DexItem item = getConstantItem(view.getNextConstantOperand());
+ if (item instanceof DexString) {
+ onConstString((DexString) item);
+ }
+ break;
+ }
+ case LIROpcodes.INVOKEDIRECT:
+ {
+ DexMethod target = getInvokeInstructionTarget(view);
+ IntList arguments = getInvokeInstructionArguments(view);
+ onInvokeDirect(target, arguments);
+ break;
+ }
+ case LIROpcodes.INVOKEVIRTUAL:
+ {
+ DexMethod target = getInvokeInstructionTarget(view);
+ IntList arguments = getInvokeInstructionArguments(view);
+ onInvokeVirtual(target, arguments);
+ break;
+ }
+ case LIROpcodes.GETSTATIC:
+ {
+ DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
+ onStaticGet(field);
+ break;
+ }
+ case LIROpcodes.RETURN:
+ {
+ onReturnVoid();
+ break;
+ }
+ case LIROpcodes.DEBUGPOS:
+ {
+ onDebugPosition();
+ break;
+ }
+ default:
+ throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(view.getOpcode()));
+ }
+ }
+
+ private DexMethod getInvokeInstructionTarget(LIRInstructionView view) {
+ return (DexMethod) getConstantItem(view.getNextConstantOperand());
+ }
+
+ private IntList getInvokeInstructionArguments(LIRInstructionView view) {
+ IntList arguments = new IntArrayList();
+ while (view.hasMoreOperands()) {
+ arguments.add(view.getNextValueOperand());
+ }
+ return arguments;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 668f6de..922fc3d 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -28,10 +28,14 @@
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.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -45,8 +49,10 @@
public static class Builder extends ProguardMap.Builder {
+ private boolean buildPreamble = false;
+ private final List<String> preamble = new ArrayList<>();
private final Map<String, ClassNamingForNameMapper.Builder> mapping = new HashMap<>();
- private LinkedHashSet<MapVersionMappingInformation> mapVersions = new LinkedHashSet<>();
+ private final LinkedHashSet<MapVersionMappingInformation> mapVersions = new LinkedHashSet<>();
private final Map<String, String> originalSourceFiles = new HashMap<>();
@Override
@@ -58,9 +64,22 @@
return classNamingBuilder;
}
+ Builder setBuildPreamble(boolean buildPreamble) {
+ this.buildPreamble = buildPreamble;
+ return this;
+ }
+
+ @Override
+ void addPreambleLine(String line) {
+ if (buildPreamble) {
+ preamble.add(line);
+ }
+ }
+
@Override
public ClassNameMapper build() {
- return new ClassNameMapper(buildClassNameMappings(), mapVersions, originalSourceFiles);
+ return new ClassNameMapper(
+ buildClassNameMappings(), mapVersions, originalSourceFiles, preamble);
}
private ImmutableMap<String, ClassNamingForNameMapper> buildClassNameMappings() {
@@ -100,6 +119,11 @@
return mapperFromBufferedReader(CharSource.wrap(contents).openBufferedStream(), null);
}
+ public static ClassNameMapper mapperFromStringWithPreamble(String contents) throws IOException {
+ return mapperFromBufferedReader(
+ CharSource.wrap(contents).openBufferedStream(), null, false, false, true);
+ }
+
public static ClassNameMapper mapperFromString(
String contents, DiagnosticsHandler diagnosticsHandler) throws IOException {
return mapperFromBufferedReader(
@@ -110,38 +134,43 @@
String contents,
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
- boolean allowExperimentalMapping)
+ boolean allowExperimentalMapping,
+ boolean readPreamble)
throws IOException {
return mapperFromLineReader(
LineReader.fromBufferedReader(CharSource.wrap(contents).openBufferedStream()),
diagnosticsHandler,
allowEmptyMappedRanges,
- allowExperimentalMapping);
+ allowExperimentalMapping,
+ readPreamble);
}
private static ClassNameMapper mapperFromBufferedReader(
BufferedReader reader, DiagnosticsHandler diagnosticsHandler) throws IOException {
- return mapperFromBufferedReader(reader, diagnosticsHandler, false, false);
+ return mapperFromBufferedReader(reader, diagnosticsHandler, false, false, false);
}
public static ClassNameMapper mapperFromBufferedReader(
BufferedReader reader,
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
- boolean allowExperimentalMapping)
+ boolean allowExperimentalMapping,
+ boolean buildPreamble)
throws IOException {
return mapperFromLineReader(
LineReader.fromBufferedReader(reader),
diagnosticsHandler,
allowEmptyMappedRanges,
- allowExperimentalMapping);
+ allowExperimentalMapping,
+ buildPreamble);
}
public static ClassNameMapper mapperFromLineReader(
LineReader reader,
DiagnosticsHandler diagnosticsHandler,
boolean allowEmptyMappedRanges,
- boolean allowExperimentalMapping)
+ boolean allowExperimentalMapping,
+ boolean buildPreamble)
throws IOException {
try (ProguardMapReader proguardReader =
new ProguardMapReader(
@@ -149,7 +178,7 @@
diagnosticsHandler != null ? diagnosticsHandler : new Reporter(),
allowEmptyMappedRanges,
allowExperimentalMapping)) {
- ClassNameMapper.Builder builder = ClassNameMapper.builder();
+ ClassNameMapper.Builder builder = ClassNameMapper.builder().setBuildPreamble(buildPreamble);
proguardReader.parse(builder);
return builder.build();
}
@@ -180,20 +209,27 @@
private final Map<Signature, Signature> signatureMap = new HashMap<>();
private final LinkedHashSet<MapVersionMappingInformation> mapVersions;
private final Map<String, String> originalSourceFiles;
+ private final List<String> preamble;
private ClassNameMapper(
ImmutableMap<String, ClassNamingForNameMapper> classNameMappings,
LinkedHashSet<MapVersionMappingInformation> mapVersions,
- Map<String, String> originalSourceFiles) {
+ Map<String, String> originalSourceFiles,
+ List<String> preamble) {
this.classNameMappings = classNameMappings;
this.mapVersions = mapVersions;
this.originalSourceFiles = originalSourceFiles;
+ this.preamble = preamble;
}
public Map<String, ClassNamingForNameMapper> getClassNameMappings() {
return classNameMappings;
}
+ public Collection<String> getPreamble() {
+ return preamble;
+ }
+
private Signature canonicalizeSignature(Signature signature) {
Signature result = signatureMap.get(signature);
if (result != null) {
@@ -274,7 +310,14 @@
// This will overwrite existing source files but the chance of that happening should be very
// slim.
newSourcesFiles.putAll(other.originalSourceFiles);
- return new ClassNameMapper(builder.build(), newMapVersions, newSourcesFiles);
+
+ List<String> newPreamble = Collections.emptyList();
+ if (!this.preamble.isEmpty() || !other.preamble.isEmpty()) {
+ newPreamble = new ArrayList<>();
+ newPreamble.addAll(this.preamble);
+ newPreamble.addAll(other.preamble);
+ }
+ return new ClassNameMapper(builder.build(), newMapVersions, newSourcesFiles, newPreamble);
}
@Override
@@ -301,7 +344,7 @@
ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
classNameMappings.forEach(builder::put);
- return new ClassNameMapper(builder.build(), mapVersions, originalSourceFiles);
+ return new ClassNameMapper(builder.build(), mapVersions, originalSourceFiles, preamble);
}
public boolean verifyIsSorted() {
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 6e1bc00..3919bbb 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -11,14 +11,22 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.OutlineCallsiteMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.OutlineMappingInformation;
import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.ThrowsCondition;
+import com.android.tools.r8.references.ArrayReference;
+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.utils.BiMapContainer;
+import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SegmentTree;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2IntSortedMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
@@ -28,48 +36,70 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
public class ComposingBuilder {
- /**
- * To ensure we can do alpha renaming of classes and members without polluting the existing
- * mappping, we use a committed map that we update for each class name mapping. That allows us to
- * rename to existing renamed names as long as these are also renamed later in the map.
- */
- private final Map<String, ComposingClassBuilder> committed = new HashMap<>();
-
- private Map<String, ComposingClassBuilder> current = new HashMap<>();
-
private MapVersionMappingInformation currentMapVersion = null;
- private final ComposingSharedData sharedData = new ComposingSharedData();
+ /**
+ * When composing we store a view of the previously known mappings in committed and retain a
+ * current working set. When composing of a new map is finished we commit everything in current
+ * into the committed set.
+ *
+ * <p>The reason for not having just a single set is that we can have a circular mapping as
+ * follows:
+ *
+ * <pre>
+ * a -> b:
+ * ...
+ * b -> a:
+ * </pre>
+ *
+ * After composing our current view of a with the above, we could end up transforming 'a' into 'b'
+ * and then later transforming 'b' back into 'a' again. To ensure we do not mess up namings while
+ * composing classes and methods we resort to a working set and committed set.
+ */
+ private final ComposingData committed = new ComposingData();
+
+ private ComposingData current;
public void compose(ClassNameMapper classNameMapper) throws MappingComposeException {
- MapVersionMappingInformation thisMapVersion = classNameMapper.getFirstMapVersionInformation();
- if (thisMapVersion != null) {
+ current = new ComposingData();
+ MapVersionMappingInformation newMapVersionInfo =
+ classNameMapper.getFirstMapVersionInformation();
+ if (newMapVersionInfo != null) {
+ MapVersion newMapVersion = newMapVersionInfo.getMapVersion();
+ if (newMapVersion.isLessThan(MapVersion.MAP_VERSION_2_1) || newMapVersion.isUnknown()) {
+ throw new MappingComposeException(
+ "Composition of mapping files supported from map version 2.1.");
+ }
if (currentMapVersion == null
- || currentMapVersion.getMapVersion().isLessThan(thisMapVersion.getMapVersion())) {
- currentMapVersion = thisMapVersion;
+ || currentMapVersion.getMapVersion().isLessThan(newMapVersion)) {
+ currentMapVersion = newMapVersionInfo;
}
}
- sharedData.patchupMappingInformation(classNameMapper);
for (ClassNamingForNameMapper classMapping : classNameMapper.getClassNameMappings().values()) {
compose(classMapping);
}
- commit();
+ committed.commit(current, classNameMapper);
}
private void compose(ClassNamingForNameMapper classMapping) throws MappingComposeException {
String originalName = classMapping.originalName;
- ComposingClassBuilder composingClassBuilder = committed.get(originalName);
+ ComposingClassBuilder composingClassBuilder = committed.classBuilders.get(originalName);
String renamedName = classMapping.renamedName;
if (composingClassBuilder == null) {
- composingClassBuilder = new ComposingClassBuilder(originalName, renamedName, sharedData);
+ composingClassBuilder = new ComposingClassBuilder(originalName, renamedName);
} else {
composingClassBuilder.setRenamedName(renamedName);
- committed.remove(originalName);
+ committed.classBuilders.remove(originalName);
}
- ComposingClassBuilder duplicateMapping = current.put(renamedName, composingClassBuilder);
+ composingClassBuilder.setCurrentComposingData(current, classMapping.originalName);
+ ComposingClassBuilder duplicateMapping =
+ current.classBuilders.put(renamedName, composingClassBuilder);
if (duplicateMapping != null) {
throw new MappingComposeException(
"Duplicate class mapping. Both '"
@@ -83,31 +113,13 @@
composingClassBuilder.compose(classMapping);
}
- private void commit() throws MappingComposeException {
- for (Entry<String, ComposingClassBuilder> newEntry : current.entrySet()) {
- String renamedName = newEntry.getKey();
- ComposingClassBuilder classBuilder = newEntry.getValue();
- ComposingClassBuilder duplicateMapping = committed.put(renamedName, classBuilder);
- if (duplicateMapping != null) {
- throw new MappingComposeException(
- "Duplicate class mapping. Both '"
- + duplicateMapping.getOriginalName()
- + "' and '"
- + classBuilder.getOriginalName()
- + "' maps to '"
- + renamedName
- + "'.");
- }
- }
- current = new HashMap<>();
- }
@Override
public String toString() {
- List<ComposingClassBuilder> classBuilders = new ArrayList<>(committed.values());
+ List<ComposingClassBuilder> classBuilders = new ArrayList<>(committed.classBuilders.values());
classBuilders.sort(Comparator.comparing(ComposingClassBuilder::getOriginalName));
StringBuilder sb = new StringBuilder();
- // TODO(b/241763080): Keep preamble of mapping files"
+ committed.preamble.forEach(preambleLine -> sb.append(preambleLine).append("\n"));
if (currentMapVersion != null) {
sb.append("# ").append(currentMapVersion.serialize()).append("\n");
}
@@ -118,34 +130,246 @@
return sb.toString();
}
- public static class ComposingSharedData {
+ public static class ComposingData {
/**
+ * A map of minified names to their class builders. When committing to a new minified name we
+ * destructively remove the previous minified mapping and replace it with the up-to-date one.
+ */
+ private final Map<String, ComposingClassBuilder> classBuilders = new HashMap<>();
+ /**
* RewriteFrameInformation contains condition clauses that are bound to the residual program. As
* a result of that, we have to patch up the conditions when we compose new class mappings.
*/
- private final List<RewriteFrameMappingInformation> mappingInformationToPatchUp =
- new ArrayList<>();
+ private final List<RewriteFrameMappingInformation> rewriteFrameInformation = new ArrayList<>();
+ /** Map of newly added outline call site informations which do not require any rewriting. */
+ private Map<ClassDescriptorAndMethodName, OutlineCallsiteMappingInformation>
+ outlineCallsiteInformation = new HashMap<>();
+ /**
+ * Map of updated outline definitions which has to be committed. The positions in the caller are
+ * fixed at this point since these are local to the method when rewriting.
+ */
+ private final Map<ClassDescriptorAndMethodName, UpdateOutlineCallsiteInformation>
+ outlineSourcePositionsUpdated = new HashMap<>();
- private void patchupMappingInformation(ClassNameMapper classNameMapper) {
- BiMapContainer<String, String> obfuscatedToOriginalMapping =
- classNameMapper.getObfuscatedToOriginalMapping();
- for (RewriteFrameMappingInformation rewriteMappingInfo : mappingInformationToPatchUp) {
+ private final List<String> preamble = new ArrayList<>();
+
+ public void commit(ComposingData current, ClassNameMapper classNameMapper)
+ throws MappingComposeException {
+ preamble.addAll(classNameMapper.getPreamble());
+ commitClassBuilders(current);
+ commitRewriteFrameInformation(current, classNameMapper);
+ commitOutlineCallsiteInformation(current, classNameMapper);
+ }
+
+ private void commitClassBuilders(ComposingData current) throws MappingComposeException {
+ for (Entry<String, ComposingClassBuilder> newEntry : current.classBuilders.entrySet()) {
+ String renamedName = newEntry.getKey();
+ ComposingClassBuilder classBuilder = newEntry.getValue();
+ ComposingClassBuilder duplicateMapping = classBuilders.put(renamedName, classBuilder);
+ if (duplicateMapping != null) {
+ throw new MappingComposeException(
+ "Duplicate class mapping. Both '"
+ + duplicateMapping.getOriginalName()
+ + "' and '"
+ + classBuilder.getOriginalName()
+ + "' maps to '"
+ + renamedName
+ + "'.");
+ }
+ }
+ }
+
+ private void commitRewriteFrameInformation(
+ ComposingData current, ClassNameMapper classNameMapper) {
+ // First update the existing frame information to have new class name mappings.
+ Map<String, String> inverse = classNameMapper.getObfuscatedToOriginalMapping().inverse;
+ for (RewriteFrameMappingInformation rewriteMappingInfo : rewriteFrameInformation) {
rewriteMappingInfo
.getConditions()
.forEach(
rewriteCondition -> {
ThrowsCondition throwsCondition = rewriteCondition.asThrowsCondition();
if (throwsCondition != null) {
- String originalName = throwsCondition.getClassReference().getTypeName();
- String obfuscatedName = obfuscatedToOriginalMapping.inverse.get(originalName);
- if (obfuscatedName != null) {
- throwsCondition.setClassReferenceInternal(
- Reference.classFromTypeName(obfuscatedName));
- }
+ throwsCondition.setClassReferenceInternal(
+ mapTypeReference(inverse, throwsCondition.getClassReference()).asClass());
}
});
}
+ rewriteFrameInformation.addAll(current.rewriteFrameInformation);
+ }
+
+ private void commitOutlineCallsiteInformation(
+ ComposingData current, ClassNameMapper classNameMapper) {
+ // To commit outline call site information, we take the previously committed and bring forward
+ // to a new mapping, and potentially rewrite source positions if available.
+ Map<ClassDescriptorAndMethodName, OutlineCallsiteMappingInformation> newOutlineCallsiteInfo =
+ new HashMap<>();
+ Map<String, String> inverse = classNameMapper.getObfuscatedToOriginalMapping().inverse;
+ outlineCallsiteInformation.forEach(
+ (holderAndMethodNameOfOutline, outlineInfo) -> {
+ UpdateOutlineCallsiteInformation updateOutlineCallsiteInformation =
+ current.outlineSourcePositionsUpdated.get(holderAndMethodNameOfOutline);
+ String newMethodName = outlineInfo.getOutline().getMethodName();
+ if (updateOutlineCallsiteInformation != null) {
+ // We have a callsite mapping that we need to update.
+ MappedRangeOriginalToMinifiedMap originalToMinifiedMap =
+ MappedRangeOriginalToMinifiedMap.build(
+ updateOutlineCallsiteInformation.newMappedRanges);
+ Int2IntSortedMap newPositionMap = new Int2IntLinkedOpenHashMap();
+ outlineInfo
+ .getPositions()
+ .forEach(
+ (originalPosition, destination) -> {
+ originalToMinifiedMap.visitMinified(
+ originalPosition,
+ newMinified -> {
+ newPositionMap.put(newMinified, destination);
+ });
+ });
+ outlineInfo.setPositionsInternal(newPositionMap);
+ newMethodName = updateOutlineCallsiteInformation.newMethodName;
+ }
+ // Holder, return type or formals could have changed the outline descriptor.
+ MethodReference outline = outlineInfo.getOutline();
+ ClassReference newHolder =
+ mapTypeReference(inverse, outline.getHolderClass()).asClass();
+ outlineInfo.setOutlineInternal(
+ Reference.method(
+ newHolder,
+ newMethodName,
+ mapTypeReferences(inverse, outline.getFormalTypes()),
+ mapTypeReference(inverse, outline.getReturnType())));
+ newOutlineCallsiteInfo.put(
+ new ClassDescriptorAndMethodName(
+ newHolder.getTypeName(), holderAndMethodNameOfOutline.getMethodName()),
+ outlineInfo);
+ });
+ newOutlineCallsiteInfo.putAll(current.outlineCallsiteInformation);
+ outlineCallsiteInformation = newOutlineCallsiteInfo;
+ }
+
+ public void addNewOutlineCallsiteInformation(
+ MethodReference outline, OutlineCallsiteMappingInformation outlineCallsiteInfo) {
+ outlineCallsiteInformation.put(
+ new ClassDescriptorAndMethodName(
+ outline.getHolderClass().getTypeName(), outline.getMethodName()),
+ outlineCallsiteInfo);
+ }
+
+ public UpdateOutlineCallsiteInformation getUpdateOutlineCallsiteInformation(
+ String originalHolder, String originalMethodName, String newMethodName) {
+ return outlineSourcePositionsUpdated.computeIfAbsent(
+ new ClassDescriptorAndMethodName(originalHolder, originalMethodName),
+ ignore -> new UpdateOutlineCallsiteInformation(newMethodName));
+ }
+
+ private List<TypeReference> mapTypeReferences(
+ Map<String, String> typeNameMap, List<TypeReference> typeReferences) {
+ return ListUtils.map(typeReferences, typeRef -> mapTypeReference(typeNameMap, typeRef));
+ }
+
+ private TypeReference mapTypeReference(
+ Map<String, String> typeNameMap, TypeReference typeReference) {
+ if (typeReference == null || typeReference.isPrimitive()) {
+ return typeReference;
+ }
+ if (typeReference.isArray()) {
+ ArrayReference arrayReference = typeReference.asArray();
+ return Reference.array(
+ mapTypeReference(typeNameMap, arrayReference.getBaseType()),
+ arrayReference.getDimensions());
+ } else {
+ assert typeReference.isClass();
+ String newTypeName = typeNameMap.get(typeReference.getTypeName());
+ return newTypeName == null ? typeReference : Reference.classFromTypeName(newTypeName);
+ }
+ }
+ }
+
+ private static class ClassDescriptorAndMethodName {
+
+ private final String holderTypeName;
+ private final String methodName;
+
+ public ClassDescriptorAndMethodName(String holderTypeName, String methodName) {
+ this.holderTypeName = holderTypeName;
+ this.methodName = methodName;
+ }
+
+ public String getHolderTypeName() {
+ return holderTypeName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof ClassDescriptorAndMethodName)) {
+ return false;
+ }
+ ClassDescriptorAndMethodName that = (ClassDescriptorAndMethodName) o;
+ return holderTypeName.equals(that.holderTypeName) && methodName.equals(that.methodName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(holderTypeName, methodName);
+ }
+ }
+
+ private static class UpdateOutlineCallsiteInformation {
+
+ private List<MappedRange> newMappedRanges;
+ private final String newMethodName;
+
+ private UpdateOutlineCallsiteInformation(String newMethodName) {
+ this.newMethodName = newMethodName;
+ }
+
+ private void setNewMappedRanges(List<MappedRange> mappedRanges) {
+ newMappedRanges = mappedRanges;
+ }
+ }
+
+ private static class MappedRangeOriginalToMinifiedMap {
+
+ private final Int2ReferenceMap<List<Integer>> originalToMinified;
+
+ private MappedRangeOriginalToMinifiedMap(Int2ReferenceMap<List<Integer>> originalToMinified) {
+ this.originalToMinified = originalToMinified;
+ }
+
+ private static MappedRangeOriginalToMinifiedMap build(List<MappedRange> mappedRanges) {
+ Int2ReferenceMap<List<Integer>> positionMap = new Int2ReferenceOpenHashMap<>();
+ for (MappedRange mappedRange : mappedRanges) {
+ Range originalRange = mappedRange.originalRange;
+ for (int position = originalRange.from; position <= originalRange.to; position++) {
+ // It is perfectly fine to have multiple minified ranges mapping to the same source, we
+ // just need to keep the additional information.
+ positionMap
+ .computeIfAbsent(position, ignoreArgument(ArrayList::new))
+ .add(mappedRange.minifiedRange.from + (position - originalRange.from));
+ }
+ }
+ return new MappedRangeOriginalToMinifiedMap(positionMap);
+ }
+
+ public int lookupFirst(int originalPosition) {
+ List<Integer> minifiedPositions = originalToMinified.get(originalPosition);
+ return minifiedPositions == null ? 0 : minifiedPositions.get(0);
+ }
+
+ public void visitMinified(int originalPosition, Consumer<Integer> consumer) {
+ List<Integer> minifiedPositions = originalToMinified.get(originalPosition);
+ if (minifiedPositions != null) {
+ minifiedPositions.forEach(consumer);
+ }
}
}
@@ -163,13 +387,23 @@
// one method since any shrinker should put in line numbers for overloads.
private final Map<String, SegmentTree<List<MappedRange>>> methodMembers = new HashMap<>();
private List<MappingInformation> additionalMappingInfo = null;
- private final ComposingSharedData sharedData;
- private ComposingClassBuilder(
- String originalName, String renamedName, ComposingSharedData sharedData) {
+ private ComposingData current;
+
+ /**
+ * Keeps track of the current original name which is different from originalName if this is a
+ * subsequent mapping.
+ */
+ private String currentOriginalName;
+
+ private ComposingClassBuilder(String originalName, String renamedName) {
this.originalName = originalName;
this.renamedName = renamedName;
- this.sharedData = sharedData;
+ }
+
+ public void setCurrentComposingData(ComposingData current, String currentMinifiedName) {
+ this.current = current;
+ this.currentOriginalName = currentMinifiedName;
}
public void setRenamedName(String renamedName) {
@@ -268,7 +502,8 @@
* long as the current mapped range is the same method and return a mapped range result
* containing all ranges for a method along with some additional information.
*/
- private MappedRangeResult getMappedRangesForMethod(List<MappedRange> mappedRanges, int index) {
+ private MappedRangeResult getMappedRangesForMethod(List<MappedRange> mappedRanges, int index)
+ throws MappingComposeException {
if (index >= mappedRanges.size()) {
return null;
}
@@ -297,10 +532,30 @@
&& !isInlineMappedRange(mappedRanges, i)) {
break;
}
+ // Register mapping information that is dependent on the residual naming to allow updating
+ // later on.
for (MappingInformation mappingInformation : thisMappedRange.getAdditionalMappingInfo()) {
if (mappingInformation.isRewriteFrameMappingInformation()) {
- sharedData.mappingInformationToPatchUp.add(
- mappingInformation.asRewriteFrameMappingInformation());
+ RewriteFrameMappingInformation rewriteFrameMappingInformation =
+ mappingInformation.asRewriteFrameMappingInformation();
+ rewriteFrameMappingInformation
+ .getConditions()
+ .forEach(
+ condition -> {
+ if (condition.isThrowsCondition()) {
+ current.rewriteFrameInformation.add(rewriteFrameMappingInformation);
+ }
+ });
+ } else if (mappingInformation.isOutlineCallsiteInformation()) {
+ OutlineCallsiteMappingInformation outlineCallsiteInfo =
+ mappingInformation.asOutlineCallsiteInformation();
+ MethodReference outline = outlineCallsiteInfo.getOutline();
+ if (outline == null) {
+ throw new MappingComposeException(
+ "Unable to compose outline call site information without outline key: "
+ + outlineCallsiteInfo.serialize());
+ }
+ current.addNewOutlineCallsiteInformation(outline, outlineCallsiteInfo);
}
}
seenMappedRanges.add(thisMappedRange);
@@ -345,6 +600,7 @@
Int2ReferenceMap<List<MappedRange>> mappedRangesForPosition =
getExistingMapping(existingRanges);
List<MappedRange> newComposedRanges = new ArrayList<>();
+ ComputedOutlineInformation computedOutlineInformation = new ComputedOutlineInformation();
for (int i = 0; i < newRanges.size(); i++) {
if (isInlineMappedRange(newRanges, i)) {
throw new MappingComposeException(
@@ -378,6 +634,7 @@
newComposedRanges,
newRange,
existingMappedRanges,
+ computedOutlineInformation,
newRange.minifiedRange.from,
newRange.minifiedRange.to);
} else {
@@ -403,6 +660,7 @@
newComposedRanges,
newRange,
existingMappedRanges,
+ computedOutlineInformation,
lastStartingMinifiedFrom,
position - 1);
lastStartingMinifiedFrom = position;
@@ -413,11 +671,41 @@
newComposedRanges,
newRange,
existingMappedRanges,
+ computedOutlineInformation,
lastStartingMinifiedFrom,
newRange.minifiedRange.to);
}
}
}
+ MappedRange lastComposedRange = ListUtils.last(newComposedRanges);
+ if (computedOutlineInformation.seenOutlineMappingInformation != null) {
+ current
+ .getUpdateOutlineCallsiteInformation(
+ currentOriginalName,
+ ListUtils.last(newRanges).signature.getName(),
+ lastComposedRange.renamedName)
+ .setNewMappedRanges(newRanges);
+ lastComposedRange.addMappingInformation(
+ computedOutlineInformation.seenOutlineMappingInformation,
+ ConsumerUtils.emptyConsumer());
+ }
+ if (!computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp.isEmpty()) {
+ MappedRangeOriginalToMinifiedMap originalToMinifiedMap =
+ MappedRangeOriginalToMinifiedMap.build(newRanges);
+ List<OutlineCallsiteMappingInformation> outlineCallSites =
+ new ArrayList<>(computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp);
+ outlineCallSites.sort(Comparator.comparing(mapping -> mapping.getOutline().toString()));
+ for (OutlineCallsiteMappingInformation outlineCallSite : outlineCallSites) {
+ Int2IntSortedMap positionMap = outlineCallSite.getPositions();
+ for (Integer keyPosition : positionMap.keySet()) {
+ int keyPositionInt = keyPosition;
+ int originalDestination = positionMap.get(keyPositionInt);
+ int newDestination = originalToMinifiedMap.lookupFirst(originalDestination);
+ positionMap.put(keyPositionInt, newDestination);
+ }
+ lastComposedRange.addMappingInformation(outlineCallSite, ConsumerUtils.emptyConsumer());
+ }
+ }
return newComposedRanges;
}
@@ -453,6 +741,7 @@
List<MappedRange> newComposedRanges,
MappedRange newMappedRange,
List<MappedRange> existingMappedRanges,
+ ComputedOutlineInformation computedOutlineInformation,
int lastStartingMinifiedFrom,
int position) {
Range existingRange = existingMappedRanges.get(0).minifiedRange;
@@ -485,7 +774,17 @@
existingMappedRange
.getAdditionalMappingInfo()
.forEach(
- info -> computedRange.addMappingInformation(info, ConsumerUtils.emptyConsumer()));
+ info -> {
+ if (info.isOutlineMappingInformation()) {
+ computedOutlineInformation.seenOutlineMappingInformation =
+ info.asOutlineMappingInformation();
+ } else if (info.isOutlineCallsiteInformation()) {
+ computedOutlineInformation.outlineCallsiteMappingInformationToPatchUp.add(
+ info.asOutlineCallsiteInformation());
+ } else {
+ computedRange.addMappingInformation(info, ConsumerUtils.emptyConsumer());
+ }
+ });
newComposedRanges.add(computedRange);
}
}
@@ -573,5 +872,11 @@
this.allRanges = allRanges;
}
}
+
+ private static class ComputedOutlineInformation {
+ private final Set<OutlineCallsiteMappingInformation>
+ outlineCallsiteMappingInformationToPatchUp = Sets.newIdentityHashSet();
+ private OutlineMappingInformation seenOutlineMappingInformation = null;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MapVersion.java b/src/main/java/com/android/tools/r8/naming/MapVersion.java
index 8d8c9e0..322d15d 100644
--- a/src/main/java/com/android/tools/r8/naming/MapVersion.java
+++ b/src/main/java/com/android/tools/r8/naming/MapVersion.java
@@ -12,10 +12,11 @@
MAP_VERSION_NONE("none"),
MAP_VERSION_1_0("1.0"),
MAP_VERSION_2_0("2.0"),
+ MAP_VERSION_2_1("2.1"),
MAP_VERSION_EXPERIMENTAL("experimental"),
MAP_VERSION_UNKNOWN("unknown");
- public static final MapVersion STABLE = MAP_VERSION_2_0;
+ public static final MapVersion STABLE = MAP_VERSION_2_1;
private final String name;
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMap.java b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
index 6b95444..38fe729 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMap.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMap.java
@@ -13,6 +13,8 @@
abstract ClassNaming.Builder classNamingBuilder(
String renamedName, String originalName, Position position);
+ abstract void addPreambleLine(String line);
+
abstract Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion);
abstract ProguardMap build();
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index a3a3aa0..edb8793 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -69,6 +69,7 @@
private final DiagnosticsHandler diagnosticsHandler;
private final boolean allowEmptyMappedRanges;
private final boolean allowExperimentalMapping;
+ private boolean seenClassMapping = false;
private final CardinalPositionRangeAllocator cardinalRangeCache =
PositionRangeAllocator.createCardinalPositionRangeAllocator();
@@ -147,11 +148,11 @@
}
}
- private boolean nextLine() throws IOException {
+ private boolean nextLine(ProguardMap.Builder mapBuilder) throws IOException {
if (line.length() != lineOffset) {
throw new ParseException("Expected end of line");
}
- return skipLine();
+ return skipLine(mapBuilder);
}
private boolean isEmptyOrCommentLine(String line) {
@@ -184,10 +185,6 @@
return false;
}
- private boolean isClassMapping() {
- return !isEmptyOrCommentLine(line) && line.endsWith(":");
- }
-
private static boolean hasFirstCharJsonBrace(String line, int commentCharIndex) {
for (int i = commentCharIndex + 1; i < line.length(); i++) {
char c = line.charAt(i);
@@ -200,12 +197,17 @@
return false;
}
- private boolean skipLine() throws IOException {
+ private boolean skipLine(ProguardMap.Builder mapBuilder) throws IOException {
lineOffset = 0;
+ boolean isEmptyOrCommentLine;
do {
- lineNo++;
line = reader.readLine();
- } while (hasLine() && isEmptyOrCommentLine(line));
+ lineNo++;
+ isEmptyOrCommentLine = isEmptyOrCommentLine(line);
+ if (!seenClassMapping && isEmptyOrCommentLine) {
+ mapBuilder.addPreambleLine(line);
+ }
+ } while (hasLine() && isEmptyOrCommentLine);
return hasLine();
}
@@ -242,10 +244,7 @@
void parse(ProguardMap.Builder mapBuilder) throws IOException {
// Read the first line.
- do {
- line = reader.readLine();
- lineNo++;
- } while (hasLine() && isEmptyOrCommentLine(line));
+ skipLine(mapBuilder);
parseClassMappings(mapBuilder);
}
@@ -255,17 +254,23 @@
while (hasLine()) {
skipWhitespace();
if (isCommentLineWithJsonBrace()) {
- parseMappingInformation(
+ if (!parseMappingInformation(
info -> {
assert info.isMapVersionMappingInformation()
|| info.isUnknownJsonMappingInformation();
if (info.isMapVersionMappingInformation()) {
mapBuilder.setCurrentMapVersion(info.asMapVersionMappingInformation());
+ } else if (!seenClassMapping) {
+ mapBuilder.addPreambleLine(line);
}
- });
+ })) {
+ if (!seenClassMapping) {
+ mapBuilder.addPreambleLine(line);
+ }
+ }
// Skip reading the rest of the line.
lineOffset = line.length();
- nextLine();
+ nextLine(mapBuilder);
continue;
}
String before = parseType(false);
@@ -284,40 +289,47 @@
String after = parseType(false);
skipWhitespace();
expect(':');
+ seenClassMapping = true;
ClassNaming.Builder currentClassBuilder =
mapBuilder.classNamingBuilder(after, before, getPosition());
skipWhitespace();
- if (nextLine()) {
- parseMemberMappings(currentClassBuilder);
+ if (nextLine(mapBuilder)) {
+ parseMemberMappings(mapBuilder, currentClassBuilder);
}
}
}
- private void parseMappingInformation(Consumer<MappingInformation> onMappingInfo) {
- MappingInformation.fromJsonObject(
- version,
- parseJsonInComment(),
- diagnosticsHandler,
- lineNo,
- info -> {
- MapVersionMappingInformation generatorInfo = info.asMapVersionMappingInformation();
- if (generatorInfo != null) {
- if (generatorInfo.getMapVersion().equals(MapVersion.MAP_VERSION_EXPERIMENTAL)) {
- // A mapping file that is marked "experimental" will be treated as an unversioned
- // file if the compiler/tool is not explicitly running with experimental support.
- version =
- allowExperimentalMapping
- ? MapVersion.MAP_VERSION_EXPERIMENTAL
- : MapVersion.MAP_VERSION_NONE;
- } else {
- version = generatorInfo.getMapVersion();
+ private boolean parseMappingInformation(Consumer<MappingInformation> onMappingInfo) {
+ JsonObject object = parseJsonInComment();
+ if (object != null) {
+ MappingInformation.fromJsonObject(
+ version,
+ object,
+ diagnosticsHandler,
+ lineNo,
+ info -> {
+ MapVersionMappingInformation generatorInfo = info.asMapVersionMappingInformation();
+ if (generatorInfo != null) {
+ if (generatorInfo.getMapVersion().equals(MapVersion.MAP_VERSION_EXPERIMENTAL)) {
+ // A mapping file that is marked "experimental" will be treated as an unversioned
+ // file if the compiler/tool is not explicitly running with experimental support.
+ version =
+ allowExperimentalMapping
+ ? MapVersion.MAP_VERSION_EXPERIMENTAL
+ : MapVersion.MAP_VERSION_NONE;
+ } else {
+ version = generatorInfo.getMapVersion();
+ }
}
- }
- onMappingInfo.accept(info);
- });
+ onMappingInfo.accept(info);
+ });
+ return true;
+ }
+ return false;
}
- private void parseMemberMappings(ClassNaming.Builder classNamingBuilder) throws IOException {
+ private void parseMemberMappings(
+ ProguardMap.Builder mapBuilder, ClassNaming.Builder classNamingBuilder) throws IOException {
MemberNaming lastAddedNaming = null;
MemberNaming activeMemberNaming = null;
MappedRange activeMappedRange = null;
@@ -421,7 +433,7 @@
activeMemberNaming =
new MemberNaming(signature, signature.asRenamed(renamedName), getPosition());
previousMappedRange = mappedRange;
- } while (nextLine());
+ } while (nextLine(mapBuilder));
if (activeMemberNaming != null) {
boolean notAdded =
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index bf8044e..45f7a5b 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -63,6 +63,11 @@
}
@Override
+ void addPreambleLine(String line) {
+ // Do nothing.
+ }
+
+ @Override
ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
// Do nothing
return this;
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index 787a0bc..c7d53f9 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -66,6 +66,10 @@
return null;
}
+ public OutlineMappingInformation asOutlineMappingInformation() {
+ return null;
+ }
+
public OutlineCallsiteMappingInformation asOutlineCallsiteInformation() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java
index 6cb093d..efdb788 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java
@@ -6,6 +6,9 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
@@ -15,14 +18,18 @@
public class OutlineCallsiteMappingInformation extends MappingInformation {
public static final MapVersion SUPPORTED_VERSION = MapVersion.MAP_VERSION_2_0;
+ public static final MapVersion SUPPORTED_WITH_OUTLINE_VERSION = MapVersion.MAP_VERSION_2_1;
public static final String ID = "com.android.tools.r8.outlineCallsite";
private static final String POSITIONS_KEY = "positions";
+ private static final String OUTLINE_KEY = "outline";
- private final Int2IntSortedMap positions;
+ private Int2IntSortedMap positions;
+ private MethodReference outline;
- private OutlineCallsiteMappingInformation(Int2IntSortedMap positions) {
+ private OutlineCallsiteMappingInformation(Int2IntSortedMap positions, MethodReference outline) {
this.positions = positions;
+ this.outline = outline;
}
@Override
@@ -40,6 +47,9 @@
mappedPositions.add(obfuscatedPosition + "", new JsonPrimitive(originalPosition));
});
result.add(POSITIONS_KEY, mappedPositions);
+ if (outline != null) {
+ result.add(OUTLINE_KEY, new JsonPrimitive(outline.toString()));
+ }
return result.toString();
}
@@ -62,8 +72,13 @@
return positions.getOrDefault(originalPosition, originalPosition);
}
- public static OutlineCallsiteMappingInformation create(Int2IntSortedMap positions) {
- return new OutlineCallsiteMappingInformation(positions);
+ public MethodReference getOutline() {
+ return outline;
+ }
+
+ public static OutlineCallsiteMappingInformation create(
+ Int2IntSortedMap positions, MethodReference outline) {
+ return new OutlineCallsiteMappingInformation(positions, outline);
}
public static boolean isSupported(MapVersion version) {
@@ -75,8 +90,7 @@
if (isSupported(version)) {
JsonObject postionsMapObject = object.getAsJsonObject(POSITIONS_KEY);
if (postionsMapObject == null) {
- throw new CompilationError(
- "Expected '" + POSITIONS_KEY + "' to be present: " + object.getAsString());
+ throw new CompilationError("Expected '" + POSITIONS_KEY + "' to be present: " + object);
}
Int2IntSortedMap positionsMap = new Int2IntLinkedOpenHashMap();
postionsMapObject
@@ -92,7 +106,26 @@
throw new CompilationError("Invalid position entry: " + entry.toString());
}
});
- onMappingInfo.accept(OutlineCallsiteMappingInformation.create(positionsMap));
+ MethodReference outline = null;
+ JsonElement outlineElement = object.get(OUTLINE_KEY);
+ if (outlineElement != null) {
+ outline = MethodReferenceUtils.methodFromSmali(outlineElement.getAsString());
+ } else if (version.isGreaterThanOrEqualTo(SUPPORTED_WITH_OUTLINE_VERSION)) {
+ throw new CompilationError("Expected '" + OUTLINE_KEY + "' to be present: " + object);
+ }
+ onMappingInfo.accept(OutlineCallsiteMappingInformation.create(positionsMap, outline));
}
}
+
+ public void setOutlineInternal(MethodReference outline) {
+ this.outline = outline;
+ }
+
+ public Int2IntSortedMap getPositions() {
+ return positions;
+ }
+
+ public void setPositionsInternal(Int2IntSortedMap positions) {
+ this.positions = positions;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java
index 1914431..54c392b 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java
@@ -27,6 +27,11 @@
}
@Override
+ public OutlineMappingInformation asOutlineMappingInformation() {
+ return this;
+ }
+
+ @Override
public boolean allowOther(MappingInformation information) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/references/MethodReference.java b/src/main/java/com/android/tools/r8/references/MethodReference.java
index e0314b8..5ef8596 100644
--- a/src/main/java/com/android/tools/r8/references/MethodReference.java
+++ b/src/main/java/com/android/tools/r8/references/MethodReference.java
@@ -85,6 +85,6 @@
@Override
public String toString() {
- return getHolderClass().toString() + getMethodName() + getMethodDescriptor();
+ return getHolderClass() + getMethodName() + getMethodDescriptor();
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 1008d80..90b9dfb 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2820,19 +2820,20 @@
WorkList<DexType> worklist = WorkList.newIdentityWorkList(type);
worklist.addIfNotSeen(interfaces);
while (worklist.hasNext()) {
- DexClass clazz = appInfo().definitionFor(worklist.next());
- if (clazz == null) {
- continue;
- }
- if (clazz.isProgramClass()) {
- markProgramMethodOverridesAsLive(instantiation, clazz.asProgramClass());
- } else {
- markLibraryAndClasspathMethodOverridesAsLive(instantiation, clazz);
- }
- if (clazz.superType != null) {
- worklist.addIfNotSeen(clazz.superType);
- }
- worklist.addIfNotSeen(clazz.interfaces);
+ ClassResolutionResult classResolutionResult =
+ appInfo().contextIndependentDefinitionForWithResolutionResult(worklist.next());
+ classResolutionResult.forEachClassResolutionResult(
+ clazz -> {
+ if (clazz.isProgramClass()) {
+ markProgramMethodOverridesAsLive(instantiation, clazz.asProgramClass());
+ } else {
+ markLibraryAndClasspathMethodOverridesAsLive(instantiation, clazz);
+ }
+ if (clazz.superType != null) {
+ worklist.addIfNotSeen(clazz.superType);
+ }
+ worklist.addIfNotSeen(clazz.interfaces);
+ });
}
}
@@ -2973,12 +2974,18 @@
worklist.addIfNotSeen(instantiatedClass);
while (worklist.hasNext()) {
DexProgramClass clazz = worklist.next();
- DexEncodedMethod override = clazz.lookupVirtualMethod(libraryMethodOverride);
+ ProgramMethod override = clazz.lookupProgramMethod(libraryMethodOverride);
if (override != null) {
- if (override.isLibraryMethodOverride().isTrue()) {
+ if (override.getDefinition().isLibraryMethodOverride().isTrue()) {
continue;
}
- override.setLibraryMethodOverride(OptionalBool.TRUE);
+ override.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE);
+ // TODO(b/243483849): The minifier does not detect library overrides if the library class
+ // is present both as program and library class. We force disable minification here as a
+ // work-around until this is fixed.
+ if (options.loadAllClassDefinitions) {
+ shouldNotBeMinified(override);
+ }
}
clazz.forEachImmediateSupertype(
superType -> {
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 cf357ed..773903d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -288,7 +288,7 @@
|| parseUnsupportedOptionAndErr(optionStart)) {
// Intentionally left empty.
} else if (acceptString("keepkotlinmetadata")) {
- configurationBuilder.addRule(
+ ProguardKeepRule keepKotlinMetadata =
ProguardKeepRule.builder()
.setType(ProguardKeepRuleType.KEEP)
.setClassType(ProguardClassType.CLASS)
@@ -301,7 +301,11 @@
.build())
.setMemberRules(Collections.singletonList(ProguardMemberRule.defaultKeepAllRule()))
.setSource("-keepkotlinmetadata")
- .build());
+ .build();
+ // Mark the rule as used to ensure we do not report any information messages if the class
+ // is not present.
+ keepKotlinMetadata.markAsUsed();
+ configurationBuilder.addRule(keepKotlinMetadata);
configurationBuilder.addKeepAttributePatterns(
Collections.singletonList(RUNTIME_VISIBLE_ANNOTATIONS));
} else if (acceptString("renamesourcefileattribute")) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 37641f0..8760fb8 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -387,9 +388,8 @@
.map(DexEncodedMember::getReference)
.noneMatch(appInfo::isPinned);
- if (!appInfo
- .getClassToFeatureSplitMap()
- .isInSameFeatureOrBothInSameBase(sourceClass, targetClass, appView)) {
+ if (!FeatureSplitBoundaryOptimizationUtils.isSafeForVerticalClassMerging(
+ sourceClass, targetClass, appView)) {
return false;
}
if (appView.appServices().allServiceTypes().contains(sourceClass.type)
diff --git a/src/main/java/com/android/tools/r8/startup/StartupClassBuilder.java b/src/main/java/com/android/tools/r8/startup/StartupClassBuilder.java
new file mode 100644
index 0000000..43a63b4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/startup/StartupClassBuilder.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;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+
+/** Interface for providing information about a startup class to the compiler. */
+@Keep
+public interface StartupClassBuilder {
+
+ StartupClassBuilder setClassReference(ClassReference classReference);
+}
diff --git a/src/main/java/com/android/tools/r8/startup/StartupMethodBuilder.java b/src/main/java/com/android/tools/r8/startup/StartupMethodBuilder.java
new file mode 100644
index 0000000..0c1efe2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/startup/StartupMethodBuilder.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;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.MethodReference;
+
+/** Interface for providing information about a startup method to the compiler. */
+@Keep
+public interface StartupMethodBuilder {
+
+ StartupMethodBuilder setMethodReference(MethodReference methodReference);
+}
diff --git a/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java b/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
new file mode 100644
index 0000000..d3626a1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/startup/StartupProfileBuilder.java
@@ -0,0 +1,43 @@
+// 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;
+
+import com.android.tools.r8.Keep;
+import java.util.function.Consumer;
+
+/** Interface for providing a startup profile to the compiler. */
+@Keep
+public interface StartupProfileBuilder {
+
+ /** API for adding information about a startup class to the compiler. */
+ StartupProfileBuilder addStartupClass(Consumer<StartupClassBuilder> startupClassBuilderConsumer);
+
+ /** API for adding information about a startup method to the compiler. */
+ StartupProfileBuilder addStartupMethod(
+ Consumer<StartupMethodBuilder> startupMethodBuilderConsumer);
+
+ /**
+ * API for adding information about a synthetic startup method to the compiler.
+ *
+ * <p>When shrinking an app using R8, the names of synthetic classes may differ from the synthetic
+ * names that arise from dexing the app using D8. Therefore, synthetic classes and methods should
+ * not be added to the startup profile using {@link #addStartupClass(Consumer)} and {@link
+ * #addStartupMethod(Consumer)}.
+ *
+ * <p>Instead, synthetic items should be added to the startup profile using this method, which
+ * takes the name of the synthetic context instead of the synthetic name. The addition of the
+ * synthetic context will be interpreted as the presence of any method on any synthetic class that
+ * has been synthesized from the synthetic context.
+ *
+ * <p>Example: Instead of adding "Lcom/example/MainActivity$ExternalSynthetic0;->m()V" as a
+ * (non-synthetic) startup method, a synthetic startup method should be added with synthetic
+ * context "Lcom/example/MainActivity;".
+ *
+ * <p>NOTE: This should only be used when supplying a startup profile that is generated from an
+ * unobfuscated build of the app to R8.
+ */
+ StartupProfileBuilder addSyntheticStartupMethod(
+ Consumer<SyntheticStartupMethodBuilder> syntheticStartupMethodBuilderConsumer);
+}
diff --git a/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java b/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.java
new file mode 100644
index 0000000..4d98373
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/startup/StartupProfileProvider.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.startup;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.Resource;
+
+/** Interface for providing a startup profile to the compiler. */
+@Keep
+public interface StartupProfileProvider extends Resource {
+
+ // TODO(b/238173796): Change the implementation to use the new API below.
+ /** Return the startup profile. */
+ String get();
+
+ /** Provides the startup profile by callbacks to the given {@param startupProfileBuilder}. */
+ void getStartupProfile(StartupProfileBuilder startupProfileBuilder);
+}
diff --git a/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java b/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.java
new file mode 100644
index 0000000..281bac7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/startup/SyntheticStartupMethodBuilder.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;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+
+/** Interface for providing information about a synthetic startup method to the compiler. */
+@Keep
+public interface SyntheticStartupMethodBuilder {
+
+ SyntheticStartupMethodBuilder setSyntheticContextReference(ClassReference classReference);
+}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index 9aed303..2de90be 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -29,6 +29,7 @@
public final SyntheticKind API_MODEL_STUB = generator.forGlobalClass();
// Classpath only synthetics in the global type namespace.
+ public final SyntheticKind GENERIC_API_CONVERSION_STUB = generator.forGlobalClasspathClass();
public final SyntheticKind RETARGET_STUB = generator.forGlobalClasspathClass();
public final SyntheticKind EMULATED_INTERFACE_MARKER_CLASS = generator.forGlobalClasspathClass();
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 d784613..f02405c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1793,6 +1793,7 @@
public static class TestingOptions {
public boolean neverReuseCfLocalRegisters = false;
+ public boolean roundtripThroughLIR = false;
private boolean hasReadCheckDeterminism = false;
private DeterminismChecker determinismChecker = null;
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index a0a2a74..90d26b4 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -57,6 +57,7 @@
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.PositionRangeAllocator;
import com.android.tools.r8.naming.PositionRangeAllocator.CardinalPositionRangeAllocator;
import com.android.tools.r8.naming.PositionRangeAllocator.NonCardinalPositionRangeAllocator;
@@ -71,6 +72,7 @@
import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.RemoveInnerFramesAction;
import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.ThrowsCondition;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.internal.RetraceUtils;
import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -674,7 +676,9 @@
DexMethod outlineMethod = getOutlineMethod(mappedPositions.get(0));
if (outlineMethod != null) {
outlinesToFix
- .computeIfAbsent(outlineMethod, ignored -> new OutlineFixupBuilder())
+ .computeIfAbsent(
+ outlineMethod,
+ outline -> new OutlineFixupBuilder(computeMappedMethod(outline, appView)))
.setMappedPositionsOutline(mappedPositions);
methodMappingInfo.add(OutlineMappingInformation.builder().build());
}
@@ -778,7 +782,8 @@
});
outlinesToFix
.computeIfAbsent(
- firstPosition.outlineCallee, ignored -> new OutlineFixupBuilder())
+ firstPosition.outlineCallee,
+ outline -> new OutlineFixupBuilder(computeMappedMethod(outline, appView)))
.addMappedRangeForOutlineCallee(lastMappedRange, positionMap);
}
i = j;
@@ -831,6 +836,14 @@
return false;
}
+ private static MethodReference computeMappedMethod(DexMethod current, AppView<?> appView) {
+ NamingLens namingLens = appView.getNamingLens();
+ DexMethod renamedMethodSignature =
+ namingLens.lookupMethod(
+ appView.graphLens().getRenamedMethodSignature(current), appView.dexItemFactory());
+ return renamedMethodSignature.asMethodReference();
+ }
+
private static DexMethod getOutlineMethod(MappedPosition mappedPosition) {
if (mappedPosition.isOutline) {
return mappedPosition.method;
@@ -1403,12 +1416,17 @@
private static class OutlineFixupBuilder {
- private static int MINIFIED_POSITION_REMOVED = -1;
+ private static final int MINIFIED_POSITION_REMOVED = -1;
+ private final MethodReference outlineMethod;
private List<MappedPosition> mappedOutlinePositions = null;
private final List<Pair<MappedRange, Int2IntMap>> mappedOutlineCalleePositions =
new ArrayList<>();
+ private OutlineFixupBuilder(MethodReference outlineMethod) {
+ this.outlineMethod = outlineMethod;
+ }
+
public void setMappedPositionsOutline(List<MappedPosition> mappedPositionsOutline) {
this.mappedOutlinePositions = mappedPositionsOutline;
}
@@ -1437,7 +1455,7 @@
}
});
mappedRange.addMappingInformation(
- OutlineCallsiteMappingInformation.create(map), Unreachable::raise);
+ OutlineCallsiteMappingInformation.create(map, outlineMethod), Unreachable::raise);
}
}
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 037fafb..8e240d7 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -147,4 +147,13 @@
}
return builder.append(")").toString();
}
+
+ public static MethodReference methodFromSmali(String smali) {
+ int holderEndIndex = smali.indexOf(";") + 1;
+ int methodDescriptorIndex = smali.indexOf("(", holderEndIndex);
+ return Reference.methodFromDescriptor(
+ smali.substring(0, holderEndIndex),
+ smali.substring(holderEndIndex, methodDescriptorIndex),
+ smali.substring(methodDescriptorIndex));
+ }
}
diff --git a/src/test/examplesJava9/stackwalker/Example.java b/src/test/examplesJava9/stackwalker/Example.java
new file mode 100644
index 0000000..6208060
--- /dev/null
+++ b/src/test/examplesJava9/stackwalker/Example.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 stackwalker;
+
+import java.lang.StackWalker.StackFrame;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class Example {
+ public static void main(String[] args) {
+ List<String> OneFrameStack =
+ StackWalker.getInstance()
+ .walk(s -> s.limit(7).map(StackFrame::getMethodName).collect(Collectors.toList()));
+ System.out.println(OneFrameStack);
+ frame1();
+ }
+
+ public static void frame1() {
+ frame2();
+ }
+
+ public static void frame2() {
+ List<String> ThreeFrameStack =
+ StackWalker.getInstance()
+ .walk(s -> s.limit(7).map(StackFrame::getMethodName).collect(Collectors.toList()));
+ System.out.println(ThreeFrameStack);
+ }
+}
diff --git a/src/test/examplesJava9/timeunit/Example.java b/src/test/examplesJava9/timeunit/Example.java
new file mode 100644
index 0000000..09e1d0c
--- /dev/null
+++ b/src/test/examplesJava9/timeunit/Example.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 timeunit;
+
+import java.time.temporal.ChronoUnit;
+import java.util.concurrent.TimeUnit;
+
+public class Example {
+
+ public static void main(String[] args) {
+ TimeUnit timeUnit = TimeUnit.of(ChronoUnit.NANOS);
+ System.out.println(timeUnit.toChronoUnit());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 3186dc1..3050989 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -8,6 +8,8 @@
import static junit.framework.TestCase.assertTrue;
import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidAppConsumers;
@@ -23,6 +25,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -195,6 +198,7 @@
if (programConsumer != null) {
return null;
}
+ assertNoUnexpectedDiagnosticMessages();
return new L8TestCompileResult(
sink.build(),
apiLevel,
@@ -203,16 +207,53 @@
mapping,
state,
backend.isCf() ? OutputMode.ClassFile : OutputMode.DexIndexed)
- .applyIf(
- finalPrefixVerification,
- compileResult ->
- compileResult.inspect(
- inspector ->
- inspector.forAllClasses(
- clazz ->
- assertTrue(
- clazz.getFinalName().startsWith("j$.")
- || clazz.getFinalName().startsWith("java.")))));
+ .applyIf(finalPrefixVerification, this::validatePrefix);
+ }
+
+ private void validatePrefix(L8TestCompileResult compileResult) throws IOException {
+ InternalOptions options = new InternalOptions();
+ DesugaredLibrarySpecification specification =
+ DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+ this.desugaredLibrarySpecification,
+ options.dexItemFactory(),
+ options.reporter,
+ true,
+ apiLevel.getLevel());
+ Set<String> maintainTypeOrPrefix = specification.getMaintainTypeOrPrefixForTesting();
+ compileResult.inspect(
+ inspector ->
+ inspector.forAllClasses(
+ clazz -> {
+ String finalName = clazz.getFinalName();
+ if (finalName.startsWith("java.")) {
+ assertTrue(maintainTypeOrPrefix.stream().anyMatch(finalName::startsWith));
+ } else {
+ assertTrue(finalName.startsWith("j$."));
+ }
+ }));
+ }
+
+ private void assertNoUnexpectedDiagnosticMessages() {
+ TestDiagnosticMessages diagnosticsMessages = state.getDiagnosticsMessages();
+ diagnosticsMessages.assertNoErrors();
+ List<Diagnostic> warnings = diagnosticsMessages.getWarnings();
+ // We allow warnings exclusively when using the extended version for JDK11 testing.
+ // In this case, all warnings should apply to org.testng.Assert types which are not present
+ // in the vanilla desugared library.
+ // Vanilla desugared library compilation should have no warnings.
+ assertTrue(
+ warnings.isEmpty()
+ || warnings.stream()
+ .allMatch(warn -> warn.getDiagnosticMessage().contains("org.testng.Assert")));
+ List<Diagnostic> infos = diagnosticsMessages.getInfos();
+ // The rewriting confuses the generic signatures in some methods. Such signatures are never
+ // used by tools (they use the non library desugared version) and are stripped when compiling
+ // with R8 anyway.
+ // TODO(b/243483320): Investigate the Invalid signature.
+ assertTrue(
+ infos.isEmpty()
+ || infos.stream()
+ .allMatch(info -> info.getDiagnosticMessage().contains("Invalid signature ")));
}
private L8Command.Builder addProgramClassFileData(L8Command.Builder builder) {
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 4150236..52d84fc 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.errors.Unimplemented;
-import com.android.tools.r8.errors.UnsupportedFeatureDiagnostic;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index d948ad2..f31c1ae 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -619,7 +619,7 @@
ImmutableMap.<DexVm, String>builder()
.put(DexVm.ART_DEFAULT, "art")
.put(DexVm.ART_MASTER_HOST, "host/art-master")
- .put(DexVm.ART_13_0_0_HOST, "host/art-13-dev")
+ .put(DexVm.ART_13_0_0_HOST, "host/art-13.0.0")
.put(DexVm.ART_12_0_0_HOST, "host/art-12.0.0-beta4")
.put(DexVm.ART_10_0_0_HOST, "art-10.0.0")
.put(DexVm.ART_9_0_0_HOST, "art-9.0.0")
diff --git a/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java
index 46f8916..9095f727 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks.desugaredlib;
+import static com.android.tools.r8.ToolHelper.getDesugarLibConversions;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LATEST;
+
import com.android.tools.r8.L8TestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestState;
@@ -61,7 +64,7 @@
LibraryDesugaringSpecification spec =
new LibraryDesugaringSpecification(
"JDK11_Benchmark",
- ImmutableSet.of(undesugarJdkLib),
+ ImmutableSet.of(undesugarJdkLib, getDesugarLibConversions(LATEST)),
Paths.get("src/library_desugar/jdk11/desugar_jdk_libs.json"),
ImmutableSet.of(androidJar.getRoot(environment).resolve("android.jar")),
LibraryDesugaringSpecification.JDK11_DESCRIPTOR,
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 417a097..28f7855 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -5,12 +5,15 @@
package com.android.tools.r8.classmerging.horizontal;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.StartupProfileProvider;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupProfile;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
@@ -63,7 +66,23 @@
toDexType(startupClass, dexItemFactory))
.build())))
.build();
- StartupProfileProvider startupProfileProvider = startupProfile::serializeToString;
+ StartupProfileProvider startupProfileProvider =
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return startupProfile.serializeToString();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
options.getStartupOptions().setStartupProfileProvider(startupProfileProvider);
})
.addHorizontallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
index 33aab91..7a10578 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.compilerapi.mockdata.MockClass;
import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
+import com.android.tools.r8.compilerapi.mockdata.PostStartupMockClass;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -75,6 +76,10 @@
return MockClassWithAssertion.class;
}
+ public Class<?> getPostStartupMockClass() {
+ return PostStartupMockClass.class;
+ }
+
public Path getJava8RuntimeJar() {
return Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar");
}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index 14a04dc..6d12c20 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -17,7 +17,9 @@
import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest;
import com.android.tools.r8.compilerapi.mockdata.MockClass;
import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
+import com.android.tools.r8.compilerapi.mockdata.PostStartupMockClass;
import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest;
+import com.android.tools.r8.compilerapi.startupprofile.StartupProfileApiTest;
import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
import com.android.tools.r8.compilerapi.wrappers.CommandLineParserTest;
import com.android.tools.r8.compilerapi.wrappers.EnableMissingLibraryApiModelingTest;
@@ -41,16 +43,16 @@
CustomSourceFileTest.ApiTest.class,
AssertionConfigurationTest.ApiTest.class,
InputDependenciesTest.ApiTest.class,
- DesugarDependenciesTest.ApiTest.class);
-
- private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(
+ DesugarDependenciesTest.ApiTest.class,
GlobalSyntheticsTest.ApiTest.class,
CommandLineParserTest.ApiTest.class,
EnableMissingLibraryApiModelingTest.ApiTest.class,
AndroidPlatformBuildApiTest.ApiTest.class,
UnsupportedFeaturesDiagnosticApiTest.ApiTest.class);
+ private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
+ ImmutableList.of(StartupProfileApiTest.ApiTest.class);
+
private final TemporaryFolder temp;
public CompilerApiTestCollection(TemporaryFolder temp) {
@@ -74,7 +76,11 @@
@Override
public List<Class<?>> getAdditionalClassesForTests() {
- return ImmutableList.of(CompilerApiTest.class, MockClass.class, MockClassWithAssertion.class);
+ return ImmutableList.of(
+ CompilerApiTest.class,
+ MockClass.class,
+ MockClassWithAssertion.class,
+ PostStartupMockClass.class);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
index 0aae1dd..a2d7693 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollectionTest.java
@@ -36,7 +36,7 @@
/**
* If this test fails the test.jar needs to be regenerated and uploaded to cloud storage.
*
- * <p>See: {@code CompilerApiTestCollection.main} to regenerate.
+ * <p>See: {@link CompilerApiTestCollectionTest#main} to regenerate.
*
* <p>To preserve compatibility, make sure only to regenerate together with test changes and with
* NO changes to the compiler itself.
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java
index ee135e8..0ef8064 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestRunner.java
@@ -15,9 +15,8 @@
* Base runner for all compiler API tests.
*
* <p>Using this runner will automatically create an externalized variant of the test. That is
- * useful to more quickely ensure the test itself is not using resources that are not available.
- * Note however, that it does not prevent using non-kept code in the compilers unless testing with
- * r8lib!
+ * useful to more quickly ensure the test itself is not using resources that are not available. Note
+ * however, that it does not prevent using non-kept code in the compilers unless testing with r8lib!
*/
@RunWith(Parameterized.class)
public abstract class CompilerApiTestRunner extends TestBase {
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mockdata/PostStartupMockClass.java b/src/test/java/com/android/tools/r8/compilerapi/mockdata/PostStartupMockClass.java
new file mode 100644
index 0000000..6529022
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/mockdata/PostStartupMockClass.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.compilerapi.mockdata;
+
+// Class to use as data for the compilation.
+public class PostStartupMockClass {}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java
new file mode 100644
index 0000000..f8bc404
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/startupprofile/StartupProfileApiTest.java
@@ -0,0 +1,201 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.compilerapi.startupprofile;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.BiConsumer;
+import org.junit.Test;
+
+public class StartupProfileApiTest extends CompilerApiTestRunner {
+
+ private static final int FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX = 21;
+
+ public StartupProfileApiTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void testD8ArrayApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runD8(ApiTest::addStartupProfileProviderUsingArrayApi, programConsumer));
+ }
+
+ @Test
+ public void testD8CollectionApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runD8(ApiTest::addStartupProfileProviderUsingCollectionApi, programConsumer));
+ }
+
+ @Test
+ public void testR8ArrayApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runR8(ApiTest::addStartupProfileProviderUsingArrayApi, programConsumer));
+ }
+
+ @Test
+ public void testR8CollectionApi() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(
+ test,
+ programConsumer ->
+ test.runR8(ApiTest::addStartupProfileProviderUsingCollectionApi, programConsumer));
+ }
+
+ private void runTest(ApiTest test, ThrowingConsumer<ProgramConsumer, Exception> testRunner)
+ throws Exception {
+ Path output = temp.newFolder().toPath();
+ testRunner.accept(new DexIndexedConsumer.DirectoryConsumer(output));
+ assertThat(
+ new CodeInspector(output.resolve("classes.dex")).clazz(test.getMockClass()), isPresent());
+ // TODO(b/238173796): The PostStartupMockClass should be in classes2.dex.
+ assertThat(
+ new CodeInspector(output.resolve("classes.dex")).clazz(test.getPostStartupMockClass()),
+ isPresent());
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ private StartupProfileProvider getStartupProfileProvider() {
+ return new StartupProfileProvider() {
+ @Override
+ public String get() {
+ // Intentionally empty. All uses of this API should be rewritten to use getStartupProfile.
+ return "";
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder ->
+ startupClassBuilder.setClassReference(Reference.classFromClass(getMockClass())));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+ }
+
+ public void runD8(
+ BiConsumer<D8Command.Builder, StartupProfileProvider> startupProfileProviderInstaller,
+ ProgramConsumer programConsumer)
+ throws Exception {
+ D8Command.Builder commandBuilder =
+ D8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addClassProgramData(getBytesForClass(getPostStartupMockClass()), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setMinApiLevel(FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX)
+ .setProgramConsumer(programConsumer);
+ startupProfileProviderInstaller.accept(commandBuilder, getStartupProfileProvider());
+ D8.run(commandBuilder.build());
+ }
+
+ public void runR8(
+ BiConsumer<R8Command.Builder, StartupProfileProvider> startupProfileProviderInstaller,
+ ProgramConsumer programConsumer)
+ throws Exception {
+ R8Command.Builder commandBuilder =
+ R8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addClassProgramData(getBytesForClass(getPostStartupMockClass()), Origin.unknown())
+ .addProguardConfiguration(
+ Collections.singletonList("-keep class * { *; }"), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setMinApiLevel(FIRST_API_LEVEL_WITH_NATIVE_MULTIDEX)
+ .setProgramConsumer(programConsumer);
+ startupProfileProviderInstaller.accept(commandBuilder, getStartupProfileProvider());
+ R8.run(commandBuilder.build());
+ }
+
+ @Test
+ public void testD8ArrayApi() throws Exception {
+ runD8(ApiTest::addStartupProfileProviderUsingArrayApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingArrayApi(
+ D8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ StartupProfileProvider[] startupProfileProviders =
+ new StartupProfileProvider[] {startupProfileProvider};
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ @Test
+ public void testD8CollectionApi() throws Exception {
+ runD8(
+ ApiTest::addStartupProfileProviderUsingCollectionApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingCollectionApi(
+ D8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ Collection<StartupProfileProvider> startupProfileProviders =
+ Collections.singleton(startupProfileProvider);
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ @Test
+ public void testR8ArrayApi() throws Exception {
+ runR8(ApiTest::addStartupProfileProviderUsingArrayApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingArrayApi(
+ R8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ StartupProfileProvider[] startupProfileProviders =
+ new StartupProfileProvider[] {startupProfileProvider};
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+
+ @Test
+ public void testR8CollectionApi() throws Exception {
+ runR8(
+ ApiTest::addStartupProfileProviderUsingCollectionApi, DexIndexedConsumer.emptyConsumer());
+ }
+
+ private static void addStartupProfileProviderUsingCollectionApi(
+ R8Command.Builder commandBuilder, StartupProfileProvider startupProfileProvider) {
+ Collection<StartupProfileProvider> startupProfileProviders =
+ Collections.singleton(startupProfileProvider);
+ commandBuilder.addStartupProfileProviders(startupProfileProviders);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
index 102902a..8d84ddd 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
@@ -66,7 +66,7 @@
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addInnerClasses(getClass())
.addKeepMainRule(Executor.class)
- .setMode(compilationMode)
+ .overrideCompilationMode(compilationMode)
.addOptionsModification(
options -> {
// Devirtualizing is correcting the invalid member-rebinding.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
index d17c851..2838f2c 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryContentTest.java
@@ -6,8 +6,9 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
-import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -46,7 +47,7 @@
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
- getJdk8Jdk11(),
+ ImmutableList.of(JDK8, JDK11, JDK11_PATH),
ImmutableList.of(D8_L8DEBUG));
}
@@ -65,6 +66,7 @@
testForL8(parameters.getApiLevel())
.apply(libraryDesugaringSpecification::configureL8TestBuilder)
.compile()
+ .assertNoMessages()
.inspect(this::assertCorrect);
}
@@ -108,7 +110,9 @@
assertThat(
clazz.getOriginalName(),
CoreMatchers.anyOf(startsWith("j$."), startsWith("java."))));
- assertThat(inspector.clazz("j$.time.Clock"), isPresent());
+ if (parameters.getApiLevel().getLevel() <= AndroidApiLevel.R.getLevel()) {
+ assertThat(inspector.clazz("j$.time.Clock"), isPresent());
+ }
// Above N the following classes are removed instead of being desugared.
if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
assertFalse(inspector.clazz("j$.util.Optional").isPresent());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 3737814..c572169 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -117,8 +117,11 @@
"java.util.stream.LongStream"
+ " java.util.stream.LongStream.flatMap(java.util.function.LongFunction)");
+ // Missing conversions in JDK8 desugared library that are fixed in JDK11 desugared library.
private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_8 =
- ImmutableSet.of("java.util.Set java.util.stream.Collector.characteristics()");
+ ImmutableSet.of(
+ "java.util.Set java.util.stream.Collector.characteristics()",
+ "java.lang.Object java.lang.StackWalker.walk(java.util.function.Function)");
// TODO(b/238179854): Investigate how to fix these.
private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_PATH =
@@ -147,7 +150,7 @@
// TODO: parameterize to check both api<=23 as well as 23<api<26 for which the spec differs.
private final AndroidApiLevel minApi = AndroidApiLevel.B;
- private final AndroidApiLevel targetApi = AndroidApiLevel.S;
+ private final AndroidApiLevel targetApi = AndroidApiLevel.MASTER;
private Set<String> getMissingGenericTypeConversions() {
HashSet<String> missing = new HashSet<>(MISSING_GENERIC_TYPE_CONVERSION);
@@ -407,6 +410,11 @@
indirectWrappers.computeIfAbsent(t, k -> new HashSet<>()).add(reference);
}
};
+ if (clazz.getAccessFlags().isEnum()) {
+ // Enum are not really wrapped, instead, each instance is converted to the matching
+ // instance, so there is no need to wrap indirect parameters and return types.
+ continue;
+ }
clazz.forAllVirtualMethods(
method -> {
assertTrue(method.toString(), method.isPublic() || method.isProtected());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java
index c9c9a31..3309c38 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/DesugaredLibraryJDK11Undesugarer.java
@@ -16,6 +16,7 @@
import java.nio.file.Files;
import java.nio.file.OpenOption;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.zip.ZipEntry;
@@ -34,10 +35,21 @@
.put("java/lang/DesugarMath", "java/lang/Math")
.put("java/io/DesugarBufferedReader", "java/io/BufferedReader")
.put("java/io/DesugarInputStream", "java/io/InputStream")
- .put("wrapper/adapter/HybridFileSystemProvider", "java/adapter/HybridFileSystemProvider")
- .put("wrapper/adapter/HybridFileTypeDetector", "java/adapter/HybridFileTypeDetector")
+ .put("sun/misc/DesugarUnsafe", "jdk/internal/misc/Unsafe")
+ .put("wrapper/adapter/HybridFileSystemProvider", "sun/nio/fs/DefaultFileSystemProvider")
+ .put("wrapper/adapter/HybridFileTypeDetector", "sun/nio/fs/DefaultFileTypeDetector")
.build();
+ public static void main(String[] args) {
+ if (!Files.exists(Paths.get(args[0]))) {
+ throw new RuntimeException("Undesugarer source not found");
+ }
+ if (Files.exists(Paths.get(args[1]))) {
+ throw new RuntimeException("Undesugarer destination already exists");
+ }
+ generateUndesugaredJar(Paths.get(args[0]), Paths.get(args[1]));
+ }
+
public static Path undesugaredJarJDK11(Path undesugarFolder, Path jdk11Jar) {
String fileName = jdk11Jar.getFileName().toString();
String newFileName = fileName.substring(0, fileName.length() - 4) + "_undesugared.jar";
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StackWalkerTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StackWalkerTest.java
new file mode 100644
index 0000000..920798f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StackWalkerTest.java
@@ -0,0 +1,91 @@
+// 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.jdk11;
+
+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.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_MINIMAL;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+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 com.google.common.collect.ImmutableList;
+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 StackWalkerTest 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 + "stackwalker.jar");
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("[main]", "[frame2, frame1, main]");
+ private static final String EXPECTED_OUTPUT_R8 = StringUtils.lines("[main]", "[main]");
+ private static final String MAIN_CLASS = "stackwalker.Example";
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withDexRuntime(Version.MASTER)
+ .withApiLevel(AndroidApiLevel.B)
+ .withApiLevel(AndroidApiLevel.T)
+ .build(),
+ ImmutableList.of(JDK11_MINIMAL, JDK11, JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public StackWalkerTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ Assume.assumeTrue(
+ "Run only once",
+ compilationSpecification == D8_L8DEBUG && libraryDesugaringSpecification == JDK11);
+ // No desugared library, this should work.
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramFiles(INPUT_JAR)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testDesugaredLibrary() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .overrideLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.MASTER))
+ // Missing class java.lang.StackWalker$StackFrame.
+ .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(
+ compilationSpecification.isProgramShrink() ? EXPECTED_OUTPUT_R8 : EXPECTED_OUTPUT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StreamCollectorCharacteristicsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StreamCollectorCharacteristicsTest.java
index 56bfcd6..0d44dc3 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StreamCollectorCharacteristicsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/StreamCollectorCharacteristicsTest.java
@@ -51,7 +51,7 @@
}
@Test
- public void test() throws Exception {
+ public void test() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
.addProgramClasses(TestClass.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/TimeUnitTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/TimeUnitTest.java
new file mode 100644
index 0000000..54b3bc9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/TimeUnitTest.java
@@ -0,0 +1,67 @@
+// 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.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+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.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+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.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TimeUnitTest 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 + "timeunit.jar");
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("Nanos");
+ private static final String MAIN_CLASS = "timeunit.Example";
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withDexRuntimesStartingFromIncluding(Version.V13_0_0)
+ .withApiLevel(AndroidApiLevel.B)
+ .build(),
+ getJdk8Jdk11(),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public TimeUnitTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .overrideLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .addKeepMainRule(MAIN_CLASS)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index cd5c568..468494f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineTopLevelFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MultiAPILevelMachineDesugaredLibrarySpecificationJsonExporter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.origin.Origin;
@@ -41,6 +42,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
+import java.nio.file.Path;
import java.util.Map;
import org.junit.Assume;
import org.junit.Test;
@@ -110,6 +112,37 @@
}
@Test
+ public void testMultiLevelLegacyUsingMain() throws IOException {
+ LibraryDesugaringSpecification legacySpec = LibraryDesugaringSpecification.JDK8;
+ testMultiLevelUsingMain(legacySpec);
+ }
+
+ @Test
+ public void testMultiLevelHumanUsingMain() throws IOException {
+ LibraryDesugaringSpecification humanSpec = LibraryDesugaringSpecification.JDK11;
+ testMultiLevelUsingMain(humanSpec);
+ }
+
+ private void testMultiLevelUsingMain(LibraryDesugaringSpecification spec) throws IOException {
+ Assume.assumeTrue(ToolHelper.isLocalDevelopment());
+
+ Path output = temp.newFile().toPath();
+ DesugaredLibraryConverter.convertMultiLevelAnythingToMachineSpecification(
+ spec.getSpecification(), spec.getDesugarJdkLibs(), spec.getLibraryFiles(), output);
+
+ InternalOptions options = new InternalOptions();
+ MachineDesugaredLibrarySpecification machineSpecParsed =
+ new MachineDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(),
+ options.reporter,
+ true,
+ AndroidApiLevel.B.getLevel(),
+ new SyntheticNaming())
+ .parse(StringResource.fromFile(output));
+ assertFalse(machineSpecParsed.getRewriteType().isEmpty());
+ }
+
+ @Test
public void testSingleLevel() throws IOException {
Assume.assumeTrue(ToolHelper.isLocalDevelopment());
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 46d0b73..380f2d5 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
@@ -48,6 +48,7 @@
private String l8ExtraKeepRules = "";
private Consumer<InternalOptions> l8OptionModifier = ConsumerUtils.emptyConsumer();
private boolean l8FinalPrefixVerification = true;
+ private boolean overrideDefaultLibraryFiles = false;
private CustomLibrarySpecification customLibrarySpecification = null;
private TestingKeepRuleConsumer keepRuleConsumer = null;
@@ -67,7 +68,6 @@
private void setUp() {
builder
- .addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
.setMinApi(parameters.getApiLevel())
.setMode(compilationSpecification.getProgramCompilationMode());
LibraryDesugaringTestConfiguration.Builder libraryConfBuilder =
@@ -149,6 +149,17 @@
return this;
}
+ /**
+ * By default the compilation uses as library libraryDesugaringSpecification.getLibraryFiles(),
+ * which is android.jar at the required compilation api level. Use this Api to set different
+ * library files.
+ */
+ public DesugaredLibraryTestBuilder<T> overrideLibraryFiles(Path... files) {
+ overrideDefaultLibraryFiles = true;
+ builder.addLibraryFiles(files);
+ return this;
+ }
+
public DesugaredLibraryTestBuilder<T> addProgramFiles(Collection<Path> files) {
builder.addProgramFiles(files);
return this;
@@ -165,7 +176,12 @@
return this;
}
- public DesugaredLibraryTestBuilder<T> setMode(CompilationMode mode) {
+ /**
+ * By default the compilation uses libraryDesugaringSpecification.getProgramCompilationMode()
+ * which maps to the studio set-up: D8-debug, D8-release and R8-release. Use this Api to set a
+ * different compilation mode.
+ */
+ public DesugaredLibraryTestBuilder<T> overrideCompilationMode(CompilationMode mode) {
builder.setMode(mode);
return this;
}
@@ -311,13 +327,22 @@
return this;
}
+ private void prepareCompilation() {
+ if (overrideDefaultLibraryFiles) {
+ return;
+ }
+ builder.addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles());
+ }
+
public DesugaredLibraryTestCompileResult<T> compile() throws Exception {
+ prepareCompilation();
TestCompileResult<?, ? extends SingleTestRunResult<?>> compile = builder.compile();
return internalCompile(compile);
}
public DesugaredLibraryTestCompileResult<T> compileWithExpectedDiagnostics(
DiagnosticsConsumer consumer) throws Exception {
+ prepareCompilation();
TestCompileResult<?, ? extends SingleTestRunResult<?>> compile =
builder.compileWithExpectedDiagnostics(consumer);
return internalCompile(compile);
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
index 8b8118f..3ec019a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/LibraryDesugaringSpecification.java
@@ -118,7 +118,7 @@
new LibraryDesugaringSpecification(
"JDK11_PATH",
getUndesugaredJdk11LibJarForTesting(),
- "jdk11/desugar_jdk_libs_path.json",
+ "jdk11/desugar_jdk_libs_nio.json",
AndroidApiLevel.R,
JDK11_PATH_DESCRIPTOR,
LATEST);
@@ -128,7 +128,7 @@
new LibraryDesugaringSpecification(
"JDK11_PATH_ALTERNATIVE_3",
getUndesugaredJdk11LibJarForTesting(),
- "jdk11/desugar_jdk_libs_path_alternative_3.json",
+ "jdk11/desugar_jdk_libs_nio_alternative_3.json",
AndroidApiLevel.R,
JDK11_PATH_DESCRIPTOR,
LATEST);
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 e293b6e..a44f221 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -21,13 +21,13 @@
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.StartupProfileProvider;
import com.android.tools.r8.StringConsumer.FileConsumer;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.tracereferences.TraceReferencesCommand;
import com.android.tools.r8.tracereferences.TraceReferencesKeepRules;
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 89b37d0..c7211db 100644
--- a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
@@ -13,13 +13,16 @@
import com.android.tools.r8.ArchiveProgramResourceProvider;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.StartupProfileProvider;
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.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -236,8 +239,24 @@
Path outDirectory)
throws Exception {
StartupProfileProvider startupProfileProvider =
- StringResource.fromFile(chromeDirectory.resolve("startup.txt"))
- ::getStringWithRuntimeException;
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return StringResource.fromFile(chromeDirectory.resolve("startup.txt"))
+ .getStringWithRuntimeException();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+
buildR8(
testBuilder ->
testBuilder.addOptionsModification(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java
index 29ab7c9..4c3f07d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantInstanceStoreTest.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,11 +36,7 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- // See b/229706824.
- parameters.getDexRuntimeVersion().equals(Version.V13_0_0),
- r -> r.assertSuccessWithOutputLines("0"),
- r -> r.assertSuccessWithOutputLines("1"));
+ .assertSuccessWithOutputLines("1");
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java
index abf3664..60a0b34 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/ThrowingInstructionBeforeOtherwiseRedundantStaticStoreTest.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,11 +36,7 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- // See b/229706824.
- parameters.getDexRuntimeVersion().equals(Version.V13_0_0),
- r -> r.assertSuccessWithOutputLines("0"),
- r -> r.assertSuccessWithOutputLines("1"));
+ .assertSuccessWithOutputLines("1");
}
static class Main {
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 dfc31a7..5a3960e 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
@@ -18,7 +18,6 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
@@ -86,7 +85,7 @@
// Keep UtilKt#comma*Join*(). Let R8 optimize (inline) others, such as joinOf*(String).
.addKeepRules("-keep class **.UtilKt")
.addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepKotlinMetadata()
.compile()
.inspect(this::inspectMerged)
.writeToZip();
@@ -130,7 +129,7 @@
.addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }")
// Keep yet rename joinOf*(String).
.addKeepRules("-keepclassmembers,allowobfuscation class * { ** joinOf*(...); }")
- .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
+ .addKeepKotlinMetadata()
.compile()
.inspect(this::inspectRenamed)
.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
index 0602eb3..1273bfd 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
@@ -11,7 +11,11 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.references.Reference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -30,10 +34,18 @@
@Test
public void test() throws Exception {
- LIRCode code = LIRCode.builder().addConstNull().addConstInt(42).build();
-
- // State to keep track of position in the byte array as we don't expose this in the iterator.
- IntBox offset = new IntBox(0);
+ DexItemFactory factory = new DexItemFactory();
+ DexMethod method = factory.createMethod(Reference.methodFromDescriptor("LFoo;", "bar", "()V"));
+ LIRCode code =
+ LIRCode.builder(
+ method,
+ v -> {
+ throw new Unreachable();
+ })
+ .setMetadata(IRMetadata.unknown())
+ .addConstNull()
+ .addConstInt(42)
+ .build();
LIRIterator it = code.iterator();
@@ -43,23 +55,17 @@
assertSame(it, next);
it.accept(
- (int opcode, int operandOffset, int operandSize) -> {
- int headerSize = 1;
- assertEquals(LIROpcodes.ACONST_NULL, opcode);
- assertEquals(offset.get() + headerSize, operandOffset);
- assertEquals(0, operandSize);
- offset.increment(headerSize + operandSize);
+ insn -> {
+ assertEquals(LIROpcodes.ACONST_NULL, insn.getOpcode());
+ assertEquals(0, insn.getRemainingOperandSizeInBytes());
});
assertTrue(it.hasNext());
it.next();
it.accept(
- (int opcode, int operandOffset, int operandSize) -> {
- int headerSize = 2; // opcode + payload-size
- assertEquals(LIROpcodes.ICONST, opcode);
- assertEquals(offset.get() + headerSize, operandOffset);
- assertEquals(4, operandSize);
- offset.increment(headerSize + operandSize);
+ insn -> {
+ assertEquals(LIROpcodes.ICONST, insn.getOpcode());
+ assertEquals(4, insn.getRemainingOperandSizeInBytes());
});
assertFalse(it.hasNext());
@@ -69,10 +75,10 @@
for (LIRInstructionView view : code) {
if (oldView == null) {
oldView = view;
- view.accept((opcode, ignore1, ignore2) -> assertEquals(LIROpcodes.ACONST_NULL, opcode));
+ view.accept(insn -> assertEquals(LIROpcodes.ACONST_NULL, insn.getOpcode()));
} else {
assertSame(oldView, view);
- view.accept((opcode, ignore1, ignore2) -> assertEquals(LIROpcodes.ICONST, opcode));
+ view.accept(insn -> assertEquals(LIROpcodes.ICONST, insn.getOpcode()));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
new file mode 100644
index 0000000..a169bfc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
@@ -0,0 +1,74 @@
+// 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.lightir;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class LIRRoundtripTest extends TestBase {
+
+ static class TestClass {
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public LIRRoundtripTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testRoundtrip() throws Exception {
+ testForD8(parameters.getBackend())
+ .release()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(TestClass.class)
+ .addOptionsModification(
+ o -> {
+ o.testing.forceIRForCfToCfDesugar = true;
+ o.testing.roundtripThroughLIR = true;
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testRoundtripDebug() throws Exception {
+ testForD8(parameters.getBackend())
+ .debug()
+ .setMinApi(AndroidApiLevel.B)
+ .addProgramClasses(TestClass.class)
+ .addOptionsModification(
+ o -> {
+ o.testing.forceIRForCfToCfDesugar = true;
+ o.testing.roundtripThroughLIR = true;
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
index bbc6c01..c91527d 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
@@ -4,13 +4,14 @@
package com.android.tools.r8.mappingcompose;
-import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposeException;
import com.android.tools.r8.naming.MappingComposer;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
@@ -31,18 +32,20 @@
private static final String mappingFoo =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '1.0' }", "com.foo -> a:");
+ "# { id: 'com.android.tools.r8.mapping', version: '2.1' }", "com.foo -> a:");
private static final String mappingBar =
- StringUtils.unixLines("# { id: 'com.android.tools.r8.mapping', version: '2.0' }", "a -> b:");
- private static final String mappingResult =
- StringUtils.unixLines(
- "# {'id':'com.android.tools.r8.mapping','version':'2.0'}", "com.foo -> b:");
+ StringUtils.unixLines("# { id: 'com.android.tools.r8.mapping', version: '2.2' }", "a -> b:");
@Test
public void testCompose() throws Exception {
ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
- String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
- assertEquals(mappingResult, doubleToSingleQuote(composed));
+ MappingComposeException mappingComposeException =
+ assertThrows(
+ MappingComposeException.class,
+ () -> MappingComposer.compose(mappingForFoo, mappingForBar));
+ assertEquals(
+ "Composition of mapping files supported from map version 2.1.",
+ mappingComposeException.getMessage());
}
}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
index 98f1cd5..8ba1011 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.mappingcompose;
import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -26,12 +26,12 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ return getTestParameters().withNoneRuntime().build();
}
private static final String mappingFoo =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
+ "# { id: 'com.android.tools.r8.mapping', version: '2.1' }",
"outline.Class -> a:",
" 1:2:int some.inlinee():75:76 -> a",
" 1:2:int outline():0 -> a",
@@ -41,24 +41,27 @@
" 5:5:int foo.bar.baz.outlineCaller(int):98:98 -> s",
" 5:5:int outlineCaller(int):24 -> s",
" 27:27:int outlineCaller(int):0:0 -> s",
- " # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '1': 4, '2': 5 } }");
- private static final String mappingBar =
+ " # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '1': 4, '2': 5 },"
+ + " 'outline':'La;a()I' }");
+ private static final String mappingBar = StringUtils.unixLines("a -> b:");
+ private static final String mappingBaz =
StringUtils.unixLines(
- "a -> b:",
+ "b -> c:",
" 4:5:int a():1:2 -> m",
- "x -> c:",
+ "x -> y:",
" 8:9:int s(int):4:5 -> o",
" 42:42:int s(int):27:27 -> o");
private static final String mappingResult =
StringUtils.unixLines(
- "# {'id':'com.android.tools.r8.mapping','version':'2.0'}",
- "outline.Callsite -> c:",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.1'}",
+ "outline.Callsite -> y:",
" 8:8:int outlineCaller(int):23 -> o",
" 9:9:int foo.bar.baz.outlineCaller(int):98:98 -> o",
" 9:9:int outlineCaller(int):24 -> o",
" 42:42:int outlineCaller(int):0:0 -> o",
- " # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '4': 8, '5': 9 } }",
- "outline.Class -> b:",
+ " #"
+ + " {'id':'com.android.tools.r8.outlineCallsite','positions':{'4':8,'5':9},'outline':'Lc;m()I'}",
+ "outline.Class -> c:",
" 4:5:int some.inlinee():75:76 -> m",
" 4:5:int outline():0 -> m",
" # {'id':'com.android.tools.r8.outline'}");
@@ -67,8 +70,8 @@
public void testCompose() throws Exception {
ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
- String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
- // TODO(b/242682464): Update this test when the link has been added to the mapping information.
- assertNotEquals(mappingResult, doubleToSingleQuote(composed));
+ ClassNameMapper mappingForBaz = ClassNameMapper.mapperFromString(mappingBaz);
+ String composed = MappingComposer.compose(mappingForFoo, mappingForBar, mappingForBaz);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
}
}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposePreambleCommentTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposePreambleCommentTest.java
new file mode 100644
index 0000000..98a070d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposePreambleCommentTest.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.mappingcompose;
+
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposer;
+import com.android.tools.r8.utils.StringUtils;
+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 ComposePreambleCommentTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ private static final String mappingFoo =
+ StringUtils.unixLines(
+ "# This is a multi line ",
+ "# preamble, with custom information",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.1'}",
+ "# foo bar",
+ "# {'id':'this is invalid json due to no comma' neededInfo:'foobar' }",
+ "com.A -> a:",
+ "# This is a comment that will be removed.",
+ "com.B -> c:");
+ private static final String mappingBar =
+ StringUtils.unixLines(
+ "# Additional multiline ",
+ "# second preamble, with custom information",
+ "# {'id':'bar',neededInfo:'barbaz'}",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.1'}",
+ "a -> b:",
+ "# This is another comment that will be removed.",
+ "c -> d:");
+ private static final String mappingResult =
+ StringUtils.unixLines(
+ "# This is a multi line ",
+ "# preamble, with custom information",
+ "# foo bar",
+ "# {'id':'this is invalid json due to no comma' neededInfo:'foobar' }",
+ "# Additional multiline ",
+ "# second preamble, with custom information",
+ "# {'id':'bar',neededInfo:'barbaz'}",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.1'}",
+ "com.A -> b:",
+ "com.B -> d:");
+
+ @Test
+ public void testCompose() throws Exception {
+ ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromStringWithPreamble(mappingFoo);
+ ClassNameMapper mappingForBar = ClassNameMapper.mapperFromStringWithPreamble(mappingBar);
+ String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
index cae1f89..f8b6ebc 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
@@ -31,7 +31,7 @@
private static final String mappingFoo =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
+ "# { id: 'com.android.tools.r8.mapping', version: '2.1' }",
"my.CustomException -> a:",
"foo.Bar -> x:",
" 4:4:void other.Class.inlinee():23:23 -> a",
@@ -40,13 +40,13 @@
+ "conditions: ['throws(La;)'], actions: ['removeInnerFrames(1)'] }");
private static final String mappingBar =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
+ "# { id: 'com.android.tools.r8.mapping', version: '2.1' }",
"a -> b:",
"x -> c:",
" 8:8:void a(Other.Class):4:4 -> m");
private static final String mappingResult =
StringUtils.unixLines(
- "# {'id':'com.android.tools.r8.mapping','version':'2.0'}",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.1'}",
"foo.Bar -> c:",
" 8:8:void other.Class.inlinee():23:23 -> m",
" 8:8:void caller(other.Class):7 -> m",
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
index 20585e9..e91eed6 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
@@ -31,7 +31,7 @@
private static final String mappingFoo =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
+ "# { id: 'com.android.tools.r8.mapping', version: '2.1' }",
"com.foo -> a:",
"# { id: 'com.android.tools.r8.synthesized' }",
" int f -> a",
@@ -40,7 +40,7 @@
" # { id: 'com.android.tools.r8.synthesized' }");
private static final String mappingBar =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
+ "# { id: 'com.android.tools.r8.mapping', version: '2.1' }",
"a -> b:",
" int a -> b",
"com.bar -> c:",
@@ -49,7 +49,7 @@
" # { id: 'com.android.tools.r8.synthesized' }");
private static final String mappingResult =
StringUtils.unixLines(
- "# {'id':'com.android.tools.r8.mapping','version':'1.0'}",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.1'}",
"com.bar -> c:",
"# {'id':'com.android.tools.r8.synthesized'}",
" void bar() -> a",
diff --git a/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java b/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
index 5a4db90..d1991df 100644
--- a/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
@@ -38,7 +38,8 @@
CharSource.wrap(StringUtils.joinLines(lines)).openBufferedStream(),
diagnosticsHandler,
false,
- true);
+ true,
+ false);
}
private static ClassNameMapper read(String... lines) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderArgumentsTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderArgumentsTest.java
index 6c7156d..8e91ae1 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderArgumentsTest.java
@@ -65,9 +65,6 @@
diagnosticMessage(containsString("Could not locate 'id'")),
diagnosticPosition(positionLine(4))),
allOf(
- diagnosticMessage(containsString("Not valid JSON")),
- diagnosticPosition(positionLine(6))),
- allOf(
diagnosticMessage(containsString("Could not find a handler for bar")),
diagnosticPosition(positionLine(8)))))
.assertAllInfosMatch(diagnosticType(MappingInformationDiagnostics.class));
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
new file mode 100644
index 0000000..3349571
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfRuleWithFieldAnnotation.java
@@ -0,0 +1,94 @@
+// 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.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class IfRuleWithFieldAnnotation extends TestBase {
+
+ static final String EXPECTED = "foobar";
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public IfRuleWithFieldAnnotation(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ 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"
+ + "}")
+ .setMinApi(parameters.getApiLevel())
+ .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);
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface SerializedName {}
+
+ public static class Foo {
+ public static Object object;
+
+ public static void main(String[] args) {
+ callOnBar(args);
+ System.out.println("foobar");
+ }
+
+ private static void callOnBar(String[] args) {
+ if (System.currentTimeMillis() == 0) {
+ int i = ((Bar) instantiateObject()).value;
+ System.out.println(i);
+ }
+ }
+
+ private static Object instantiateObject() {
+ try {
+ return Class.forName("class" + System.currentTimeMillis()).newInstance();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ }
+
+ public static class Bar {
+ @SerializedName public int value;
+
+ public Bar(int value) {
+ this.value = 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
new file mode 100644
index 0000000..6eff67b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/InliningOutOfStartupPartitionTest.java
@@ -0,0 +1,91 @@
+// 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;
+
+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 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.utils.StartupTestingUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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 InliningOutOfStartupPartitionTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimesAndAllApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ List<StartupItem<ClassReference, MethodReference, ?>> startupItems =
+ ImmutableList.of(
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(Main.class))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
+ .build());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .apply(
+ testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupItems))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+ 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());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ PostStartupClass.runPostStartup();
+ }
+
+ static void postStartupMethod() {
+ System.out.println("Hello, world!");
+ }
+ }
+
+ static class PostStartupClass {
+
+ static void runPostStartup() {
+ Main.postStartupMethod();
+ }
+ }
+}
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 ec7e59f..ad43216 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -10,11 +10,11 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.StartupProfileProvider;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.experimental.startup.StartupConfigurationParser;
import com.android.tools.r8.experimental.startup.StartupItem;
import com.android.tools.r8.experimental.startup.StartupProfile;
@@ -22,9 +22,12 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ClassReferenceUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
@@ -157,7 +160,23 @@
builder.addStartupItem(
convertStartupItemToDex(startupItem, dexItemFactory))))
.build();
- StartupProfileProvider startupProfileProvider = startupProfile::serializeToString;
+ StartupProfileProvider startupProfileProvider =
+ new StartupProfileProvider() {
+ @Override
+ public String get() {
+ return startupProfile.serializeToString();
+ }
+
+ @Override
+ public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+ throw new Unimplemented();
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
options.getStartupOptions().setStartupProfileProvider(startupProfileProvider);
});
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index e29b4e8..aeb419e 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -281,6 +281,10 @@
return clazz(Reference.classFromTypeName(name));
}
+ public ClassNameMapper getMapping() {
+ return mapping;
+ }
+
// Simple wrapper to more easily change the implementation for retracing subjects.
// This should in time be replaced by use of the Retrace API.
public static class MappingWrapper {
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 d9e0fb6..040d696 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-2b8f463bcc995898411329d9cd55892289d5763e
\ No newline at end of file
+98dc48c246bd0855133e138c2cd2b5835cf862cf
\ No newline at end of file
diff --git a/tools/apk_masseur.py b/tools/apk_masseur.py
index 7f3932b..2bcea93 100755
--- a/tools/apk_masseur.py
+++ b/tools/apk_masseur.py
@@ -75,7 +75,7 @@
with utils.ChangedWorkingDirectory(processed_out, quiet=quiet):
dex_files = glob.glob('*.dex')
resource_files = glob.glob(resources) if resources else []
- cmd = ['zip', '-u', '-9', processed_apk] + dex_files + resource_files
+ cmd = ['zip', '-u', '-0', processed_apk] + dex_files + resource_files
utils.RunCmd(cmd, quiet=quiet, logging=logging)
return processed_apk
diff --git a/tools/apk_utils.py b/tools/apk_utils.py
index 906284e..f126f91 100755
--- a/tools/apk_utils.py
+++ b/tools/apk_utils.py
@@ -56,7 +56,7 @@
zipalign_path = (
'zipalign' if 'build_tools' in os.environ.get('PATH')
else os.path.join(utils.getAndroidBuildTools(), 'zipalign'))
- cmd = [zipalign_path, '-f', '4', apk, aligned_apk]
+ cmd = [zipalign_path, '-f', '-p', '4', apk, aligned_apk]
utils.RunCmd(cmd, quiet=True, logging=False)
return aligned_apk
diff --git a/tools/archive.py b/tools/archive.py
index 652fbe9..6a39594 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -31,6 +31,9 @@
result.add_option('--dry-run-output', '--dry_run_output',
help='Output directory for \'build only, no upload\'.',
type="string", action="store")
+ result.add_option('--skip-gradle-build', '--skip_gradle_build',
+ help='Skip Gradle build. Can only be used for local testing.',
+ default=False, action='store_true')
return result.parse_args()
def GetVersion():
@@ -112,6 +115,8 @@
not os.path.isdir(options.dry_run_output))):
raise Exception(options.dry_run_output
+ ' does not exist or is not a directory')
+ if (options.skip_gradle_build and not options.dry_run):
+ raise Exception('Using --skip-gradle-build only supported with --dry-run')
if utils.is_bot() and not utils.IsWindows():
SetRLimitToMax()
@@ -134,27 +139,35 @@
version_writer.write('version-file.version.code=1\n')
# Create maven release which uses a build that exclude dependencies.
- create_maven_release.generate_r8_maven_zip(utils.MAVEN_ZIP, version_file=version_file)
create_maven_release.generate_r8_maven_zip(
- utils.MAVEN_ZIP_LIB, is_r8lib=True, version_file=version_file)
+ utils.MAVEN_ZIP,
+ version_file=version_file,
+ skip_gradle_build=options.skip_gradle_build)
+ create_maven_release.generate_r8_maven_zip(
+ utils.MAVEN_ZIP_LIB,
+ is_r8lib=True,
+ version_file=version_file,
+ skip_gradle_build=options.skip_gradle_build)
# Generate and copy a full build without dependencies.
- gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
+ if (not options.skip_gradle_build):
+ gradle.RunGradleExcludeDeps([utils.R8, utils.R8_SRC])
shutil.copyfile(utils.R8_JAR, utils.R8_FULL_EXCLUDE_DEPS_JAR)
# Ensure all archived artifacts has been built before archiving.
# The target tasks postfixed by 'lib' depend on the actual target task so
# building it invokes the original task first.
# The '-Pno_internal' flag is important because we generate the lib based on uses in tests.
- gradle.RunGradle([
- utils.R8,
- utils.R8LIB,
- utils.R8LIB_NO_DEPS,
- utils.R8RETRACE,
- utils.R8RETRACE_NO_DEPS,
- utils.LIBRARY_DESUGAR_CONVERSIONS,
- '-Pno_internal'
- ])
+ if (not options.skip_gradle_build):
+ gradle.RunGradle([
+ utils.R8,
+ utils.R8LIB,
+ utils.R8LIB_NO_DEPS,
+ utils.R8RETRACE,
+ utils.R8RETRACE_NO_DEPS,
+ utils.LIBRARY_DESUGAR_CONVERSIONS,
+ '-Pno_internal'
+ ])
# Create maven release of the desuage_jdk_libs configuration. This require
# an r8.jar with dependencies to have been built.
@@ -169,6 +182,22 @@
utils.DESUGAR_IMPLEMENTATION_JDK11,
utils.LIBRARY_DESUGAR_CONVERSIONS_LEGACY_ZIP)
+ create_maven_release.generate_desugar_configuration_maven_zip(
+ utils.DESUGAR_CONFIGURATION_JDK11_MINIMAL_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION_JDK11_MINIMAL,
+ utils.DESUGAR_IMPLEMENTATION_JDK11,
+ utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP)
+ create_maven_release.generate_desugar_configuration_maven_zip(
+ utils.DESUGAR_CONFIGURATION_JDK11_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION_JDK11,
+ utils.DESUGAR_IMPLEMENTATION_JDK11,
+ utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP)
+ create_maven_release.generate_desugar_configuration_maven_zip(
+ utils.DESUGAR_CONFIGURATION_JDK11_NIO_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION_JDK11_NIO,
+ utils.DESUGAR_IMPLEMENTATION_JDK11,
+ utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP)
+
version = GetVersion()
is_main = IsMain(version)
if is_main:
@@ -200,6 +229,9 @@
utils.DESUGAR_CONFIGURATION_MAVEN_ZIP,
utils.DESUGAR_CONFIGURATION_JDK11_LEGACY,
utils.DESUGAR_CONFIGURATION_JDK11_LEGACY_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION_JDK11_MINIMAL_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION_JDK11_MAVEN_ZIP,
+ utils.DESUGAR_CONFIGURATION_JDK11_NIO_MAVEN_ZIP,
utils.GENERATED_LICENSE,
]:
file_name = os.path.basename(file)
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 5298435..1762c2c 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -19,7 +19,10 @@
# repository to fetch the artifact com.android.tools:desugar_jdk_libs:1.0.0
import archive
+import defines
import git_utils
+import gradle
+import hashlib
import jdk
import optparse
import os
@@ -28,17 +31,59 @@
import subprocess
import sys
import utils
+import zipfile
VERSION_FILE_JDK8 = 'VERSION.txt'
+VERSION_FILE_JDK11_LEGACY = 'VERSION_JDK11_LEGACY.txt'
+VERSION_FILE_JDK11_MINIMAL = 'VERSION_JDK11_MINIMAL.txt'
VERSION_FILE_JDK11 = 'VERSION_JDK11.txt'
-LIBRARY_NAME = 'desugar_jdk_libs'
+VERSION_FILE_JDK11_NIO = 'VERSION_JDK11_NIO.txt'
+
+VERSION_MAP = {
+ 'jdk8': VERSION_FILE_JDK8,
+ 'jdk11_legacy': VERSION_FILE_JDK11_LEGACY,
+ 'jdk11_minimal': VERSION_FILE_JDK11_MINIMAL,
+ 'jdk11': VERSION_FILE_JDK11,
+ 'jdk11_nio': VERSION_FILE_JDK11_NIO
+}
+
+GITHUB_REPRO = 'desugar_jdk_libs'
+
+BASE_LIBRARY_NAME = 'desugar_jdk_libs'
+
+LIBRARY_NAME_MAP = {
+ 'jdk8': BASE_LIBRARY_NAME,
+ 'jdk11_legacy': BASE_LIBRARY_NAME,
+ 'jdk11_minimal': BASE_LIBRARY_NAME + '_minimal',
+ 'jdk11': BASE_LIBRARY_NAME,
+ 'jdk11_nio': BASE_LIBRARY_NAME + '_nio'
+}
+
+MAVEN_RELEASE_TARGET_MAP = {
+ 'jdk8': 'maven_release',
+ 'jdk11_legacy': 'maven_release_jdk11_legacy',
+ 'jdk11_minimal': 'maven_release_jdk11_minimal',
+ 'jdk11': 'maven_release_jdk11',
+ 'jdk11_nio': 'maven_release_jdk11_nio'
+}
+
+MAVEN_RELEASE_ZIP = {
+ 'jdk8': BASE_LIBRARY_NAME + '.zip',
+ 'jdk11_legacy': BASE_LIBRARY_NAME + '_jdk11_legacy.zip',
+ 'jdk11_minimal': BASE_LIBRARY_NAME + '_jdk11_minimal.zip',
+ 'jdk11': BASE_LIBRARY_NAME + '_jdk11.zip',
+ 'jdk11_nio': BASE_LIBRARY_NAME + '_jdk11_nio.zip'
+}
+
def ParseOptions(argv):
result = optparse.OptionParser()
result.add_option('--variant',
- help='.',
- choices = ['jdk8', 'jdk11'],
- default='jdk11')
+ help="Variant(s) to build",
+ metavar=('<variants(s)>'),
+ choices=['jdk8', 'jdk11_legacy', 'jdk11_minimal', 'jdk11', 'jdk11_nio'],
+ default=[],
+ action='append')
result.add_option('--dry-run', '--dry_run',
help='Running on bot, use third_party dependency.',
default=False,
@@ -89,7 +134,7 @@
def CloneDesugaredLibrary(github_account, checkout_dir):
git_utils.GitClone(
'https://github.com/'
- + github_account + '/' + LIBRARY_NAME, checkout_dir)
+ + github_account + '/' + GITHUB_REPRO, checkout_dir)
def GetJavaEnv():
java_env = dict(os.environ, JAVA_HOME = jdk.GetJdk11Home())
@@ -98,9 +143,11 @@
return java_env
-def BuildDesugaredLibrary(checkout_dir, variant):
- if (variant != 'jdk8' and variant != 'jdk11'):
- raise Exception('Variant ' + variant + 'is not supported')
+def BuildDesugaredLibrary(checkout_dir, variant, version = None):
+ if not variant in MAVEN_RELEASE_TARGET_MAP:
+ raise Exception('Variant ' + variant + ' is not supported')
+ if variant != 'jdk8' and variant != 'jdk11_legacy' and version is None:
+ raise Exception('Variant ' + variant + ' require version for undesugaring')
with utils.ChangedWorkingDirectory(checkout_dir):
bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin', 'bazel')
cmd = [
@@ -109,7 +156,7 @@
'build',
'--spawn_strategy=local',
'--verbose_failures',
- 'maven_release' + ('_jdk11' if variant == 'jdk11' else '')]
+ MAVEN_RELEASE_TARGET_MAP[variant]]
utils.PrintCmd(cmd)
subprocess.check_call(cmd, env=GetJavaEnv())
cmd = [bazel, 'shutdown']
@@ -122,19 +169,136 @@
library_jar = os.path.join(
checkout_dir, 'bazel-bin', 'src', 'share', 'classes', 'java', 'libjava.jar')
else:
+ # All JDK11 variants use the same library code.
library_jar = os.path.join(
checkout_dir, 'bazel-bin', 'jdk11', 'src', 'd8_java_base_selected_with_addon.jar')
maven_zip = os.path.join(
checkout_dir,
'bazel-bin',
- LIBRARY_NAME + ('_jdk11' if variant == 'jdk11' else '') +'.zip')
- return (library_jar, maven_zip)
+ MAVEN_RELEASE_ZIP[variant])
+ if variant != 'jdk8' and variant != 'jdk11_legacy':
+ # The undesugaring is temporary...
+ undesugared_maven_zip = os.path.join(checkout_dir, 'undesugared_maven')
+ Undesugar(variant, maven_zip, version, undesugared_maven_zip)
+ undesugared_maven_zip = os.path.join(checkout_dir, 'undesugared_maven.zip')
+ return (library_jar, undesugared_maven_zip)
+ else:
+ return (library_jar, maven_zip)
+
+def hash_for(file, hash):
+ with open(file, 'rb') as f:
+ while True:
+ # Read chunks of 1MB
+ chunk = f.read(2 ** 20)
+ if not chunk:
+ break
+ hash.update(chunk)
+ return hash.hexdigest()
+
+def write_md5_for(file):
+ hexdigest = hash_for(file, hashlib.md5())
+ with (open(file + '.md5', 'w')) as file:
+ file.write(hexdigest)
+
+def write_sha1_for(file):
+ hexdigest = hash_for(file, hashlib.sha1())
+ with (open(file + '.sha1', 'w')) as file:
+ file.write(hexdigest)
+
+def Undesugar(variant, maven_zip, version, undesugared_maven_zip):
+ gradle.RunGradle(['testJar', 'repackageTestDeps'])
+ with utils.TempDir() as tmp:
+ with zipfile.ZipFile(maven_zip, 'r') as zip_ref:
+ zip_ref.extractall(tmp)
+ desugar_jdk_libs_jar = os.path.join(
+ tmp,
+ 'com',
+ 'android',
+ 'tools',
+ LIBRARY_NAME_MAP[variant],
+ version,
+ '%s-%s.jar' % (LIBRARY_NAME_MAP[variant], version))
+ print(desugar_jdk_libs_jar)
+ undesugared_jar = os.path.join(tmp, 'undesugared.jar')
+ buildLibs = os.path.join(defines.REPO_ROOT, 'build', 'libs')
+ cmd = [jdk.GetJavaExecutable(),
+ '-cp',
+ '%s:%s:%s' % (os.path.join(buildLibs, 'r8_with_deps.jar'), os.path.join(buildLibs, 'r8tests.jar'), os.path.join(buildLibs, 'test_deps_all.jar')),
+ 'com.android.tools.r8.desugar.desugaredlibrary.jdk11.DesugaredLibraryJDK11Undesugarer',
+ desugar_jdk_libs_jar,
+ undesugared_jar]
+ print(cmd)
+ try:
+ output = subprocess.check_output(cmd, stderr = subprocess.STDOUT).decode('utf-8')
+ except subprocess.CalledProcessError as e:
+ print(e)
+ print(e.output)
+ raise e
+ print(output)
+ # Copy the undesugared jar into place and update the checksums.
+ shutil.copyfile(undesugared_jar, desugar_jdk_libs_jar)
+ write_md5_for(desugar_jdk_libs_jar)
+ write_sha1_for(desugar_jdk_libs_jar)
+ shutil.make_archive(undesugared_maven_zip, 'zip', tmp)
+ print(undesugared_maven_zip)
+ output = subprocess.check_output(['ls', '-l', os.path.dirname(undesugared_maven_zip)], stderr = subprocess.STDOUT).decode('utf-8')
+ print(output)
def MustBeExistingDirectory(path):
if (not os.path.exists(path) or not os.path.isdir(path)):
raise Exception(path + ' does not exist or is not a directory')
+def BuildAndUpload(options, variant):
+ if options.build_only:
+ with utils.TempDir() as checkout_dir:
+ CloneDesugaredLibrary(options.github_account, checkout_dir)
+ (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir, variant)
+ shutil.copyfile(
+ library_jar,
+ os.path.join(options.build_only, os.path.basename(library_jar)))
+ shutil.copyfile(
+ maven_zip,
+ os.path.join(options.build_only, os.path.basename(maven_zip)))
+ return
+
+ # Only handling versioned desugar_jdk_libs.
+ is_main = False
+
+ with utils.TempDir() as checkout_dir:
+ CloneDesugaredLibrary(options.github_account, checkout_dir)
+ version = GetVersion(os.path.join(checkout_dir, VERSION_MAP[variant]))
+
+ destination = archive.GetVersionDestination(
+ 'gs://', LIBRARY_NAME_MAP[variant] + '/' + version, is_main)
+ if utils.cloud_storage_exists(destination) and not options.dry_run:
+ raise Exception(
+ 'Target archive directory %s already exists' % destination)
+
+ (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir, variant, version)
+
+ storage_path = LIBRARY_NAME_MAP[variant] + '/' + version
+ # Upload the jar file with the library.
+ destination = archive.GetUploadDestination(
+ storage_path, LIBRARY_NAME_MAP[variant] + '.jar', is_main)
+ Upload(options, library_jar, storage_path, destination, is_main)
+
+ # Upload the maven zip file with the library.
+ destination = archive.GetUploadDestination(
+ storage_path, MAVEN_RELEASE_ZIP[variant], is_main)
+ Upload(options, maven_zip, storage_path, destination, is_main)
+
+ # 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,
+ is_main)
+ if options.dry_run:
+ print('Dry run, not actually creating maven repo')
+ else:
+ utils.upload_file_to_cloud_storage(library_jar, maven_destination)
+ print('Maven repo root available at: %s' % archive.GetMavenUrl(is_main))
+
def Main(argv):
(options, args) = ParseOptions(argv)
if (len(args) > 0):
@@ -154,58 +318,8 @@
utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE)
- if options.build_only:
- with utils.TempDir() as checkout_dir:
- CloneDesugaredLibrary(options.github_account, checkout_dir)
- (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir, options.variant)
- shutil.copyfile(
- library_jar,
- os.path.join(options.build_only, os.path.basename(library_jar)))
- shutil.copyfile(
- maven_zip,
- os.path.join(options.build_only, os.path.basename(maven_zip)))
- return
-
- # Only handling versioned desugar_jdk_libs.
- is_main = False
-
- with utils.TempDir() as checkout_dir:
- CloneDesugaredLibrary(options.github_account, checkout_dir)
- version = GetVersion(
- os.path.join(
- checkout_dir,
- VERSION_FILE_JDK11 if options.variant == 'jdk11' else VERSION_FILE_JDK8))
-
- destination = archive.GetVersionDestination(
- 'gs://', LIBRARY_NAME + '/' + version, is_main)
- if utils.cloud_storage_exists(destination) and not options.dry_run:
- raise Exception(
- 'Target archive directory %s already exists' % destination)
-
- (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir, options.variant)
-
- storage_path = LIBRARY_NAME + '/' + version
- # Upload the jar file with the library.
- destination = archive.GetUploadDestination(
- storage_path, LIBRARY_NAME + '.jar', is_main)
- Upload(options, library_jar, storage_path, destination, is_main)
-
- # Upload the maven zip file with the library.
- destination = archive.GetUploadDestination(
- storage_path, LIBRARY_NAME + '.zip', is_main)
- Upload(options, maven_zip, storage_path, destination, is_main)
-
- # 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,
- is_main)
- if options.dry_run:
- print('Dry run, not actually creating maven repo')
- else:
- utils.upload_file_to_cloud_storage(library_jar, maven_destination)
- print('Maven repo root available at: %s' % archive.GetMavenUrl(is_main))
-
+ for v in options.variant:
+ BuildAndUpload(options, v)
if __name__ == '__main__':
sys.exit(Main(sys.argv[1:]))
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 33e6f75..fc7eafb 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -81,7 +81,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.android.tools</groupId>
- <artifactId>desugar_jdk_libs_configuration</artifactId>
+ <artifactId>$artifactId</artifactId>
<version>$version</version>
<name>D8 configuration to desugar desugar_jdk_libs</name>
<description>
@@ -260,11 +260,19 @@
return result
def write_default_r8_pom_file(pom_file, version):
- write_pom_file(R8_POMTEMPLATE, pom_file, version, generate_dependencies(), '')
+ write_pom_file(R8_POMTEMPLATE, pom_file, version, dependencies=generate_dependencies())
-def write_pom_file(template, pom_file, version, dependencies='', library_licenses=''):
- version_pom = template.substitute(
- version=version, dependencies=dependencies, library_licenses=library_licenses)
+def write_pom_file(
+ template, pom_file, version, artifact_id=None, dependencies='', library_licenses=''):
+ version_pom = (
+ template.substitute(
+ artifactId=artifact_id,
+ version=version,
+ dependencies=dependencies,
+ library_licenses=library_licenses)
+ if artifact_id else
+ template.substitute(
+ version=version, dependencies=dependencies, library_licenses=library_licenses))
with open(pom_file, 'w') as file:
file.write(version_pom)
@@ -309,12 +317,13 @@
base_no_zip = out[0:len(out)-4]
make_archive(base_no_zip, 'zip', tmp_dir)
-def generate_r8_maven_zip(out, is_r8lib=False, version_file=None):
+def generate_r8_maven_zip(out, is_r8lib=False, version_file=None, skip_gradle_build=False):
# Build the R8 no deps artifact.
- if not is_r8lib:
- gradle.RunGradleExcludeDeps([utils.R8])
- else:
- gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
+ if not skip_gradle_build:
+ if not is_r8lib:
+ gradle.RunGradleExcludeDeps([utils.R8])
+ else:
+ gradle.RunGradle([utils.R8LIB, '-Pno_internal'])
version = determine_version()
with utils.TempDir() as tmp_dir:
@@ -331,8 +340,8 @@
R8_POMTEMPLATE,
pom_file,
version,
- "" if is_r8lib else generate_dependencies(),
- generate_library_licenses() if is_r8lib else "")
+ dependencies='' if is_r8lib else generate_dependencies(),
+ library_licenses=generate_library_licenses() if is_r8lib else '')
# Write the maven zip file.
generate_maven_zip(
'r8',
@@ -382,14 +391,31 @@
make_archive(destination, 'zip', tmp_dir)
move(destination + '.zip', destination)
+def convert_desugar_configuration(configuration, machine_configuration):
+ cmd = [jdk.GetJavaExecutable(),
+ '-cp',
+ utils.R8_JAR,
+ 'com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.DesugaredLibraryConverter',
+ configuration,
+ utils.DESUGAR_IMPLEMENTATION_JDK11,
+ utils.get_android_jar(33),
+ machine_configuration]
+ subprocess.check_call(cmd)
+
# Generate the maven zip for the configuration to desugar desugar_jdk_libs.
def generate_desugar_configuration_maven_zip(
out, configuration, implementation, conversions):
with utils.TempDir() as tmp_dir:
- version = utils.desugar_configuration_version(configuration)
+ (name, version) = utils.desugar_configuration_name_and_version(configuration, False)
+
+ if (not version.startswith("1.")):
+ machine_configuration = join(tmp_dir, "machine.json")
+ convert_desugar_configuration(configuration, machine_configuration)
+ configuration = machine_configuration
+
# Generate the pom file.
pom_file = join(tmp_dir, 'desugar_configuration.pom')
- write_pom_file(DESUGAR_CONFIGUATION_POMTEMPLATE, pom_file, version)
+ write_pom_file(DESUGAR_CONFIGUATION_POMTEMPLATE, pom_file, version, artifact_id=name)
# Generate the jar with the configuration file.
jar_file = join(tmp_dir, 'desugar_configuration.jar')
generate_jar_with_desugar_configuration(
@@ -398,8 +424,7 @@
conversions,
jar_file)
# Write the maven zip file.
- generate_maven_zip(
- 'desugar_jdk_libs_configuration', version, pom_file, jar_file, out)
+ generate_maven_zip(name, version, pom_file, jar_file, out)
def main(argv):
options = parse_options(argv)
diff --git a/tools/desugar_jdk_libs_repository.py b/tools/desugar_jdk_libs_repository.py
index 55db5b6..4417255 100755
--- a/tools/desugar_jdk_libs_repository.py
+++ b/tools/desugar_jdk_libs_repository.py
@@ -14,9 +14,12 @@
import utils
import create_maven_release
-class Configuration(Enum):
+class Variant(Enum):
jdk8 = 'jdk8'
- jdk11_legacy = 'jdk11-legacy'
+ jdk11_legacy = 'jdk11_legacy'
+ jdk11_minimal = 'jdk11_minimal'
+ jdk11 = 'jdk11'
+ jdk11_nio = 'jdk11_nio'
def __str__(self):
return self.value
@@ -32,7 +35,7 @@
default=False,
action='store_true',
help='Clear the Maven repository so it only has one version present')
- parser.add_argument('--configuration', default='jdk8', type=Configuration, choices=list(Configuration))
+ parser.add_argument('--variant', type=Variant, choices=list(Variant))
parser.add_argument('--desugar-jdk-libs-checkout', '--desugar_jdk_libs_checkout',
default=None,
metavar=('<path>'),
@@ -56,29 +59,70 @@
def pom_file(unzip_dir, artifact, version):
return jar_or_pom_file(unzip_dir, artifact, version, 'pom')
-def main():
- args = parse_options()
- if args.clear_repo:
- shutil.rmtree(args.repo_root, ignore_errors=True)
- utils.makedirs_if_needed(args.repo_root)
- configuration = (utils.DESUGAR_CONFIGURATION
- if args.configuration is Configuration.jdk8
- else utils.DESUGAR_CONFIGURATION_JDK11_LEGACY)
- implementation = (utils.DESUGAR_IMPLEMENTATION
- if args.configuration is Configuration.jdk8
- else utils.DESUGAR_IMPLEMENTATION_JDK11)
- version_file = ('VERSION.txt'
- if args.configuration is Configuration.jdk8 else
- 'VERSION_JDK11.txt')
- with utils.TempDir() as tmp_dir:
- version = utils.desugar_configuration_version(configuration)
+def run(args):
+ artifact = None
+ configuration_artifact = None
+ configuration = None
+ conversions = None
+ implementation = None
+ version_file = None
+ implementation_build_target = None
+ implementation_build_output = None
+ match args.variant:
+ case Variant.jdk8:
+ artifact = 'desugar_jdk_libs'
+ configuration_artifact = 'desugar_jdk_libs_configuration'
+ configuration = utils.DESUGAR_CONFIGURATION
+ conversions = utils.LIBRARY_DESUGAR_CONVERSIONS_LEGACY_ZIP
+ implementation = utils.DESUGAR_IMPLEMENTATION
+ version_file = 'VERSION.txt'
+ implementation_build_target = ':maven_release'
+ implementation_build_output = join('bazel-bin', 'desugar_jdk_libs.zip')
+ case Variant.jdk11_legacy:
+ artifact = 'desugar_jdk_libs'
+ configuration_artifact = 'desugar_jdk_libs_configuration'
+ configuration = utils.DESUGAR_CONFIGURATION_JDK11_LEGACY
+ conversions = utils.LIBRARY_DESUGAR_CONVERSIONS_LEGACY_ZIP
+ implementation = utils.DESUGAR_IMPLEMENTATION_JDK11
+ version_file = 'VERSION_JDK11_LEGACY.txt'
+ implementation_build_target = ':maven_release_jdk11_legacy'
+ implementation_build_output = join('bazel-bin', 'desugar_jdk_libs_jdk11_legacy.zip')
+ case Variant.jdk11_minimal:
+ artifact = 'desugar_jdk_libs_minimal'
+ configuration_artifact = 'desugar_jdk_libs_configuration_minimal'
+ configuration = utils.DESUGAR_CONFIGURATION_JDK11_MINIMAL
+ conversions = utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP
+ implementation = utils.DESUGAR_IMPLEMENTATION_JDK11
+ version_file = 'VERSION_JDK11_MINIMAL.txt'
+ implementation_build_target = ':maven_release_jdk11_minimal'
+ implementation_build_output = join('bazel-bin', 'desugar_jdk_libs_jdk11_minimal.zip')
+ case Variant.jdk11:
+ artifact = 'desugar_jdk_libs'
+ configuration_artifact = 'desugar_jdk_libs_configuration'
+ configuration = utils.DESUGAR_CONFIGURATION_JDK11
+ conversions = utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP
+ implementation = utils.DESUGAR_IMPLEMENTATION_JDK11
+ version_file = 'VERSION_JDK11.txt'
+ implementation_build_target = ':maven_release_jdk11'
+ implementation_build_output = join('bazel-bin', 'desugar_jdk_libs_jdk11.zip')
+ case Variant.jdk11_nio:
+ artifact = 'desugar_jdk_libs_nio'
+ configuration_artifact = 'desugar_jdk_libs_configuration_nio'
+ configuration = utils.DESUGAR_CONFIGURATION_JDK11_NIO
+ conversions = utils.LIBRARY_DESUGAR_CONVERSIONS_ZIP
+ implementation = utils.DESUGAR_IMPLEMENTATION_JDK11
+ 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')
+ with utils.TempDir(delete=False) as tmp_dir:
+ (name, version) = utils.desugar_configuration_name_and_version(configuration, False)
# Checkout desugar_jdk_libs from GitHub
use_existing_checkout = args.desugar_jdk_libs_checkout != None
checkout_dir = (args.desugar_jdk_libs_checkout
if use_existing_checkout
else join(tmp_dir, 'desugar_jdk_libs'))
if (not use_existing_checkout):
- utils.RunCmd(['git', 'clone', 'https://github.com/google/desugar_jdk_libs.git', checkout_dir])
+ subprocess.check_call(['git', 'clone', 'https://github.com/google/desugar_jdk_libs.git', checkout_dir])
with utils.ChangedWorkingDirectory(checkout_dir):
with open(version_file) as version_file:
version_file_lines = version_file.readlines()
@@ -99,56 +143,51 @@
create_maven_release.generate_desugar_configuration_maven_zip(
maven_zip,
configuration,
- implementation)
+ implementation,
+ conversions)
unzip_dir = join(tmp_dir, 'desugar_jdk_libs_configuration_unzipped')
cmd = ['unzip', '-q', maven_zip, '-d', unzip_dir]
- utils.RunCmd(cmd)
+ subprocess.check_call(cmd)
cmd = [
'mvn',
'deploy:deploy-file',
'-Durl=file:' + args.repo_root,
'-DrepositoryId=someName',
- '-Dfile=' + jar_file(unzip_dir, 'desugar_jdk_libs_configuration', version),
- '-DpomFile=' + pom_file(unzip_dir, 'desugar_jdk_libs_configuration', version)]
- utils.RunCmd(cmd)
+ '-Dfile=' + jar_file(unzip_dir, configuration_artifact, version),
+ '-DpomFile=' + pom_file(unzip_dir, configuration_artifact, version)]
+ subprocess.check_call(cmd)
# Build desugared library.
print("Building desugared library " + version)
with utils.ChangedWorkingDirectory(checkout_dir):
- utils.RunCmd([
+ subprocess.check_call([
'bazel',
'--bazelrc=/dev/null',
'build',
'--spawn_strategy=local',
'--verbose_failures',
- (':maven_release'
- if args.configuration is Configuration.jdk8
- else ':maven_release_jdk11')])
+ implementation_build_target])
unzip_dir = join(tmp_dir, 'desugar_jdk_libs_unzipped')
cmd = [
'unzip',
'-q',
- join(checkout_dir,
- 'bazel-bin',
- ('desugar_jdk_libs.zip'
- if args.configuration is Configuration.jdk8
- else 'desugar_jdk_libs_jdk11.zip')),
+ join(checkout_dir, implementation_build_output),
'-d',
unzip_dir]
- utils.RunCmd(cmd)
+ subprocess.check_call(cmd)
cmd = [
'mvn',
'deploy:deploy-file',
'-Durl=file:' + args.repo_root,
'-DrepositoryId=someName',
- '-Dfile=' + jar_file(unzip_dir, 'desugar_jdk_libs', version),
- '-DpomFile=' + pom_file(unzip_dir, 'desugar_jdk_libs', version)]
- utils.RunCmd(cmd)
+ '-Dfile=' + jar_file(unzip_dir, artifact, version),
+ '-DpomFile=' + pom_file(unzip_dir, artifact, version)]
+ subprocess.check_call(cmd)
print()
print("Artifacts:")
- print(" com.android.tools:desugar_jdk_libs_configuration:" + version)
- print(" com.android.tools:desugar_jdk_libs:" + version)
+ print(" com.android.tools:%s:%s" % (configuration_artifact, version))
+ print(" com.android.tools:%s:%s" % (artifact, version))
print()
print("deployed to Maven repository at " + args.repo_root + ".")
print()
@@ -161,7 +200,7 @@
print("to dependencyResolutionManagement.repositories in settings.gradle, and use")
print('the "changing" property of the coreLibraryDesugaring dependency:')
print()
- print(" coreLibraryDesugaring('com.android.tools:desugar_jdk_libs:" + version + "') {")
+ print(" coreLibraryDesugaring('com.android.tools:%s:%s') {" % (artifact, version))
print(" changing = true")
print(" }")
print()
@@ -170,5 +209,17 @@
+ "to ensure the cache is not used when the same version is published."
+ "multiple times.")
+def main():
+ args = parse_options()
+ if args.clear_repo:
+ shutil.rmtree(args.repo_root, ignore_errors=True)
+ utils.makedirs_if_needed(args.repo_root)
+ if (args.variant):
+ run(args)
+ else:
+ for v in Variant:
+ args.variant = v
+ run(args)
+
if __name__ == '__main__':
sys.exit(main())
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions
index 94d78e1..5e38ebd 100644
--- a/tools/linux/README.art-versions
+++ b/tools/linux/README.art-versions
@@ -67,25 +67,29 @@
art-13 (Android T)
------------------
-Build from tm-dev commit 442e1091f39417c692d91609af05e58af60d8e2b.
+Build branch android-13.0.0_r3.
-repo sync -cq -j24
+export BRANCH=android-13.0.0_r3
+mkdir ${BRANCH}
+cd ${BRANCH}
+repo init -u https://android.googlesource.com/platform/manifest -b ${BRANCH}
+repo sync -cq -j48
source build/envsetup.sh
lunch aosp_redfin-userdebug
-m -j48
-m -j48 build-art
-m -j48 test-art-host
+USE_RBE=false m -j48
+USE_RBE=false m -j48 build-art
+USE_RBE=false m -j48 test-art-host
Collected into tools/linux/host/art-13. The "host" path element is checked
by the script for running Art.
cd <r8 checkout>
scripts/update-host-art.sh \
- --android-checkout <...>/android/tm-dev \
+ --android-checkout <...>/android-13.0.0_r3 \
--art-dir host/art-13 \
--android-product redfin
-(cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-13)
+ (cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-13)
art-12.0.0 (Android S)
---------------------
diff --git a/tools/linux/host/art-13-dev.tar.gz.sha1 b/tools/linux/host/art-13-dev.tar.gz.sha1
deleted file mode 100644
index 5367655..0000000
--- a/tools/linux/host/art-13-dev.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fed34a1eecaf012550cdd9df24434b8b2068a194
\ No newline at end of file
diff --git a/tools/linux/host/art-13.0.0.tar.gz.sha1 b/tools/linux/host/art-13.0.0.tar.gz.sha1
new file mode 100644
index 0000000..8ec439c
--- /dev/null
+++ b/tools/linux/host/art-13.0.0.tar.gz.sha1
@@ -0,0 +1 @@
+241ecc532b3bf804ef92bdb568ce7d4d85248434
\ No newline at end of file
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index 60a7ee3..a376018 100755
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -150,6 +150,25 @@
'Expected stdout to end with ".apk", was: %s' % stdout)
return apk_path
+def get_component_name(app_id, activity):
+ if activity.startswith(app_id):
+ return '%s/.%s' % (app_id, activity[len(app_id)+1:])
+ else:
+ return '%s/%s' % (app_id, activity)
+
+def get_meminfo(app_id, device_id=None):
+ cmd = create_adb_cmd('shell dumpsys meminfo -s %s' % app_id, device_id)
+ stdout = subprocess.check_output(cmd).decode('utf-8').strip()
+ for line in stdout.splitlines():
+ if 'TOTAL PSS: ' in line:
+ elements = [s for s in line.replace('TOTAL ', 'TOTAL_').split()]
+ assert elements[0] == 'TOTAL_PSS:', elements[0]
+ assert elements[1].isdigit()
+ assert elements[2] == 'TOTAL_RSS:'
+ assert elements[3].isdigit()
+ return { 'total_pss': int(elements[1]), 'total_rss': int(elements[3]) }
+ raise ValueError('Unexpected stdout: %s' % stdout)
+
def get_profile_data(app_id, device_id=None):
with utils.TempDir() as temp:
source = get_profile_path(app_id)
@@ -268,18 +287,22 @@
time.sleep(sleep_in_seconds)
def launch_activity(
- app_id, activity, device_id=None, wait_for_activity_to_launch=False):
+ app_id,
+ activity,
+ device_id=None,
+ intent_data_uri=None,
+ wait_for_activity_to_launch=False):
args = ['shell', 'am', 'start', '-n', '%s/%s' % (app_id, activity)]
+ if intent_data_uri:
+ args.extend(['-d', intent_data_uri])
if wait_for_activity_to_launch:
args.append('-W')
cmd = create_adb_cmd(args, device_id)
stdout = subprocess.check_output(cmd).decode('utf-8').strip()
- if activity.startswith(app_id):
- expected_stdout = (
- 'Starting: Intent { cmp=%s/.%s }' % (app_id, activity[len(app_id)+1:]))
- else:
- expected_stdout = 'Starting: Intent { cmp=%s/%s }' % (app_id, activity)
- assert stdout.startswith(expected_stdout), 'was %s, expected %s' % (stdout, expected_stdout)
+ assert stdout.startswith('Starting: Intent {')
+ expected_component = 'cmp=%s' % get_component_name(app_id, activity)
+ assert expected_component in stdout, \
+ 'was %s, expected %s' % (stdout, expected_component)
lines = stdout.splitlines()
result = {}
for line in lines:
@@ -324,10 +347,12 @@
stdout = subprocess.check_output(cmd).decode('utf-8').strip()
assert len(stdout) == 0
-def start_logcat(device_id=None, format=None, filter=None):
+def start_logcat(device_id=None, format=None, filter=None, silent=False):
args = ['logcat']
if format:
args.extend(['--format', format])
+ if silent:
+ args.append('-s')
if filter:
args.append(filter)
cmd = create_adb_cmd(args, device_id)
diff --git a/tools/startup/measure_startup.py b/tools/startup/measure_startup.py
index e00797c..1b94fad 100755
--- a/tools/startup/measure_startup.py
+++ b/tools/startup/measure_startup.py
@@ -4,7 +4,9 @@
# BSD-style license that can be found in the LICENSE file.
import argparse
+import datetime
import os
+import re
import statistics
import sys
import time
@@ -141,6 +143,16 @@
def run(out_dir, options, tmp_dir):
assert adb_utils.get_screen_state(options.device_id).is_on_and_unlocked()
+ # Start logcat for time to fully drawn.
+ logcat_process = None
+ if options.fully_drawn_logcat_message:
+ adb_utils.clear_logcat(options.device_id)
+ logcat_process = adb_utils.start_logcat(
+ options.device_id,
+ format='time',
+ filter='%s ActivityTaskManager:I' % options.fully_drawn_logcat_filter,
+ silent=True)
+
# Start perfetto trace collector.
perfetto_process = None
perfetto_trace_path = None
@@ -153,17 +165,64 @@
options.app_id,
options.main_activity,
options.device_id,
+ intent_data_uri=options.intent_data_uri,
wait_for_activity_to_launch=True)
+ # Wait for app to be fully drawn.
+ logcat = None
+ if logcat_process is not None:
+ wait_until_fully_drawn(logcat_process, options)
+ logcat = adb_utils.stop_logcat(logcat_process)
+
# Wait for perfetto trace collector to stop.
if options.perfetto:
perfetto_utils.stop_record_android_trace(perfetto_process, out_dir)
# Get minor and major page faults from app process.
- data = compute_data(launch_activity_result, perfetto_trace_path, options)
+ data = compute_data(
+ launch_activity_result, logcat, perfetto_trace_path, options)
write_data(out_dir, data)
return data
+def wait_until_fully_drawn(logcat_process, options):
+ print('Waiting until app is fully drawn')
+ while True:
+ is_fully_drawn = any(
+ is_app_fully_drawn_logcat_message(line, options) \
+ for line in logcat_process.lines)
+ if is_fully_drawn:
+ break
+ time.sleep(1)
+ print('Done')
+
+def compute_time_to_fully_drawn_from_time_to_first_frame(logcat, options):
+ displayed_time = None
+ fully_drawn_time = None
+ for line in logcat:
+ if is_app_displayed_logcat_message(line, options):
+ displayed_time = get_timestamp_from_logcat_message(line)
+ elif is_app_fully_drawn_logcat_message(line, options):
+ fully_drawn_time = get_timestamp_from_logcat_message(line)
+ assert displayed_time is not None
+ assert fully_drawn_time is not None
+ assert fully_drawn_time > displayed_time
+ return fully_drawn_time - displayed_time
+
+def get_timestamp_from_logcat_message(line):
+ time_end_index = len('00-00 00:00:00.000')
+ time_format = '%m-%d %H:%M:%S.%f'
+ time_str = line[0:time_end_index] + '000'
+ time_seconds = datetime.datetime.strptime(time_str, time_format).timestamp()
+ return int(time_seconds * 1000)
+
+def is_app_displayed_logcat_message(line, options):
+ substring = 'Displayed %s' % adb_utils.get_component_name(
+ options.app_id, options.main_activity)
+ return substring in line
+
+def is_app_fully_drawn_logcat_message(line, options):
+ return re.search(options.fully_drawn_logcat_message, line)
+
def add_data(data_total, data):
for key, value in data.items():
if key == 'app_id':
@@ -183,23 +242,35 @@
assert isinstance(value, int), key
data_total[key] = [value]
-def compute_data(launch_activity_result, perfetto_trace_path, options):
+def compute_data(launch_activity_result, logcat, perfetto_trace_path, options):
minfl, majfl = adb_utils.get_minor_major_page_faults(
options.app_id, options.device_id)
+ meminfo = adb_utils.get_meminfo(options.app_id, options.device_id)
data = {
'app_id': options.app_id,
'time': time.ctime(time.time()),
'minfl': minfl,
'majfl': majfl
}
+ data.update(meminfo)
startup_data = compute_startup_data(
- launch_activity_result, perfetto_trace_path, options)
+ launch_activity_result, logcat, perfetto_trace_path, options)
return data | startup_data
-def compute_startup_data(launch_activity_result, perfetto_trace_path, options):
+def compute_startup_data(
+ launch_activity_result, logcat, perfetto_trace_path, options):
+ time_to_first_frame = launch_activity_result.get('total_time')
startup_data = {
- 'adb_startup': launch_activity_result.get('total_time')
+ 'adb_startup': time_to_first_frame
}
+
+ # Time to fully drawn.
+ if options.fully_drawn_logcat_message:
+ startup_data['time_to_fully_drawn'] = \
+ compute_time_to_fully_drawn_from_time_to_first_frame(logcat, options) \
+ + time_to_first_frame
+
+ # Perfetto stats.
perfetto_startup_data = {}
if options.perfetto:
trace_processor = TraceProcessor(file_path=perfetto_trace_path)
@@ -272,10 +343,19 @@
help='Device id (e.g., emulator-5554).')
result.add_argument('--device-pin',
help='Device pin code (e.g., 1234)')
+ result.add_argument('--fully-drawn-logcat-filter',
+ help='Logcat filter for the fully drawn message '
+ '(e.g., "tag:I")')
+ result.add_argument('--fully-drawn-logcat-message',
+ help='Logcat message that indicates that the app is '
+ 'fully drawn (regexp)')
result.add_argument('--hot-startup',
help='Measure hot startup instead of cold startup',
default=False,
action='store_true')
+ result.add_argument('--intent-data-uri',
+ help='Value to use for the -d argument to the intent '
+ 'that is used to launch the app')
result.add_argument('--iterations',
help='Number of traces to generate',
default=1,
@@ -315,6 +395,10 @@
# Profile is only used with --aot.
assert options.aot or not options.baseline_profile
+ # Fully drawn logcat filter and message is absent or both present.
+ assert (options.fully_drawn_logcat_filter is None) == \
+ (options.fully_drawn_logcat_message is None)
+
return options, args
def global_setup(options):
diff --git a/tools/test.py b/tools/test.py
index 27c8b6a..5586802 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -260,7 +260,7 @@
utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE)
- (library_jar, maven_zip) = archive_desugar_jdk_libs.BuildDesugaredLibrary(checkout_dir, 'jdk11' if options.desugared_library_configuration == 'jdk11' else 'jdk8')
+ (library_jar, maven_zip) = archive_desugar_jdk_libs.BuildDesugaredLibrary(checkout_dir, 'jdk11_legacy' if options.desugared_library_configuration == 'jdk11' else 'jdk8')
desugar_jdk_libs = os.path.join(desugar_jdk_libs_dir, os.path.basename(library_jar))
shutil.copyfile(library_jar, desugar_jdk_libs)
print('Desugared library for test in ' + desugar_jdk_libs)
diff --git a/tools/utils.py b/tools/utils.py
index 9bd518f..1830f53 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -77,12 +77,24 @@
'third_party', 'openjdk', 'desugar_jdk_libs', 'desugar_jdk_libs.jar')
DESUGAR_CONFIGURATION_JDK11_LEGACY = os.path.join(
'src', 'library_desugar', 'jdk11', 'desugar_jdk_libs_legacy.json')
+DESUGAR_CONFIGURATION_JDK11_MINIMAL = os.path.join(
+ 'src', 'library_desugar', 'jdk11', 'desugar_jdk_libs_minimal.json')
+DESUGAR_CONFIGURATION_JDK11 = os.path.join(
+ 'src', 'library_desugar', 'jdk11', 'desugar_jdk_libs.json')
+DESUGAR_CONFIGURATION_JDK11_NIO = os.path.join(
+ 'src', 'library_desugar', 'jdk11', 'desugar_jdk_libs_nio.json')
DESUGAR_IMPLEMENTATION_JDK11 = os.path.join(
'third_party', 'openjdk', 'desugar_jdk_libs_11', 'desugar_jdk_libs.jar')
DESUGAR_CONFIGURATION_MAVEN_ZIP = os.path.join(
LIBS, 'desugar_jdk_libs_configuration.zip')
DESUGAR_CONFIGURATION_JDK11_LEGACY_MAVEN_ZIP = os.path.join(
LIBS, 'desugar_jdk_libs_configuration_jdk11_legacy.zip')
+DESUGAR_CONFIGURATION_JDK11_MINIMAL_MAVEN_ZIP = os.path.join(
+ LIBS, 'desugar_jdk_libs_configuration_jdk11_minimal.zip')
+DESUGAR_CONFIGURATION_JDK11_MAVEN_ZIP = os.path.join(
+ LIBS, 'desugar_jdk_libs_configuration_jdk11.zip')
+DESUGAR_CONFIGURATION_JDK11_NIO_MAVEN_ZIP = os.path.join(
+ LIBS, 'desugar_jdk_libs_configuration_jdk11_nio.zip')
GENERATED_LICENSE = os.path.join(GENERATED_LICENSE_DIR, 'LICENSE')
RT_JAR = os.path.join(REPO_ROOT, 'third_party/openjdk/openjdk-rt-1.8/rt.jar')
R8LIB_KEEP_RULES = os.path.join(REPO_ROOT, 'src/main/keep.txt')
@@ -626,17 +638,48 @@
# so we split on '('; clean up tailing spaces; and strip off 'R8 '.
return output.split('(')[0].strip()[3:]
-def desugar_configuration_version(configuration):
+def desugar_configuration_name_and_version(configuration, is_for_maven):
+ name = 'desugar_jdk_libs_configuration'
with open(configuration, 'r') as f:
configuration_json = json.loads(f.read())
configuration_format_version = \
configuration_json.get('configuration_format_version')
+ if (not configuration_format_version):
+ raise Exception(
+ 'No "configuration_format_version" found in ' + configuration)
+ if (configuration_format_version != 3
+ and configuration_format_version != 5
+ and configuration_format_version != (200 if is_for_maven else 100)):
+ raise Exception(
+ 'Unsupported "configuration_format_version" "%s" found in %s'
+ % (configuration_format_version, configuration))
version = configuration_json.get('version')
if not version:
- raise Exception(
- 'No "version" found in ' + configuration)
- check_basic_semver_version(version, 'in ' + configuration, allowPrerelease = True)
- return version
+ if configuration_format_version == (200 if is_for_maven else 100):
+ identifier = configuration_json.get('identifier')
+ if not identifier:
+ raise Exception(
+ 'No "identifier" found in ' + configuration)
+ identifier_split = identifier.split(':')
+ if (len(identifier_split) != 3):
+ raise Exception('Invalid "identifier" found in ' + configuration)
+ if (identifier_split[0] != 'com.tools.android'):
+ raise Exception('Invalid "identifier" found in ' + configuration)
+ if not identifier_split[1].startswith('desugar_jdk_libs_configuration'):
+ raise Exception('Invalid "identifier" found in ' + configuration)
+ name = identifier_split[1]
+ version = identifier_split[2]
+ else:
+ raise Exception(
+ 'No "version" found in ' + configuration)
+ else:
+ if configuration_format_version == (200 if is_for_maven else 100):
+ raise Exception(
+ 'No "version" expected in ' + configuration)
+ # Disallow prerelease, as older R8 versions cannot parse it causing hard to
+ # understand errors.
+ check_basic_semver_version(version, 'in ' + configuration, allowPrerelease = False)
+ return (name, version)
class SemanticVersion:
def __init__(self, major, minor, patch, prerelease):