Merge commit '71a482b7a8ed665c53b1a0eb188f218c86213984' into dev-release
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
index d412e52..52d878b 100644
--- a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
@@ -21,6 +21,9 @@
"j$/util/stream/Collector$Characteristics");
private static final Set<String> WRAP_CONVERT_OWNER =
ImmutableSet.of(
+ "j$/util/stream/DoubleStream",
+ "j$/util/stream/IntStream",
+ "j$/util/stream/LongStream",
"j$/util/stream/Stream",
"j$/nio/file/spi/FileSystemProvider",
"j$/nio/file/spi/FileTypeDetector",
diff --git a/src/library_desugar/java/j$/util/stream/DoubleStream.java b/src/library_desugar/java/j$/util/stream/DoubleStream.java
new file mode 100644
index 0000000..1fdab0d
--- /dev/null
+++ b/src/library_desugar/java/j$/util/stream/DoubleStream.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 DoubleStream {
+
+ public static java.util.stream.DoubleStream wrap_convert(j$.util.stream.DoubleStream stream) {
+ return null;
+ }
+
+ public static j$.util.stream.DoubleStream wrap_convert(java.util.stream.DoubleStream stream) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/util/stream/IntStream.java b/src/library_desugar/java/j$/util/stream/IntStream.java
new file mode 100644
index 0000000..75165a8
--- /dev/null
+++ b/src/library_desugar/java/j$/util/stream/IntStream.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 IntStream {
+
+ public static java.util.stream.IntStream wrap_convert(j$.util.stream.IntStream stream) {
+ return null;
+ }
+
+ public static j$.util.stream.IntStream wrap_convert(java.util.stream.IntStream stream) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/util/stream/LongStream.java b/src/library_desugar/java/j$/util/stream/LongStream.java
new file mode 100644
index 0000000..d078146
--- /dev/null
+++ b/src/library_desugar/java/j$/util/stream/LongStream.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 LongStream {
+
+ public static java.util.stream.LongStream wrap_convert(j$.util.stream.LongStream stream) {
+ return null;
+ }
+
+ public static j$.util.stream.LongStream wrap_convert(java.util.stream.LongStream stream) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/util/stream/Stream.java b/src/library_desugar/java/j$/util/stream/Stream.java
index 70c761a..dc5861d 100644
--- a/src/library_desugar/java/j$/util/stream/Stream.java
+++ b/src/library_desugar/java/j$/util/stream/Stream.java
@@ -13,4 +13,12 @@
public static j$.util.stream.Stream<?> inverted_wrap_convert(java.util.stream.Stream<?> stream) {
return null;
}
+
+ public static java.util.stream.Stream<?> wrap_convert(j$.util.stream.Stream<?> stream) {
+ return null;
+ }
+
+ public static j$.util.stream.Stream<?> wrap_convert(java.util.stream.Stream<?> stream) {
+ return null;
+ }
}
diff --git a/src/library_desugar/java/java/util/stream/FlatMapApiFlips.java b/src/library_desugar/java/java/util/stream/FlatMapApiFlips.java
new file mode 100644
index 0000000..b9e1b9e
--- /dev/null
+++ b/src/library_desugar/java/java/util/stream/FlatMapApiFlips.java
@@ -0,0 +1,164 @@
+// 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.DoubleFunction;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.function.LongFunction;
+
+public class FlatMapApiFlips {
+
+ public static Function<?, ?> flipFunctionReturningStream(Function<?, ?> function) {
+ return new FunctionStreamWrapper<>(function);
+ }
+
+ public static IntFunction<?> flipFunctionReturningStream(IntFunction<?> function) {
+ return new IntFunctionStreamWrapper<>(function);
+ }
+
+ public static DoubleFunction<?> flipFunctionReturningStream(DoubleFunction<?> function) {
+ return new DoubleFunctionStreamWrapper<>(function);
+ }
+
+ public static LongFunction<?> flipFunctionReturningStream(LongFunction<?> function) {
+ return new LongFunctionStreamWrapper<>(function);
+ }
+
+ 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 R flipStream(R maybeStream) {
+ if (maybeStream == null) {
+ return null;
+ }
+
+ if (maybeStream instanceof java.util.stream.Stream<?>) {
+ return (R) j$.util.stream.Stream.wrap_convert((java.util.stream.Stream<?>) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.Stream<?>) {
+ return (R) j$.util.stream.Stream.wrap_convert((j$.util.stream.Stream<?>) maybeStream);
+ }
+
+ if (maybeStream instanceof java.util.stream.IntStream) {
+ return (R) j$.util.stream.IntStream.wrap_convert((java.util.stream.IntStream) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.IntStream) {
+ return (R) j$.util.stream.IntStream.wrap_convert((j$.util.stream.IntStream) maybeStream);
+ }
+
+ if (maybeStream instanceof java.util.stream.DoubleStream) {
+ return (R)
+ j$.util.stream.DoubleStream.wrap_convert((java.util.stream.DoubleStream) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.DoubleStream) {
+ return (R)
+ j$.util.stream.DoubleStream.wrap_convert((j$.util.stream.DoubleStream) maybeStream);
+ }
+
+ if (maybeStream instanceof java.util.stream.LongStream) {
+ return (R)
+ j$.util.stream.LongStream.wrap_convert((java.util.stream.LongStream) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.LongStream) {
+ return (R) j$.util.stream.LongStream.wrap_convert((j$.util.stream.LongStream) maybeStream);
+ }
+
+ throw exception("java.util.stream.*Stream", maybeStream.getClass());
+ }
+
+ public R apply(T arg) {
+ return flipStream(function.apply(arg));
+ }
+ }
+
+ public static class IntFunctionStreamWrapper<R> implements IntFunction<R> {
+
+ public IntFunction<R> function;
+
+ public IntFunctionStreamWrapper(IntFunction<R> function) {
+ this.function = function;
+ }
+
+ private R flipStream(R maybeStream) {
+ if (maybeStream == null) {
+ return null;
+ }
+ if (maybeStream instanceof java.util.stream.IntStream) {
+ return (R) j$.util.stream.IntStream.wrap_convert((java.util.stream.IntStream) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.IntStream) {
+ return (R) j$.util.stream.IntStream.wrap_convert((j$.util.stream.IntStream) maybeStream);
+ }
+ throw exception("java.util.stream.IntStream", maybeStream.getClass());
+ }
+
+ public R apply(int arg) {
+ return flipStream(function.apply(arg));
+ }
+ }
+
+ public static class DoubleFunctionStreamWrapper<R> implements DoubleFunction<R> {
+
+ public DoubleFunction<R> function;
+
+ public DoubleFunctionStreamWrapper(DoubleFunction<R> function) {
+ this.function = function;
+ }
+
+ private R flipStream(R maybeStream) {
+ if (maybeStream == null) {
+ return null;
+ }
+ if (maybeStream instanceof java.util.stream.DoubleStream) {
+ return (R)
+ j$.util.stream.DoubleStream.wrap_convert((java.util.stream.DoubleStream) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.DoubleStream) {
+ return (R)
+ j$.util.stream.DoubleStream.wrap_convert((j$.util.stream.DoubleStream) maybeStream);
+ }
+ throw exception("java.util.stream.DoubleStream", maybeStream.getClass());
+ }
+
+ public R apply(double arg) {
+ return flipStream(function.apply(arg));
+ }
+ }
+
+ public static class LongFunctionStreamWrapper<R> implements LongFunction<R> {
+
+ public LongFunction<R> function;
+
+ public LongFunctionStreamWrapper(LongFunction<R> function) {
+ this.function = function;
+ }
+
+ private R flipStream(R maybeStream) {
+ if (maybeStream == null) {
+ return null;
+ }
+ if (maybeStream instanceof java.util.stream.LongStream) {
+ return (R)
+ j$.util.stream.LongStream.wrap_convert((java.util.stream.LongStream) maybeStream);
+ }
+ if (maybeStream instanceof j$.util.stream.LongStream) {
+ return (R) j$.util.stream.LongStream.wrap_convert((j$.util.stream.LongStream) maybeStream);
+ }
+ throw exception("java.util.stream.LongStream", maybeStream.getClass());
+ }
+
+ public R apply(long arg) {
+ return flipStream(function.apply(arg));
+ }
+ }
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 103035f..609f920 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -100,6 +100,13 @@
},
"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.stream.Stream java.util.stream.Stream#flatMap(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.DoubleStream java.util.stream.DoubleStream#flatMap(java.util.function.DoubleFunction)": [0, "java.util.function.DoubleFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.DoubleFunction)"],
+ "java.util.stream.DoubleStream java.util.stream.Stream#flatMapToDouble(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.IntStream java.util.stream.Stream#flatMapToInt(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.IntStream java.util.stream.IntStream#flatMap(java.util.function.IntFunction)": [0, "java.util.function.IntFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.IntFunction)"],
+ "java.util.stream.LongStream java.util.stream.Stream#flatMapToLong(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.LongStream java.util.stream.LongStream#flatMap(java.util.function.LongFunction)": [0, "java.util.function.LongFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.LongFunction)"],
"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": [
@@ -234,6 +241,15 @@
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
},
+ "java.util.stream.DoubleStream": {
+ "j$.util.stream.DoubleStream": "java.util.stream.DoubleStream"
+ },
+ "java.util.stream.IntStream": {
+ "j$.util.stream.IntStream": "java.util.stream.IntStream"
+ },
+ "java.util.stream.LongStream": {
+ "j$.util.stream.LongStream": "java.util.stream.LongStream"
+ },
"java.util.stream.Stream": {
"j$.util.stream.Stream": "java.util.stream.Stream"
}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index a53fd3b..e2f74e8 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -237,6 +237,13 @@
},
"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.stream.Stream java.util.stream.Stream#flatMap(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.DoubleStream java.util.stream.DoubleStream#flatMap(java.util.function.DoubleFunction)": [0, "java.util.function.DoubleFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.DoubleFunction)"],
+ "java.util.stream.DoubleStream java.util.stream.Stream#flatMapToDouble(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.IntStream java.util.stream.Stream#flatMapToInt(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.IntStream java.util.stream.IntStream#flatMap(java.util.function.IntFunction)": [0, "java.util.function.IntFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.IntFunction)"],
+ "java.util.stream.LongStream java.util.stream.Stream#flatMapToLong(java.util.function.Function)": [0, "java.util.function.Function java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.Function)"],
+ "java.util.stream.LongStream java.util.stream.LongStream#flatMap(java.util.function.LongFunction)": [0, "java.util.function.LongFunction java.util.stream.FlatMapApiFlips#flipFunctionReturningStream(java.util.function.LongFunction)"],
"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": [
@@ -454,6 +461,15 @@
"java.util.Optional": {
"j$.util.Optional": "java.util.Optional"
},
+ "java.util.stream.DoubleStream": {
+ "j$.util.stream.DoubleStream": "java.util.stream.DoubleStream"
+ },
+ "java.util.stream.IntStream": {
+ "j$.util.stream.IntStream": "java.util.stream.IntStream"
+ },
+ "java.util.stream.LongStream": {
+ "j$.util.stream.LongStream": "java.util.stream.LongStream"
+ },
"java.util.stream.Stream": {
"j$.util.stream.Stream": "java.util.stream.Stream"
}
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 02e06d1..a233e49 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -12,6 +12,9 @@
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.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileForRewriting;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -57,6 +60,7 @@
private final MapIdProvider mapIdProvider;
private final SourceFileProvider sourceFileProvider;
private final boolean isAndroidPlatformBuild;
+ private final List<ArtProfileForRewriting> artProfilesForRewriting;
private final List<StartupProfileProvider> startupProfileProviders;
private final ClassConflictResolver classConflictResolver;
@@ -78,6 +82,7 @@
mapIdProvider = null;
sourceFileProvider = null;
isAndroidPlatformBuild = false;
+ artProfilesForRewriting = null;
startupProfileProviders = null;
classConflictResolver = null;
}
@@ -100,6 +105,7 @@
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
boolean isAndroidPlatformBuild,
+ List<ArtProfileForRewriting> artProfilesForRewriting,
List<StartupProfileProvider> startupProfileProviders,
ClassConflictResolver classConflictResolver) {
super(app);
@@ -121,6 +127,7 @@
this.mapIdProvider = mapIdProvider;
this.sourceFileProvider = sourceFileProvider;
this.isAndroidPlatformBuild = isAndroidPlatformBuild;
+ this.artProfilesForRewriting = artProfilesForRewriting;
this.startupProfileProviders = startupProfileProviders;
this.classConflictResolver = classConflictResolver;
}
@@ -219,6 +226,10 @@
return isAndroidPlatformBuild;
}
+ List<ArtProfileForRewriting> getArtProfilesForRewriting() {
+ return artProfilesForRewriting;
+ }
+
List<StartupProfileProvider> getStartupProfileProviders() {
return startupProfileProviders;
}
@@ -258,7 +269,6 @@
protected DesugarState desugarState = DesugarState.ON;
private List<StringResource> desugaredLibrarySpecificationResources = new ArrayList<>();
private boolean includeClassesChecksum = false;
- private boolean lookupLibraryBeforeProgram = true;
private boolean optimizeMultidexForLinearAlloc = false;
private BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true;
private List<AssertionsConfiguration> assertionsConfiguration = new ArrayList<>();
@@ -268,6 +278,7 @@
private MapIdProvider mapIdProvider = null;
private SourceFileProvider sourceFileProvider = null;
private boolean isAndroidPlatformBuild = false;
+ private List<ArtProfileForRewriting> artProfilesForRewriting = new ArrayList<>();
private List<StartupProfileProvider> startupProfileProviders = new ArrayList<>();
private ClassConflictResolver classConflictResolver = null;
@@ -685,6 +696,23 @@
return isAndroidPlatformBuild;
}
+ /**
+ * Add an ART profiles that should be rewritten to match the residual application. The ART
+ * profile is given to the compiler by the {@link ArtProfileProvider} and passed back to the
+ * {@link ArtProfileConsumer} at the end of compilation when the ART profile has been fully
+ * rewritten to match the residual application.
+ */
+ public B addArtProfileForRewriting(
+ ArtProfileProvider artProfileProvider, ArtProfileConsumer residualArtProfileProvider) {
+ artProfilesForRewriting.add(
+ new ArtProfileForRewriting(artProfileProvider, residualArtProfileProvider));
+ return self();
+ }
+
+ List<ArtProfileForRewriting> getArtProfilesForRewriting() {
+ return artProfilesForRewriting;
+ }
+
B addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
return addStartupProfileProviders(Arrays.asList(startupProfileProviders));
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 8be1456..74a3d20 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -46,19 +46,16 @@
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -278,15 +275,6 @@
boolean hasDexResources = appView.appInfo().app().getFlags().hasReadProgramClassFromDex();
Marker marker = options.getMarker(Tool.D8);
- Set<Marker> markers = new HashSet<>(appView.dexItemFactory().extractMarkers());
- // TODO(b/166617364): Don't add an additional marker when desugaring is turned off.
- if (hasClassResources
- && (options.desugarState != DesugarState.OFF
- || markers.isEmpty()
- || (markers.size() == 1 && markers.iterator().next().isL8()))) {
- markers.add(marker);
- }
- Marker.checkCompatibleDesugaredLibrary(markers, options.reporter);
timing.time(
"Run inspections",
@@ -314,7 +302,7 @@
// without iterating again the IR. We fall-back to writing one app with rewriting and
// merging it with the other app in rewriteNonDexInputs.
timing.begin("Rewrite non-dex inputs");
- DexApplication app = rewriteNonDexInputs(appView, inputApp, executor, timing);
+ DexApplication app = rewriteNonDexInputs(appView, inputApp, executor, marker, timing);
timing.end();
appView.setAppInfo(
new AppInfo(
@@ -345,8 +333,7 @@
if (options.isGeneratingClassFiles()) {
new CfApplicationWriter(appView, marker).write(options.getClassFileConsumer(), inputApp);
} else {
- new ApplicationWriter(appView, marker == null ? null : ImmutableList.copyOf(markers))
- .write(executor, inputApp);
+ new ApplicationWriter(appView, marker).write(executor, inputApp);
}
options.printWarnings();
} catch (ExecutionException e) {
@@ -408,7 +395,11 @@
}
private static DexApplication rewriteNonDexInputs(
- AppView<AppInfo> appView, AndroidApp inputApp, ExecutorService executor, Timing timing)
+ AppView<AppInfo> appView,
+ AndroidApp inputApp,
+ ExecutorService executor,
+ Marker marker,
+ Timing timing)
throws IOException, ExecutionException {
// TODO(b/154575955): Remove the naming lens in D8.
appView
@@ -437,11 +428,7 @@
ConvertedCfFiles convertedCfFiles = new ConvertedCfFiles();
new GenericSignatureRewriter(appView).run(appView.appInfo().classes(), executor);
new KotlinMetadataRewriter(appView).runForD8(executor);
- new ApplicationWriter(
- appView,
- null,
- convertedCfFiles)
- .write(executor);
+ new ApplicationWriter(appView, marker, convertedCfFiles).write(executor);
AndroidApp.Builder builder = AndroidApp.builder(inputApp);
builder.getProgramResourceProviders().clear();
builder.addProgramResourceProvider(convertedCfFiles);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 4eba3b9..e7f27d5 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileForRewriting;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardConfigurationSource;
@@ -457,6 +458,7 @@
proguardMapConsumer,
enableMissingLibraryApiModeling,
getAndroidPlatformBuild(),
+ getArtProfilesForRewriting(),
getStartupProfileProviders(),
getClassConflictResolver(),
factory);
@@ -549,6 +551,7 @@
StringConsumer proguardMapConsumer,
boolean enableMissingLibraryApiModeling,
boolean isAndroidPlatformBuild,
+ List<ArtProfileForRewriting> artProfilesForRewriting,
List<StartupProfileProvider> startupProfileProviders,
ClassConflictResolver classConflictResolver,
DexItemFactory factory) {
@@ -570,6 +573,7 @@
mapIdProvider,
null,
isAndroidPlatformBuild,
+ artProfilesForRewriting,
startupProfileProviders,
classConflictResolver);
this.intermediate = intermediate;
@@ -660,7 +664,7 @@
if (!enableMissingLibraryApiModeling) {
internal.apiModelingOptions().disableApiCallerIdentification();
- internal.apiModelingOptions().disableMissingApiModeling();
+ internal.apiModelingOptions().disableOutliningAndStubbing();
}
// Default is to remove all javac generated assertion code when generating dex.
@@ -694,6 +698,10 @@
internal.configureAndroidPlatformBuild(getAndroidPlatformBuild());
+ internal
+ .getArtProfileOptions()
+ .setArtProfilesForRewriting(getArtProfilesForRewriting())
+ .setPassthrough(true);
internal.getStartupOptions().setStartupProfileProviders(getStartupProfileProviders());
internal.programClassConflictResolver =
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 7a2950d..94440be 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileForRewriting;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AssertionConfigurationWithDefault;
@@ -121,6 +122,7 @@
null,
false,
null,
+ null,
classConflictResolver);
this.d8Command = d8Command;
this.r8Command = r8Command;
@@ -223,7 +225,7 @@
// Disable global optimizations.
internal.disableGlobalOptimizations();
internal.apiModelingOptions().disableApiCallerIdentification();
- internal.apiModelingOptions().disableMissingApiModeling();
+ internal.apiModelingOptions().disableOutliningAndStubbing();
internal.setDumpInputFlags(getDumpInputFlags());
internal.dumpOptions = dumpOptions();
@@ -372,6 +374,11 @@
.setIncludeClassesChecksum(getIncludeClassesChecksum())
.setDexClassChecksumFilter(getDexClassChecksumFilter())
.setProgramConsumer(getProgramConsumer());
+ for (ArtProfileForRewriting artProfileForRewriting : getArtProfilesForRewriting()) {
+ r8Builder.addArtProfileForRewriting(
+ artProfileForRewriting.getArtProfileProvider(),
+ artProfileForRewriting.getResidualArtProfileConsumer());
+ }
for (ClassFileResourceProvider libraryResourceProvider :
inputs.getLibraryResourceProviders()) {
r8Builder.addLibraryResourceProvider(libraryResourceProvider);
@@ -403,6 +410,11 @@
.setIncludeClassesChecksum(getIncludeClassesChecksum())
.setDexClassChecksumFilter(getDexClassChecksumFilter())
.setProgramConsumer(getProgramConsumer());
+ for (ArtProfileForRewriting artProfileForRewriting : getArtProfilesForRewriting()) {
+ d8Builder.addArtProfileForRewriting(
+ artProfileForRewriting.getArtProfileProvider(),
+ artProfileForRewriting.getResidualArtProfileConsumer());
+ }
for (ClassFileResourceProvider libraryResourceProvider :
inputs.getLibraryResourceProviders()) {
d8Builder.addLibraryResourceProvider(libraryResourceProvider);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 5855643..d4e7f3a 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -69,7 +69,7 @@
import com.android.tools.r8.optimize.AccessModifier;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
-import com.android.tools.r8.optimize.VisibilityBridgeRemover;
+import com.android.tools.r8.optimize.RedundantBridgeRemover;
import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
import com.android.tools.r8.optimize.proto.ProtoNormalizer;
@@ -112,7 +112,6 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
@@ -123,7 +122,6 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -215,18 +213,10 @@
try {
Marker marker = options.getMarker(Tool.R8);
assert marker != null;
- // Get the markers from the input which are different from the one created for this
- // compilation
- Set<Marker> markers = new HashSet<>(appView.dexItemFactory().extractMarkers());
- markers.remove(marker);
if (options.isGeneratingClassFiles()) {
new CfApplicationWriter(appView, marker).write(options.getClassFileConsumer(), inputApp);
} else {
- new ApplicationWriter(
- appView,
- // Ensure that the marker for this compilation is the first in the list.
- ImmutableList.<Marker>builder().add(marker).addAll(markers).build())
- .write(executorService, inputApp);
+ new ApplicationWriter(appView, marker).write(executorService, inputApp);
}
} catch (IOException e) {
throw new RuntimeException("Cannot write application", e);
@@ -467,10 +457,10 @@
subtypingInfo);
boolean changed = appView.setGraphLens(publicizedLens);
if (changed) {
- // We can now remove visibility bridges. Note that we do not need to update the
+ // We can now remove redundant bridges. Note that we do not need to update the
// invoke-targets here, as the existing invokes will simply dispatch to the now
// visible super-method. MemberRebinding, if run, will then dispatch it correctly.
- new VisibilityBridgeRemover(appView.withLiveness()).run(executorService);
+ new RedundantBridgeRemover(appView.withLiveness()).run(executorService);
}
}
@@ -698,10 +688,10 @@
}
}
- // Remove unneeded visibility bridges that have been inserted for member rebinding.
+ // Remove redundant bridges that have been inserted for member rebinding.
// This can only be done if we have AppInfoWithLiveness.
if (appView.appInfo().hasLiveness()) {
- new VisibilityBridgeRemover(appView.withLiveness()).run(executorService);
+ new RedundantBridgeRemover(appView.withLiveness()).run(executorService);
} else {
// If we don't have AppInfoWithLiveness here, it must be because we are not shrinking. When
// we are not shrinking, we can't move visibility bridges. In principle, though, it would be
@@ -853,19 +843,16 @@
private static boolean allReferencesAssignedApiLevel(
AppView<? extends AppInfoWithClassHierarchy> appView) {
- if (!appView.options().apiModelingOptions().checkAllApiReferencesAreSet
+ if (!appView.options().apiModelingOptions().isCheckAllApiReferencesAreSet()
|| appView.options().configurationDebugging) {
return true;
}
- // This will return false if we find anything in the library which is not modeled.
+ // This will assert false if we find anything in the library which is not modeled.
for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
clazz.forEachProgramMember(
member -> {
assert !member.getDefinition().getApiLevel().isNotSetApiLevel()
: "Every member should have been analyzed";
- assert appView.options().apiModelingOptions().enableApiCallerIdentification
- || member.getDefinition().getApiLevel().isUnknownApiLevel()
- : "Every member should have level UNKNOWN";
});
}
return true;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index cef341c..81efb90 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.naming.SourceFileRewriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.profile.art.ArtProfileForRewriting;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
import com.android.tools.r8.shaking.ProguardConfigurationParserOptions;
@@ -662,6 +663,7 @@
getSourceFileProvider(),
enableMissingLibraryApiModeling,
getAndroidPlatformBuild(),
+ getArtProfilesForRewriting(),
getStartupProfileProviders(),
getClassConflictResolver());
@@ -850,6 +852,7 @@
SourceFileProvider sourceFileProvider,
boolean enableMissingLibraryApiModeling,
boolean isAndroidPlatformBuild,
+ List<ArtProfileForRewriting> artProfilesForRewriting,
List<StartupProfileProvider> startupProfileProviders,
ClassConflictResolver classConflictResolver) {
super(
@@ -870,6 +873,7 @@
mapIdProvider,
sourceFileProvider,
isAndroidPlatformBuild,
+ artProfilesForRewriting,
startupProfileProviders,
classConflictResolver);
assert proguardConfiguration != null;
@@ -1018,7 +1022,8 @@
internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
if (!enableMissingLibraryApiModeling) {
- internal.apiModelingOptions().disableMissingApiModeling();
+ internal.apiModelingOptions().disableApiCallerIdentification();
+ internal.apiModelingOptions().disableOutliningAndStubbing();
}
// Default is to remove all javac generated assertion code when generating dex.
@@ -1054,11 +1059,11 @@
synthesizedClassPrefix.isEmpty()
? System.getProperty("com.android.tools.r8.synthesizedClassPrefix", "")
: synthesizedClassPrefix;
- boolean l8Shrinking = !synthesizedClassPrefix.isEmpty();
+ boolean l8Shrinking = !internal.synthesizedClassPrefix.isEmpty();
// TODO(b/214382176): Enable all the time.
internal.loadAllClassDefinitions = l8Shrinking;
if (l8Shrinking) {
- internal.apiModelingOptions().disableSubbingOfClasses();
+ internal.apiModelingOptions().disableStubbingOfClasses();
}
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
@@ -1071,6 +1076,7 @@
internal.configureAndroidPlatformBuild(getAndroidPlatformBuild());
+ internal.getArtProfileOptions().setArtProfilesForRewriting(getArtProfilesForRewriting());
internal.getStartupOptions().setStartupProfileProviders(getStartupProfileProviders());
internal.programClassConflictResolver =
diff --git a/src/main/java/com/android/tools/r8/TextOutputStream.java b/src/main/java/com/android/tools/r8/TextOutputStream.java
new file mode 100644
index 0000000..224d9da
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/TextOutputStream.java
@@ -0,0 +1,16 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+@Keep
+public interface TextOutputStream {
+
+ OutputStream getOutputStream();
+
+ Charset getCharset();
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
index eaf3a3d..1c61851 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -42,13 +42,15 @@
public abstract ComputedApiLevel computeApiLevelForDefinition(
Iterable<DexType> types, ComputedApiLevel unknownValue);
+ public abstract boolean isEnabled();
+
public ComputedApiLevel computeApiLevelForDefinition(
DexMember<?, ?> reference, DexItemFactory factory, ComputedApiLevel unknownValue) {
return computeApiLevelForDefinition(reference.getReferencedBaseTypes(factory), unknownValue);
}
public static AndroidApiLevelCompute create(AppView<?> appView) {
- return appView.options().apiModelingOptions().enableApiCallerIdentification
+ return appView.options().apiModelingOptions().enableLibraryApiModeling
? new DefaultAndroidApiLevelCompute(appView)
: noAndroidApiLevelCompute();
}
@@ -79,18 +81,23 @@
@Override
public ComputedApiLevel computeApiLevelForDefinition(
Iterable<DexType> types, ComputedApiLevel unknownValue) {
- return unknownValue;
+ return ComputedApiLevel.notSet();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
}
@Override
public ComputedApiLevel computeApiLevelForLibraryReference(
DexReference reference, ComputedApiLevel unknownValue) {
- return unknownValue;
+ return ComputedApiLevel.notSet();
}
@Override
public ComputedApiLevel computeInitialMinApiLevel(InternalOptions options) {
- return ComputedApiLevel.unknown();
+ return ComputedApiLevel.notSet();
}
}
@@ -115,6 +122,11 @@
}
@Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
public ComputedApiLevel computeApiLevelForLibraryReference(
DexReference reference, ComputedApiLevel unknownValue) {
return cache.lookup(reference, unknownValue);
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index 50855bb..c4d2d5a 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -36,7 +36,7 @@
public static AndroidApiReferenceLevelCache create(
AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
- assert appView.options().apiModelingOptions().enableApiCallerIdentification;
+ assert appView.options().apiModelingOptions().isApiLibraryModelingEnabled();
ImmutableList.Builder<AndroidApiForHashingReference> builder = ImmutableList.builder();
BiConsumer<DexReference, AndroidApiLevel> addItemToList =
ConsumerUtils.andThen(AndroidApiForHashingReference::create, builder::add);
diff --git a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
index 90fc9ee..db785cc 100644
--- a/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
+++ b/src/main/java/com/android/tools/r8/cf/TypeVerificationHelper.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
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.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -234,16 +235,15 @@
if (!instruction.isArgument()) {
break;
}
+ DexMethod reference = code.context().getReference();
TypeInfo argumentType;
if (argumentIndex < 0) {
argumentType =
- code.method().isInstanceInitializer()
- ? new ThisInstanceInfo(instruction.asArgument(), code.method().getHolderType())
- : createInitializedType(code.method().getHolderType());
+ reference.isInstanceInitializerInlineIntoOrMerged(appView)
+ ? new ThisInstanceInfo(instruction.asArgument(), reference.getHolderType())
+ : createInitializedType(reference.getHolderType());
} else {
- argumentType =
- createInitializedType(
- code.method().getReference().proto.parameters.values[argumentIndex]);
+ argumentType = createInitializedType(reference.proto.parameters.values[argumentIndex]);
}
Value outValue = instruction.outValue();
if (outValue.outType().isObject()) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index dc25d98..eab78b9 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -260,8 +260,18 @@
@Override
void internalRegisterUse(
UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
- locals.values().forEach(frameType -> internalRegisterUse(registry, frameType));
- stack.forEach(frameType -> internalRegisterUse(registry, frameType));
+ for (FrameType frameType : locals.values()) {
+ internalRegisterUse(registry, frameType);
+ if (registry.getTraversalContinuation().shouldBreak()) {
+ return;
+ }
+ }
+ for (FrameType frameType : stack) {
+ internalRegisterUse(registry, frameType);
+ if (registry.getTraversalContinuation().shouldBreak()) {
+ return;
+ }
+ }
}
private void internalRegisterUse(UseRegistry<?> registry, FrameType frameType) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
index 09cabd8..44f3fde 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrameVerifier.java
@@ -293,9 +293,7 @@
state =
state.storeLocal(
localIndex,
- context.isInstanceInitializer(dexItemFactory)
- || context.mustBeInlinedIntoInstanceInitializer(appView)
- || context.isHorizontallyMergedInstanceInitializer(dexItemFactory)
+ context.isInstanceInitializerInlineIntoOrMerged(appView)
? FrameType.uninitializedThis()
: FrameType.initializedNonNullReference(context.getHolderType()),
config);
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 3bd5968..bb281b3 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
@@ -62,8 +63,10 @@
import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsDexIndexedConsumer;
import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsDexPerFileConsumer;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OriginalSourceFiles;
import com.android.tools.r8.utils.PredicateUtils;
+import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
@@ -76,10 +79,12 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -92,7 +97,8 @@
public final InternalOptions options;
private final CodeToKeep desugaredLibraryCodeToKeep;
private final Predicate<DexType> isTypeMissing;
- public List<Marker> markers;
+ private final Optional<Marker> currentMarker;
+ public Collection<Marker> previousMarkers;
public List<DexString> markerStrings;
public Set<VirtualFile> globalSyntheticFiles;
@@ -168,21 +174,19 @@
}
}
- public ApplicationWriter(AppView<?> appView, List<Marker> markers) {
- this(appView, markers, null);
+ public ApplicationWriter(AppView<?> appView, Marker marker) {
+ this(appView, marker, null);
}
- public ApplicationWriter(
- AppView<?> appView,
- List<Marker> markers,
- DexIndexedConsumer consumer) {
+ public ApplicationWriter(AppView<?> appView, Marker marker, DexIndexedConsumer consumer) {
this.appView = appView;
this.options = appView.options();
this.desugaredLibraryCodeToKeep = CodeToKeep.createCodeToKeep(appView);
- this.markers = markers;
+ this.currentMarker = Optional.ofNullable(marker);
this.programConsumer = consumer;
this.isTypeMissing =
PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
+ this.previousMarkers = appView.dexItemFactory().extractMarkers();
}
private NamingLens getNamingLens() {
@@ -315,8 +319,8 @@
encodeChecksums(virtualFiles);
timing.end();
}
- assert markers == null
- || markers.isEmpty()
+ assert previousMarkers == null
+ || previousMarkers.isEmpty()
|| appView.dexItemFactory().extractMarkers() != null;
assert appView.withProtoShrinker(
shrinker -> virtualFiles.stream().allMatch(shrinker::verifyDeadProtoTypesNotReferenced),
@@ -403,26 +407,29 @@
private void computeMarkerStrings(
Box<ProguardMapId> delayedProguardMapId, List<LazyDexString> lazyDexStrings) {
- if (markers != null && !markers.isEmpty()) {
- int firstNonLazyMarker = 0;
- if (willComputeProguardMap()) {
- firstNonLazyMarker++;
- lazyDexStrings.add(
- new LazyDexString() {
-
- @Override
- public DexString internalCompute() {
- Marker marker = markers.get(0);
- marker.setPgMapId(delayedProguardMapId.get().getId());
- return marker.toDexString(appView.dexItemFactory());
- }
- });
- }
- markerStrings = new ArrayList<>(markers.size() - firstNonLazyMarker);
- for (int i = firstNonLazyMarker; i < markers.size(); i++) {
- markerStrings.add(markers.get(i).toDexString(appView.dexItemFactory()));
- }
+ List<Marker> allMarkers = new ArrayList<>();
+ if (previousMarkers != null) {
+ allMarkers.addAll(previousMarkers);
}
+ DexItemFactory factory = appView.dexItemFactory();
+ currentMarker.ifPresent(
+ marker -> {
+ if (willComputeProguardMap()) {
+ lazyDexStrings.add(
+ new LazyDexString() {
+
+ @Override
+ public DexString internalCompute() {
+ marker.setPgMapId(delayedProguardMapId.get().getId());
+ return marker.toDexString(factory);
+ }
+ });
+ } else {
+ allMarkers.add(marker);
+ }
+ });
+ allMarkers.sort(Comparator.comparing(Marker::toString));
+ markerStrings = ListUtils.map(allMarkers, marker -> marker.toDexString(factory));
}
private OriginalSourceFiles computeSourceFileString(
@@ -594,16 +601,19 @@
public static void supplyAdditionalConsumers(AppView<?> appView) {
InternalOptions options = appView.options();
+ Reporter reporter = options.reporter;
+ appView.getArtProfileCollection().supplyConsumers(appView);
if (options.configurationConsumer != null) {
ExceptionUtils.withConsumeResourceHandler(
- options.reporter, options.configurationConsumer,
+ reporter,
+ options.configurationConsumer,
options.getProguardConfiguration().getParsedConfiguration());
- ExceptionUtils.withFinishedResourceHandler(options.reporter, options.configurationConsumer);
+ ExceptionUtils.withFinishedResourceHandler(reporter, options.configurationConsumer);
}
if (options.mainDexListConsumer != null) {
ExceptionUtils.withConsumeResourceHandler(
- options.reporter, options.mainDexListConsumer, writeMainDexList(appView));
- ExceptionUtils.withFinishedResourceHandler(options.reporter, options.mainDexListConsumer);
+ reporter, options.mainDexListConsumer, writeMainDexList(appView));
+ ExceptionUtils.withFinishedResourceHandler(reporter, options.mainDexListConsumer);
}
KotlinModuleSynthesizer kotlinModuleSynthesizer = new KotlinModuleSynthesizer(appView);
@@ -642,13 +652,13 @@
.getBytes(),
AppServices.SERVICE_DIRECTORY_NAME + serviceName,
Origin.unknown()),
- options.reporter);
+ reporter);
});
}
// Rewrite/synthesize kotlin_module files
kotlinModuleSynthesizer
.synthesizeKotlinModuleFiles()
- .forEach(file -> dataResourceConsumer.accept(file, options.reporter));
+ .forEach(file -> dataResourceConsumer.accept(file, reporter));
}
if (options.featureSplitConfiguration != null) {
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 52eafdb..96a4596 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -1296,7 +1296,10 @@
read = dexReader.get();
os.write(read);
} while (read != 0);
- return dexItemFactory.createString(size, os.toByteArray());
+ byte[] content = os.toByteArray();
+ return Marker.hasMarkerPrefix(content)
+ ? dexItemFactory.createMarkerString(size, content)
+ : dexItemFactory.createString(size, content);
}
private DexType typeAt(int index) {
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index f2b1e15..911789d 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -4,20 +4,15 @@
package com.android.tools.r8.dex;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.errors.DesugaredLibraryMismatchDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import java.util.Comparator;
-import java.util.HashSet;
import java.util.Map.Entry;
-import java.util.Set;
/** Abstraction for hidden dex marker intended for the main dex file. */
public class Marker {
@@ -68,55 +63,6 @@
this.jsonObject = jsonObject;
}
- public static void checkCompatibleDesugaredLibrary(Set<Marker> markers, Reporter reporter) {
- if (markers.size() <= 1) {
- return;
- }
- // In L8 compilation, the compilation has two markers, a L8 marker, which has a desugared
- // library property, and either a D8 or a R8 marker, which has no desugared library property.
- // In other compilations, the desugared library versions have to be consistent.
- Set<String> desugaredLibraryIdentifiers = new HashSet<>();
- for (Marker marker : markers) {
- if (marker.tool == Tool.L8) {
- assert marker.getDesugaredLibraryIdentifiers().length > 0;
- assert markers.stream()
- .allMatch(m -> m.tool == Tool.L8 || m.getDesugaredLibraryIdentifiers().length == 0);
- } else {
- String[] identifiers = marker.getDesugaredLibraryIdentifiers();
- String identifier = null;
- switch (identifiers.length) {
- case 0:
- // Only add the <no-library-desugaring> identifier for DEX. A marker from CF is
- // assumed to go though desugaring for compiling to DEX, and that will introduce the
- // DEX marker with the final library desugaring identifier.
- if (marker.isDexBackend()) {
- identifier = NO_LIBRARY_DESUGARING;
- } else {
- assert marker.isCfBackend();
- }
- break;
- case 1:
- identifier = identifiers[0];
- break;
- default:
- // To be implemented once D8/R8 compilation supports multiple desugared libraries.
- throw reporter.fatalError(
- new StringDiagnostic(
- "Merging program compiled with multiple desugared libraries."));
- }
- if (marker.isDesugared() && identifier != null) {
- desugaredLibraryIdentifiers.add(identifier);
- } else {
- assert identifier == null || identifier.equals(NO_LIBRARY_DESUGARING);
- }
- }
- }
-
- if (desugaredLibraryIdentifiers.size() > 1) {
- reporter.error(new DesugaredLibraryMismatchDiagnostic(desugaredLibraryIdentifiers, markers));
- }
- }
-
public Tool getTool() {
return tool;
}
@@ -311,9 +257,7 @@
// Try to parse str as a marker.
// Returns null if parsing fails.
public static Marker parse(DexString dexString) {
- if (dexString.size > 2
- && dexString.content[0] == PREFIX_CHAR
- && dexString.content[1] == PREFIX_CHAR) {
+ if (hasMarkerPrefix(dexString.content)) {
String str = dexString.toString();
if (str.startsWith(D8_PREFIX)) {
return internalParse(Tool.D8, str.substring(D8_PREFIX.length() - 1));
@@ -328,6 +272,10 @@
return null;
}
+ public static boolean hasMarkerPrefix(byte[] content) {
+ return content.length > 2 && content[0] == PREFIX_CHAR && content[1] == PREFIX_CHAR;
+ }
+
private static Marker internalParse(Tool tool, String str) {
try {
JsonElement result = new JsonParser().parse(str);
diff --git a/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java b/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java
index ce54533..5a9ebb3 100644
--- a/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java
@@ -66,7 +66,7 @@
StringBuilder builder = new StringBuilder("Discard checks failed.");
if (messages.size() > 0) {
builder.append(System.lineSeparator());
- builder.append("The following items where not discarded");
+ builder.append("The following items were not discarded");
messages.forEach(
message -> {
builder.append(System.lineSeparator());
diff --git a/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java b/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
index 5141753..f23cee7 100644
--- a/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/errors/DesugaredLibraryMismatchDiagnostic.java
@@ -8,16 +8,17 @@
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
+import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
public class DesugaredLibraryMismatchDiagnostic implements Diagnostic {
private final Set<String> desugaredLibraryIdentifiers;
- private final Set<Marker> markers;
+ private final Collection<Marker> markers;
public DesugaredLibraryMismatchDiagnostic(
- Set<String> desugaredLibraryIdentifiers, Set<Marker> markers) {
+ Set<String> desugaredLibraryIdentifiers, Collection<Marker> markers) {
this.desugaredLibraryIdentifiers = desugaredLibraryIdentifiers;
this.markers = markers;
}
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 01f91e0..531c50e 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
import com.android.tools.r8.startup.StartupProfileProvider;
-import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SystemPropertyUtils;
import com.google.common.collect.ImmutableList;
import java.nio.file.Paths;
@@ -54,7 +53,7 @@
private Collection<StartupProfileProvider> startupProfileProviders;
- public StartupOptions(InternalOptions options) {
+ public StartupOptions() {
this.startupProfileProviders =
SystemPropertyUtils.applySystemProperty(
"com.android.tools.r8.startup.profile",
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
index 6dd7c65..3a63098 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
@@ -7,10 +7,8 @@
import com.android.tools.r8.TextInputStream;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.profile.art.AlwaysTrueArtProfileRulePredicate;
import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
-import com.android.tools.r8.profile.art.ArtProfileRulePredicate;
import com.android.tools.r8.profile.art.HumanReadableArtProfileParser;
import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
import com.android.tools.r8.startup.StartupClassBuilder;
@@ -19,7 +17,6 @@
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
-import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import java.util.ArrayList;
@@ -166,25 +163,14 @@
public StartupProfileBuilder addHumanReadableArtProfile(
TextInputStream textInputStream,
Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
- Box<ArtProfileRulePredicate> rulePredicateBox =
- new Box<>(new AlwaysTrueArtProfileRulePredicate());
- parserBuilderConsumer.accept(
- new HumanReadableArtProfileParserBuilder() {
- @Override
- public HumanReadableArtProfileParserBuilder setRulePredicate(
- ArtProfileRulePredicate rulePredicate) {
- rulePredicateBox.set(rulePredicate);
- return this;
- }
- });
-
- HumanReadableArtProfileParser parser =
+ HumanReadableArtProfileParser.Builder parserBuilder =
HumanReadableArtProfileParser.builder()
.setReporter(reporter)
.setProfileBuilder(
ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
- this, rulePredicateBox.get(), syntheticToSyntheticContextGeneralization))
- .build();
+ this, syntheticToSyntheticContextGeneralization));
+ parserBuilderConsumer.accept(parserBuilder);
+ HumanReadableArtProfileParser parser = parserBuilder.build();
parser.parse(textInputStream, startupProfileProvider.getOrigin());
return this;
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 1e6fb22..10620fc6 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -256,6 +256,12 @@
promote(Constants.ACC_PUBLIC);
}
+ public T withPublic() {
+ T newAccessFlags = copy();
+ newAccessFlags.promoteToPublic();
+ return newAccessFlags;
+ }
+
public void promoteToStatic() {
promote(Constants.ACC_STATIC);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 3633506..9c659f5 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.naming.SeedMapper;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.optimize.interfaces.collection.OpenClosedInterfacesCollection;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
import com.android.tools.r8.retrace.internal.RetraceUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.AssumeInfoCollection;
@@ -76,6 +77,7 @@
private T appInfo;
private AppInfoWithClassHierarchy appInfoForDesugaring;
private AppServices appServices;
+ private ArtProfileCollection artProfileCollection;
private AssumeInfoCollection assumeInfoCollection = AssumeInfoCollection.builder().build();
private final DontWarnConfiguration dontWarnConfiguration;
private final WholeProgramOptimizations wholeProgramOptimizations;
@@ -135,21 +137,25 @@
private final ComputedApiLevel computedMinApiLevel;
private AppView(
- T appInfo, WholeProgramOptimizations wholeProgramOptimizations, TypeRewriter mapper) {
- this(appInfo, wholeProgramOptimizations, mapper, Timing.empty());
+ T appInfo,
+ ArtProfileCollection artProfileCollection,
+ WholeProgramOptimizations wholeProgramOptimizations,
+ TypeRewriter mapper) {
+ this(appInfo, artProfileCollection, wholeProgramOptimizations, mapper, Timing.empty());
}
private AppView(
T appInfo,
+ ArtProfileCollection artProfileCollection,
WholeProgramOptimizations wholeProgramOptimizations,
TypeRewriter mapper,
Timing timing) {
assert appInfo != null;
+ this.appInfo = appInfo;
this.context =
timing.time(
- "Compilation context",
- () -> CompilationContext.createInitialContext(appInfo.options()));
- this.appInfo = appInfo;
+ "Compilation context", () -> CompilationContext.createInitialContext(options()));
+ this.artProfileCollection = artProfileCollection;
this.dontWarnConfiguration =
timing.time(
"Dont warn config",
@@ -192,12 +198,29 @@
}
public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
- return new AppView<>(appInfo, WholeProgramOptimizations.OFF, defaultTypeRewriter(appInfo));
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+ WholeProgramOptimizations.OFF,
+ defaultTypeRewriter(appInfo));
+ }
+
+ public static <T extends AppInfo> AppView<T> createForSimulatingD8InR8(T appInfo) {
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.empty(),
+ WholeProgramOptimizations.OFF,
+ defaultTypeRewriter(appInfo));
}
public static <T extends AppInfo> AppView<T> createForD8(
T appInfo, TypeRewriter mapper, Timing timing) {
- return new AppView<>(appInfo, WholeProgramOptimizations.OFF, mapper, timing);
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+ WholeProgramOptimizations.OFF,
+ mapper,
+ timing);
}
public static AppView<AppInfoWithClassHierarchy> createForR8(DexApplication application) {
@@ -216,20 +239,36 @@
mainDexInfo,
GlobalSyntheticsStrategy.forSingleOutputMode(),
startupOrder);
- return new AppView<>(appInfo, WholeProgramOptimizations.ON, defaultTypeRewriter(appInfo));
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.createInitialArtProfileCollection(application.options),
+ WholeProgramOptimizations.ON,
+ defaultTypeRewriter(appInfo));
}
public static <T extends AppInfo> AppView<T> createForL8(T appInfo, TypeRewriter mapper) {
- return new AppView<>(appInfo, WholeProgramOptimizations.OFF, mapper);
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+ WholeProgramOptimizations.OFF,
+ mapper);
}
public static <T extends AppInfo> AppView<T> createForRelocator(T appInfo) {
- return new AppView<>(appInfo, WholeProgramOptimizations.OFF, defaultTypeRewriter(appInfo));
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.empty(),
+ WholeProgramOptimizations.OFF,
+ defaultTypeRewriter(appInfo));
}
public static AppView<AppInfoWithClassHierarchy> createForTracer(
AppInfoWithClassHierarchy appInfo) {
- return new AppView<>(appInfo, WholeProgramOptimizations.ON, defaultTypeRewriter(appInfo));
+ return new AppView<>(
+ appInfo,
+ ArtProfileCollection.empty(),
+ WholeProgramOptimizations.ON,
+ defaultTypeRewriter(appInfo));
}
public AbstractValueFactory abstractValueFactory() {
@@ -315,6 +354,14 @@
this.appServices = appServices;
}
+ public ArtProfileCollection getArtProfileCollection() {
+ return artProfileCollection;
+ }
+
+ public void setArtProfileCollection(ArtProfileCollection artProfileCollection) {
+ this.artProfileCollection = artProfileCollection;
+ }
+
public AssumeInfoCollection getAssumeInfoCollection() {
return assumeInfoCollection;
}
@@ -772,6 +819,7 @@
if (appServices() != null) {
setAppServices(appServices().prunedCopy(prunedItems));
}
+ setArtProfileCollection(getArtProfileCollection().withoutPrunedItems(prunedItems));
setAssumeInfoCollection(getAssumeInfoCollection().withoutPrunedItems(prunedItems));
if (hasProguardCompatibilityActions()) {
setProguardCompatibilityActions(
@@ -854,10 +902,11 @@
appliedMemberRebindingLens.isMemberRebindingLens()
? appliedMemberRebindingLens
.asMemberRebindingLens()
- .toRewrittenFieldRebindingLens(appView, appliedLens)
+ .toRewrittenFieldRebindingLens(appView, appliedLens, appliedMemberRebindingLens)
: appliedMemberRebindingLens
.asMemberRebindingIdentityLens()
- .toRewrittenMemberRebindingIdentityLens(appView, appliedLens);
+ .toRewrittenMemberRebindingIdentityLens(
+ appView, appliedLens, appliedMemberRebindingLens);
}
}
@@ -870,6 +919,8 @@
.setAppInfo(appView.appInfoWithLiveness().rewrittenWithLens(application, lens));
}
appView.setAppServices(appView.appServices().rewrittenWithLens(lens));
+ appView.setArtProfileCollection(
+ appView.getArtProfileCollection().rewrittenWithLens(lens));
appView.setAssumeInfoCollection(
appView.getAssumeInfoCollection().rewrittenWithLens(appView, lens));
if (appView.hasInitClassLens()) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
index 3fe50aa..be0c645 100644
--- a/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/DexByteCodeWriter.java
@@ -12,7 +12,7 @@
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.List;
+import java.util.Collection;
import java.util.function.Consumer;
public abstract class DexByteCodeWriter {
@@ -49,7 +49,7 @@
}
public void writeMarkers(PrintStream output) {
- List<Marker> markers = application.dexItemFactory.extractMarkers();
+ Collection<Marker> markers = application.dexItemFactory.extractMarkers();
System.out.println("Number of markers: " + markers.size());
for (Marker marker : markers) {
output.println(marker.toString());
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index f6edecd..93011d5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -312,7 +312,8 @@
return builder(this)
.setField(field)
.disableAndroidApiLevelCheckIf(
- !appView.options().apiModelingOptions().enableApiCallerIdentification)
+ !appView.options().apiModelingOptions().enableApiCallerIdentification
+ || !appView.enableWholeProgramOptimizations())
.apply(consumer)
.build();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 05b5030..d68d906 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1125,6 +1125,7 @@
return syntheticBuilder(this)
.setMethod(newMethod)
.modifyAccessFlags(MethodAccessFlags::setSynthetic)
+ .setGenericSignature(MethodTypeSignature.noSignature())
// If the forwarding target is abstract, we can just create an abstract method. While it
// will not actually forward, it will create the same exception when hit at runtime.
// Otherwise, we need to create code that forwards the call to the target.
@@ -1132,7 +1133,6 @@
!isAbstract(),
builder ->
builder
- .setGenericSignature(MethodTypeSignature.noSignature())
.setCode(
ForwardMethodBuilder.builder(definitions.dexItemFactory())
.setStaticSource(newMethod)
@@ -1253,7 +1253,8 @@
if (from.hasClassFileVersion()) {
upgradeClassFileVersion(from.getClassFileVersion());
}
- if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
+ if (appView.options().apiModelingOptions().enableApiCallerIdentification
+ && appView.enableWholeProgramOptimizations()) {
apiLevelForCode = getApiLevelForCode().max(from.getApiLevelForCode());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index bfa111e..9620ee4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -52,7 +52,9 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedList;
@@ -79,6 +81,7 @@
/** Set of types that may be synthesized during compilation. */
private final Set<DexType> possibleCompilerSynthesizedTypes = Sets.newIdentityHashSet();
+ private final Map<DexString, DexString> markers = new ConcurrentHashMap<>();
private final Map<DexString, DexString> strings = new ConcurrentHashMap<>();
private final Map<DexString, DexType> types = new ConcurrentHashMap<>();
private final Map<DexField, DexField> fields = new ConcurrentHashMap<>();
@@ -2319,6 +2322,22 @@
return previous == null ? item : previous;
}
+ public DexString createMarkerString(int size, byte[] content) {
+ DexString potentialMarker = createString(size, content);
+ if (Marker.hasMarkerPrefix(potentialMarker.content)) {
+ markers.put(potentialMarker, potentialMarker);
+ }
+ return potentialMarker;
+ }
+
+ public DexString createMarkerString(String marker) {
+ DexString potentialMarker = createString(marker);
+ if (Marker.hasMarkerPrefix(potentialMarker.content)) {
+ markers.put(potentialMarker, potentialMarker);
+ }
+ return potentialMarker;
+ }
+
public DexString createString(int size, byte[] content) {
assert !sorted;
return canonicalize(strings, new DexString(size, content));
@@ -2598,10 +2617,9 @@
// Debugging support to extract marking string.
// Find all markers.
- public synchronized List<Marker> extractMarkers() {
- // This is slow but it is not needed for any production code yet.
- List<Marker> markers = new ArrayList<>();
- for (DexString dexString : strings.keySet()) {
+ public synchronized Collection<Marker> extractMarkers() {
+ Set<Marker> markers = new HashSet<>();
+ for (DexString dexString : this.markers.keySet()) {
Marker marker = Marker.parse(dexString);
if (marker != null) {
markers.add(marker);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 2fbdbdc..fe2f81d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -317,6 +317,12 @@
return getName().startsWith(dexItemFactory.syntheticConstructorMethodPrefix);
}
+ public boolean isInstanceInitializerInlineIntoOrMerged(AppView<?> appView) {
+ return isInstanceInitializer(appView.dexItemFactory())
+ || mustBeInlinedIntoInstanceInitializer(appView)
+ || isHorizontallyMergedInstanceInitializer(appView.dexItemFactory());
+ }
+
public DexMethod withExtraArgumentPrepended(DexType type, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(
holder, dexItemFactory.prependTypeToProto(type, proto), name);
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index f648f90..6b9015b 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -142,7 +142,7 @@
CfApplicationWriter.MARKER_STRING_CONSTANT_POOL_INDEX,
new char[reader.getMaxStringLength()]);
if (maybeMarker instanceof String) {
- application.getFactory().createString((String) maybeMarker);
+ application.getFactory().createMarkerString((String) maybeMarker);
}
} catch (IllegalArgumentException e) {
// Ignore if the type of the constant is not something readConst() allows.
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java
index f949105..6a72244 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescriptionMethodOptimizationInfoFixer.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.bridge.VirtualBridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.BitSet;
@@ -39,7 +38,7 @@
* changes were made.
*/
@Override
- public BridgeInfo fixupBridgeInfo(VirtualBridgeInfo bridgeInfo) {
+ public BridgeInfo fixupBridgeInfo(BridgeInfo bridgeInfo) {
if (getArgumentInfoCollection().isEmpty()) {
return bridgeInfo;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 0751f8e..ff9cb9e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -225,6 +225,8 @@
.setField(group.getClassIdField())
.setAccessFlags(FieldAccessFlags.createPublicFinalSynthetic())
.setApiLevel(appView.computedMinApiLevel())
+ .disableAndroidApiLevelCheckIf(
+ !appView.options().apiModelingOptions().isApiCallerIdentificationEnabled())
.build();
// For the $r8$classId synthesized fields, we try to over-approximate the set of values it may
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
index 162ec76..8718185 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
@@ -45,7 +45,7 @@
// of the class initializers without applying the unapplied code rewritings, to avoid that we
// apply the lens more than once to the same piece of code.
AppView<AppInfo> appViewForConversion =
- AppView.createForD8(
+ AppView.createForSimulatingD8InR8(
AppInfo.createInitialAppInfo(
appView.appInfo().app(), GlobalSyntheticsStrategy.forNonSynthesizing()));
appViewForConversion.setGraphLens(appView.graphLens());
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index f669473..19d4450 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -263,7 +263,6 @@
}
private MethodAccessFlags getNewAccessFlags() {
- // TODO(b/164998929): ensure this behaviour is correct, should probably calculate upper bound
return MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, true);
}
@@ -281,7 +280,7 @@
needsClassId,
extraNulls);
}
- if (isSingleton() && !group.hasClassIdField()) {
+ if (!useSyntheticMethod()) {
return getRepresentative().getDefinition().getCode();
}
return new ConstructorEntryPointSynthesizedCode(
@@ -328,7 +327,7 @@
// Move instance initializers to target class.
if (hasInstanceInitializerDescription()) {
lensBuilder.moveMethods(instanceInitializers, newMethodReference);
- } else if (isSingleton() && !group.hasClassIdField()) {
+ } else if (!useSyntheticMethod()) {
lensBuilder.moveMethod(representative.getReference(), newMethodReference, true);
} else {
for (ProgramMethod instanceInitializer : instanceInitializers) {
@@ -343,7 +342,8 @@
// Add a mapping from a synthetic name to the synthetic constructor.
DexMethod syntheticMethodReference =
getSyntheticMethodReference(classMethodsBuilder, newMethodReference);
- if (!isSingleton() || group.hasClassIdField()) {
+
+ if (useSyntheticMethod()) {
lensBuilder.recordNewMethodSignature(syntheticMethodReference, newMethodReference, true);
}
@@ -360,10 +360,14 @@
}
DexEncodedMethod representativeMethod = representative.getDefinition();
+ boolean useSynthethicBuilder = useSyntheticMethod() || representativeMethod.isD8R8Synthesized();
DexEncodedMethod newInstanceInitializer =
- DexEncodedMethod.syntheticBuilder()
+ (useSynthethicBuilder ? DexEncodedMethod.syntheticBuilder() : DexEncodedMethod.builder())
.setMethod(newMethodReference)
- .setAccessFlags(getNewAccessFlags())
+ .setAccessFlags(
+ useSynthethicBuilder
+ ? getNewAccessFlags()
+ : representative.getAccessFlags().withPublic())
.setCode(
getNewCode(
newMethodReference,
@@ -387,4 +391,8 @@
}
}
}
+
+ private boolean useSyntheticMethod() {
+ return !isSingleton() || group.hasClassIdField();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
index 992f73c..8605caa 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -23,7 +23,7 @@
this.appView = appView;
apiLevelCompute = appView.apiLevelCompute();
enableApiCallerIdentification =
- appView.options().apiModelingOptions().enableApiCallerIdentification;
+ appView.options().apiModelingOptions().isApiCallerIdentificationEnabled();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index 5bc0f58..e3986c8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -62,6 +63,18 @@
}
}
+ // If any field has a non-public type, then field merging may lead to check-cast instructions
+ // being synthesized. These synthesized accesses depends on the package.
+ for (DexEncodedField field : clazz.fields()) {
+ DexType fieldBaseType = field.getType().toBaseType(appView.dexItemFactory());
+ if (fieldBaseType.isClassType()) {
+ DexClass fieldBaseClass = appView.definitionFor(fieldBaseType);
+ if (fieldBaseClass == null || !fieldBaseClass.isPublic()) {
+ return true;
+ }
+ }
+ }
+
// Check that all accesses from [clazz] to classes or members from the current package of
// [clazz] will continue to work. This is guaranteed if the methods of [clazz] do not access
// any private or protected classes or members from the current package of [clazz].
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 4d9b9a0..74a3fb5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -42,6 +42,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.DependentMinimumKeepInfoCollection;
+import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
@@ -87,9 +88,10 @@
ProgramMethod dynamicMethod =
generatedMessageLiteClass.lookupProgramMethod(references.dynamicMethod);
if (dynamicMethod != null) {
- dependentMinimumKeepInfo
- .getOrCreateUnconditionalMinimumKeepInfoFor(dynamicMethod.getReference())
- .disallowOptimization();
+ disallowSignatureOptimizations(
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(dynamicMethod.getReference())
+ .asMethodJoiner());
}
references.forEachMethodReference(
@@ -98,14 +100,27 @@
asProgramClassOrNull(appView.definitionFor(reference.getHolderType()));
ProgramMethod method = reference.lookupOnProgramClass(holder);
if (method != null) {
- dependentMinimumKeepInfo
- .getOrCreateUnconditionalMinimumKeepInfoFor(method.getReference())
- .disallowOptimization();
+ disallowSignatureOptimizations(
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(method.getReference())
+ .asMethodJoiner());
}
});
}
}
+ private void disallowSignatureOptimizations(KeepMethodInfo.Joiner methodJoiner) {
+ methodJoiner
+ .disallowConstantArgumentOptimization()
+ .disallowMethodStaticizing()
+ .disallowParameterRemoval()
+ .disallowParameterReordering()
+ .disallowParameterTypeStrengthening()
+ .disallowReturnTypeStrengthening()
+ .disallowUnusedArgumentOptimization()
+ .disallowUnusedReturnValueOptimization();
+ }
+
public void run(IRCode code) {
ProgramMethod method = code.context();
if (references.isDynamicMethod(method.getReference())) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
index c83f118..dfc9eb3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoShrinker.java
@@ -78,6 +78,10 @@
return deadProtoTypes;
}
+ public ProtoReferences getProtoReferences() {
+ return references;
+ }
+
public void setDeadProtoTypes(Set<DexType> deadProtoTypes) {
// We should only need to keep track of the dead proto types for assertion purposes.
InternalOptions.checkAssertionsEnabled();
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 76e9d15..182b9b4 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
@@ -177,7 +177,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addArgument(index, knownToBeBoolean);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 14837ee..5715e54 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -17,6 +17,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;
public class ArrayLength extends Instruction {
@@ -151,4 +152,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addArrayLength(array());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 550ea8b..a214f7c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -14,6 +14,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -2114,4 +2116,19 @@
phiIt.remove();
}
}
+
+ public void registerUse(UseRegistry<?> registry, ProgramMethod method) {
+ for (Instruction instruction : instructions) {
+ instruction.registerUse(registry, method);
+ if (registry.getTraversalContinuation().shouldBreak()) {
+ return;
+ }
+ }
+ for (DexType guard : catchHandlers.getGuards()) {
+ registry.registerExceptionGuard(guard);
+ if (registry.getTraversalContinuation().shouldBreak()) {
+ return;
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
index 03771cd..6d8a713 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -29,6 +29,10 @@
public T getTarget() {
return target;
}
+
+ public DexType getGuard() {
+ return guard;
+ }
}
private final List<DexType> guards;
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index ea014e6..d9a1063 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -16,8 +16,10 @@
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -278,6 +280,11 @@
return false;
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerCheckCast(type, ignoreCompatRules);
+ }
+
public static class Builder extends BuilderBase<Builder, CheckCast> {
protected DexType castType;
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 1fd0fed..2dfbc18 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -13,8 +13,10 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -213,6 +215,11 @@
return UnknownValue.getInstance();
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerConstClass(clazz, null, ignoreCompatRules);
+ }
+
public static class Builder extends BuilderBase<Builder, ConstClass> {
private DexType type;
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 78ee74e..15afa68 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -3,15 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
+
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfConstMethodHandle;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.code.DexConstMethodHandle;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -129,4 +133,9 @@
public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
helper.storeOutValue(this, it);
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerMethodHandle(methodHandle, NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index 5ce3e31..0b586ca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -9,9 +9,11 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.code.DexConstMethodType;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -129,4 +131,9 @@
InliningConstraints inliningConstraints, ProgramMethod context) {
return inliningConstraints.forConstMethodType();
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerProto(methodType);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index 8d39b43..c462cf6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.utils.InternalOutputMode;
import com.android.tools.r8.utils.NumberUtils;
import java.util.Set;
@@ -343,4 +344,9 @@
AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod context) {
return appView.abstractValueFactory().createSingleNumberValue(value);
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addConstNumber(outType(), value);
+ }
}
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 afb3203..900fe91 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
@@ -182,7 +182,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> 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 1f87da5..651fb35 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
@@ -101,7 +101,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addDebugPosition(getPosition());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 78a582b..3a5623d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -9,9 +9,11 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -170,4 +172,12 @@
.abstractValueFactory()
.createSingleDexItemBasedStringValue(item, nameComputationInfo);
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ if (nameComputationInfo.needsToRegisterReference()) {
+ assert item.isDexType();
+ registry.registerTypeReference(item.asDexType());
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Div.java b/src/main/java/com/android/tools/r8/ir/code/Div.java
index 21031fb..4f99960 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Div.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Div.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.ir.analysis.constant.Bottom;
import com.android.tools.r8.ir.analysis.constant.LatticeElement;
+import com.android.tools.r8.lightir.LIRBuilder;
import java.util.function.Function;
public class Div extends ArithmeticBinop {
@@ -148,4 +149,9 @@
CfArithmeticBinop.Opcode getCfOpcode() {
return CfArithmeticBinop.Opcode.Div;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addDiv(type, leftValue(), rightValue());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
index 025dec0..b953a68 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Goto.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.utils.CfgPrinter;
import java.util.List;
import java.util.ListIterator;
@@ -126,6 +127,11 @@
return true;
}
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addGoto(getTarget());
+ }
+
public static class Builder extends BuilderBase<Builder, Goto> {
private BasicBlock target;
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index dbcd9be..7ec066c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -13,6 +13,7 @@
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.graph.UseRegistry;
import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
@@ -1514,4 +1515,18 @@
}
}
}
+
+ /**
+ * Note: This will discard instructions that are not present on the lower level code items, such
+ * as assume.
+ */
+ public void registerCodeReferences(ProgramMethod method, UseRegistry<ProgramMethod> registry) {
+ assert registry.getTraversalContinuation().shouldContinue();
+ for (BasicBlock block : blocks) {
+ block.registerUse(registry, method);
+ if (registry.getTraversalContinuation().shouldBreak()) {
+ return;
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index 9269104..12089ec 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LIRBuilder;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.InternalOutputMode;
@@ -289,4 +290,16 @@
assert inValues.get(0).outType() == inValues.get(1).outType();
builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())), this);
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ ValueType ifType = inValues.get(0).outType();
+ if (inValues.size() == 1) {
+ builder.addIf(type, ifType, inValues.get(0), getTrueTarget());
+ return;
+ }
+ assert inValues.size() == 2;
+ assert inValues.get(0).outType() == inValues.get(1).outType();
+ builder.addIfCmp(type, ifType, inValues, getTrueTarget());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index f331383..765d4a1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -11,8 +11,10 @@
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -171,6 +173,11 @@
return super.toString() + "; " + clazz.toSourceString();
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInitClass(clazz);
+ }
+
public static class Builder extends BuilderBase<Builder, InitClass> {
private DexType type;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 6f571d0..d01444f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -18,9 +18,11 @@
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -259,6 +261,11 @@
return true;
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInstanceFieldRead(getField());
+ }
+
public static class Builder extends BuilderBase<Builder, InstanceGet> {
private DexField field;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index 4af4442..680ace0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -9,8 +9,10 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.code.DexInstanceOf;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -128,4 +130,9 @@
StringBuilder builder = new StringBuilder(super.toString());
return builder.append("; ").append(type.toSourceString()).toString();
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInstanceOf(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index be3739e..bc4d8df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -17,11 +17,13 @@
import com.android.tools.r8.dex.code.DexIputWide;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -276,4 +278,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInstanceFieldWrite(getField());
+ }
}
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 55d96c5..c99ee11 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
@@ -10,9 +10,11 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
@@ -1557,10 +1559,18 @@
return false;
}
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
throw new Unimplemented("Missing impl for " + getClass().getSimpleName());
}
+ public void registerUse(UseRegistry registry, ProgramMethod context) {
+ internalRegisterUse(registry, context);
+ }
+
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ // Intentionally empty.
+ }
+
public static class SideEffectAssumption {
public static final SideEffectAssumption NONE = new SideEffectAssumption();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Invoke.java b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
index 786a73e..80bb77b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Invoke.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Invoke.java
@@ -116,6 +116,10 @@
MethodLookupResult lookupResult =
graphLens.lookupMethod(invokedMethod, context.getReference(), Type.DIRECT);
+ if (lookupResult.getType().isStatic()) {
+ // This method has been staticized. The original invoke-type is DIRECT.
+ return Type.DIRECT;
+ }
if (lookupResult.getType().isVirtual()) {
// This method has been publicized. The original invoke-type is DIRECT.
return Type.DIRECT;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index e43b0f0..75a13c9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -11,8 +11,10 @@
import com.android.tools.r8.dex.code.DexInvokeCustomRange;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.InterfaceCollection;
import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
@@ -204,4 +206,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return true;
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerCallSite(callSite);
+ }
}
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 25d775a..9203e93 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -217,10 +218,15 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addInvokeDirect(getInvokedMethod(), arguments());
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInvokeDirect(getInvokedMethod());
+ }
+
public static class Builder extends InvokeMethod.Builder<Builder, InvokeDirect> {
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 67e78d6..be6eac1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -140,4 +141,9 @@
return ClassInitializationAnalysis.InstructionUtils.forInvokeInterface(
this, clazz, context, appView, mode, assumption);
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInvokeInterface(getInvokedMethod());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 7ac70e7..de12658 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -11,8 +11,10 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -186,4 +188,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerTypeReference(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
index 5a3a1118..a2e815f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeNewArray.java
@@ -13,8 +13,10 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -214,4 +216,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerTypeReference(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index 9e6a918..ecc7327 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -237,6 +238,11 @@
.classInitializationMayHaveSideEffectsInContext(appView, context);
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInvokeStatic(getInvokedMethod());
+ }
+
public static class Builder extends InvokeMethod.Builder<Builder, InvokeStatic> {
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 705c7f9..ae47eb6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -134,4 +135,9 @@
return ClassInitializationAnalysis.InstructionUtils.forInvokeSuper(
this, clazz, context, appView, mode, assumption);
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInvokeSuper(getInvokedMethod());
+ }
}
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 f44d128..0cbf370 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
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -164,6 +165,11 @@
this, clazz, context, appView, mode, assumption);
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerInvokeVirtual(getInvokedMethod());
+ }
+
public static class Builder extends InvokeMethod.Builder<Builder, InvokeVirtual> {
@Override
@@ -178,7 +184,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addInvokeVirtual(getInvokedMethod(), arguments());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/MoveException.java b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
index 616882a..acd2cc4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/MoveException.java
+++ b/src/main/java/com/android/tools/r8/ir/code/MoveException.java
@@ -17,6 +17,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.utils.InternalOptions;
public class MoveException extends Instruction {
@@ -131,4 +132,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ @Override
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
+ builder.addMoveException(exceptionType);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 88bd18d..c6d527f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.dex.code.DexNewArray;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -164,4 +166,9 @@
public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
return false;
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerTypeReference(type);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 236bd0a..40e7d5d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -12,11 +12,13 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
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.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -232,6 +234,11 @@
return true;
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerNewInstance(clazz);
+ }
+
public static class Builder extends BuilderBase<Builder, NewInstance> {
private DexType type;
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
index 2274754..123b988 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -9,8 +9,10 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.code.DexNewUnboxedEnumInstance;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -159,4 +161,9 @@
assert type.isDefinitelyNotNull();
return true;
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerNewUnboxedEnumInstance(clazz);
+ }
}
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 935e9df..d8882e4 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
@@ -153,7 +153,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
if (hasReturnValue()) {
builder.addReturn(returnValue());
} else {
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 19b9b27..afad1ff 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
@@ -18,9 +18,11 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -268,6 +270,11 @@
}
}
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerStaticFieldRead(getField());
+ }
+
public static class Builder extends BuilderBase<Builder, StaticGet> {
private DexField field;
@@ -293,7 +300,7 @@
}
@Override
- public void buildLIR(LIRBuilder<Value> builder) {
+ public void buildLIR(LIRBuilder<Value, BasicBlock> builder) {
builder.addStaticGet(getField());
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 7d54dbb..7e18e04 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -16,11 +16,13 @@
import com.android.tools.r8.dex.code.DexSputWide;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.AnalysisAssumption;
import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis.Query;
@@ -250,4 +252,9 @@
return holder != context.getHolderType();
}
}
+
+ @Override
+ void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
+ registry.registerStaticFieldWrite(getField());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index a62fd2f..23e1ab3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -199,7 +199,7 @@
}
private Set<UninitializedThisLocalRead> insertUninitializedThisLocalReads() {
- if (!method.getDefinition().isInstanceInitializer()) {
+ if (!method.getReference().isInstanceInitializerInlineIntoOrMerged(appView)) {
return Collections.emptySet();
}
// Find all non-normal exit blocks.
@@ -246,11 +246,13 @@
assert initializers == null;
assert thisInitializers == null;
initializers = new HashMap<>();
+ boolean isInstanceInitializer =
+ method.getReference().isInstanceInitializerInlineIntoOrMerged(appView);
for (BasicBlock block : code.blocks) {
for (Instruction insn : block.getInstructions()) {
if (insn.isNewInstance()) {
initializers.put(insn.asNewInstance(), computeInitializers(insn.outValue()));
- } else if (insn.isArgument() && method.getDefinition().isInstanceInitializer()) {
+ } else if (insn.isArgument() && isInstanceInitializer) {
if (insn.outValue().isThis()) {
// By JVM8 §4.10.1.9 (invokespecial), a this() or super() call in a constructor
// changes the type of `this` from uninitializedThis
@@ -260,7 +262,7 @@
}
}
}
- assert !(method.getDefinition().isInstanceInitializer() && thisInitializers == null);
+ assert !(isInstanceInitializer && thisInitializers == null);
}
private List<InvokeDirect> computeInitializers(Value value) {
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 c62d786..ab044e3 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
@@ -49,6 +49,7 @@
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.AssertionErrorTwoArgsConstructorRewriter;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.AssumeInserter;
import com.android.tools.r8.ir.optimize.CheckNotNullConverter;
@@ -62,6 +63,7 @@
import com.android.tools.r8.ir.optimize.IdempotentFunctionCallCanonicalizer;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InstanceInitializerOutliner;
import com.android.tools.r8.ir.optimize.NaturalIntLoopRemover;
import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
@@ -131,6 +133,7 @@
private final InternalOptions options;
private final CfgPrinter printer;
public final CodeRewriter codeRewriter;
+ public final AssertionErrorTwoArgsConstructorRewriter assertionErrorTwoArgsConstructorRewriter;
private final NaturalIntLoopRemover naturalIntLoopRemover = new NaturalIntLoopRemover();
public final MemberValuePropagation<?> memberValuePropagation;
private final LensCodeRewriter lensCodeRewriter;
@@ -143,6 +146,7 @@
private final ServiceLoaderRewriter serviceLoaderRewriter;
private final EnumValueOptimizer enumValueOptimizer;
private final EnumUnboxer enumUnboxer;
+ private final InstanceInitializerOutliner instanceInitializerOutliner;
public final AssumeInserter assumeInserter;
private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -179,6 +183,8 @@
this.options = appView.options();
this.printer = printer;
this.codeRewriter = new CodeRewriter(appView);
+ this.assertionErrorTwoArgsConstructorRewriter =
+ new AssertionErrorTwoArgsConstructorRewriter(appView);
this.classInitializerDefaultsOptimization =
new ClassInitializerDefaultsOptimization(appView, this);
this.stringOptimizer = new StringOptimizer(appView);
@@ -227,6 +233,7 @@
this.enumValueOptimizer = null;
this.enumUnboxer = EnumUnboxer.empty();
this.assumeInserter = null;
+ this.instanceInitializerOutliner = null;
return;
}
this.instructionDesugaring =
@@ -237,6 +244,12 @@
options.processCovariantReturnTypeAnnotations
? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
: null;
+ if (appView.options().desugarState.isOn()
+ && appView.options().apiModelingOptions().enableOutliningOfMethods) {
+ this.instanceInitializerOutliner = new InstanceInitializerOutliner(appView);
+ } else {
+ this.instanceInitializerOutliner = null;
+ }
if (appView.enableWholeProgramOptimizations()) {
assert appView.appInfo().hasLiveness();
assert appView.rootSet() != null;
@@ -358,6 +371,14 @@
reportNestDesugarDependencies();
clearNestAttributes();
+ if (instanceInitializerOutliner != null) {
+ processSimpleSynthesizeMethods(instanceInitializerOutliner.getSynthesizedMethods(), executor);
+ }
+ if (assertionErrorTwoArgsConstructorRewriter != null) {
+ processSimpleSynthesizeMethods(
+ assertionErrorTwoArgsConstructorRewriter.getSynthesizedMethods(), executor);
+ }
+
application = commitPendingSyntheticItemsD8(appView, application);
postProcessingDesugaringForD8(methodProcessor, interfaceProcessor, executor);
@@ -780,10 +801,19 @@
builder.setHighestSortingString(highestSortingString);
if (serviceLoaderRewriter != null) {
- processSynthesizedServiceLoaderMethods(
+ processSimpleSynthesizeMethods(
serviceLoaderRewriter.getServiceLoadMethods(), executorService);
}
+ if (instanceInitializerOutliner != null) {
+ processSimpleSynthesizeMethods(
+ instanceInitializerOutliner.getSynthesizedMethods(), executorService);
+ }
+ if (assertionErrorTwoArgsConstructorRewriter != null) {
+ processSimpleSynthesizeMethods(
+ assertionErrorTwoArgsConstructorRewriter.getSynthesizedMethods(), executorService);
+ }
+
// Update optimization info for all synthesized methods at once.
feedback.updateVisibleOptimizationInfo();
@@ -865,14 +895,14 @@
return onWaveDoneActions != null;
}
- private void processSynthesizedServiceLoaderMethods(
+ private void processSimpleSynthesizeMethods(
List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
- serviceLoadMethods, this::forEachSynthesizedServiceLoaderMethod, executorService);
+ serviceLoadMethods, this::processAndFinalizeSimpleSynthesiedMethod, executorService);
}
- private void forEachSynthesizedServiceLoaderMethod(ProgramMethod method) {
+ private void processAndFinalizeSimpleSynthesiedMethod(ProgramMethod method) {
IRCode code = method.buildIR(appView);
assert code != null;
codeRewriter.rewriteMoveResult(code);
@@ -1208,6 +1238,12 @@
timing.end();
}
+ if (instanceInitializerOutliner != null) {
+ instanceInitializerOutliner.rewriteInstanceInitializers(
+ code, context, methodProcessingContext);
+ assert code.verifyTypes(appView);
+ }
+
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
// Update the IR code if collected call site optimization info has something useful.
@@ -1309,7 +1345,7 @@
naturalIntLoopRemover.run(appView, code);
timing.end();
timing.begin("Rewrite AssertionError");
- codeRewriter.rewriteAssertionErrorTwoArgumentConstructor(code, options);
+ assertionErrorTwoArgsConstructorRewriter.rewrite(code, methodProcessingContext);
timing.end();
timing.begin("Run CSE");
codeRewriter.commonSubexpressionElimination(code);
@@ -1620,10 +1656,14 @@
code = roundtripThroughLIR(code, feedback, bytecodeMetadataProvider, timing);
}
if (options.isGeneratingClassFiles()) {
+ timing.begin("IR->CF");
finalizeToCf(code, feedback, bytecodeMetadataProvider, timing);
+ timing.end();
} else {
assert options.isGeneratingDex();
+ timing.begin("IR->DEX");
finalizeToDex(code, feedback, bytecodeMetadataProvider, timing);
+ timing.end();
}
}
@@ -1632,8 +1672,12 @@
OptimizationFeedback feedback,
BytecodeMetadataProvider bytecodeMetadataProvider,
Timing timing) {
- LIRCode lirCode = IR2LIRConverter.translate(code);
+ timing.begin("IR->LIR");
+ LIRCode lirCode = IR2LIRConverter.translate(code, appView.dexItemFactory());
+ timing.end();
+ timing.begin("LIR->IR");
IRCode irCode = LIR2IRConverter.translate(code.context(), lirCode, appView);
+ timing.end();
return irCode;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index 6137206..67ec80a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -41,7 +41,7 @@
}
case INVOKE_CONSTRUCTOR:
{
- forwardMethodBuilder.setConstructorTarget(target, appView.dexItemFactory());
+ forwardMethodBuilder.setConstructorTargetWithNewInstance(target);
break;
}
default:
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 4412a67..e6faadc 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -255,7 +256,12 @@
assert theApi.equals(appView.options().getMinApiLevel());
return;
}
- assert theApi.equals(api.max(appView.options().getMinApiLevel()));
+ DexClass clazz =
+ appView
+ .contextIndependentDefinitionForWithResolutionResult(type)
+ .toSingleClassWithProgramOverLibrary();
+ assert theApi.equals(api.max(appView.options().getMinApiLevel()))
+ || (clazz != null && clazz.isProgramClass());
});
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index bf643ae..d2efd78 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -73,7 +73,9 @@
factory.createSynthesizedType("Ljava/lang/NumberFormatException;");
factory.createSynthesizedType("Ljava/lang/OutOfMemoryError;");
factory.createSynthesizedType("Ljava/lang/Runnable;");
+ factory.createSynthesizedType("Ljava/lang/RuntimeException;");
factory.createSynthesizedType("Ljava/lang/SecurityException;");
+ factory.createSynthesizedType("Ljava/lang/reflect/Constructor;");
factory.createSynthesizedType("Ljava/lang/reflect/InvocationTargetException;");
factory.createSynthesizedType("Ljava/lang/reflect/Method;");
factory.createSynthesizedType("Ljava/util/AbstractMap$SimpleImmutableEntry;");
@@ -117,6 +119,101 @@
factory.createSynthesizedType("[Ljava/util/Map$Entry;");
}
+ public static CfCode AssertionErrorMethods_createAssertionError(
+ DexItemFactory factory, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 5,
+ 3,
+ ImmutableList.of(
+ label0,
+ new CfConstClass(factory.createType("Ljava/lang/AssertionError;")),
+ new CfConstNumber(2, ValueType.INT),
+ new CfNewArray(factory.createType("[Ljava/lang/Class;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(0, ValueType.INT),
+ new CfConstClass(factory.stringType),
+ new CfArrayStore(MemberType.OBJECT),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(1, ValueType.INT),
+ new CfConstClass(factory.throwableType),
+ new CfArrayStore(MemberType.OBJECT),
+ label1,
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.classType,
+ factory.createProto(
+ factory.createType("Ljava/lang/reflect/Constructor;"),
+ factory.createType("[Ljava/lang/Class;")),
+ factory.createString("getDeclaredConstructor")),
+ false),
+ new CfStore(ValueType.OBJECT, 2),
+ label2,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfConstNumber(2, ValueType.INT),
+ new CfNewArray(factory.createType("[Ljava/lang/Object;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(0, ValueType.INT),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfArrayStore(MemberType.OBJECT),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(1, ValueType.INT),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfArrayStore(MemberType.OBJECT),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/reflect/Constructor;"),
+ factory.createProto(
+ factory.objectType, factory.createType("[Ljava/lang/Object;")),
+ factory.createString("newInstance")),
+ false),
+ new CfCheckCast(factory.createType("Ljava/lang/AssertionError;")),
+ label3,
+ new CfReturn(ValueType.OBJECT),
+ label4,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(factory.stringType),
+ FrameType.initializedNonNullReference(factory.throwableType)
+ }),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/lang/Exception;"))))),
+ new CfStore(ValueType.OBJECT, 2),
+ label5,
+ new CfNew(factory.createType("Ljava/lang/AssertionError;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 183,
+ factory.createMethod(
+ factory.createType("Ljava/lang/AssertionError;"),
+ factory.createProto(factory.voidType, factory.objectType),
+ factory.createString("<init>")),
+ false),
+ new CfReturn(ValueType.OBJECT),
+ label6),
+ ImmutableList.of(
+ new CfTryCatch(
+ label0,
+ label3,
+ ImmutableList.of(factory.createType("Ljava/lang/Exception;")),
+ ImmutableList.of(label4))),
+ ImmutableList.of());
+ }
+
public static CfCode AtomicReferenceArrayMethods_compareAndSet(
DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
@@ -781,7 +878,7 @@
factory.createType("Ljava/lang/Exception;"))))),
new CfStore(ValueType.OBJECT, 2),
label6,
- new CfNew(factory.createType("Ljava/lang/AssertionError;")),
+ new CfNew(factory.createType("Ljava/lang/RuntimeException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(factory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -826,7 +923,7 @@
new CfInvoke(
183,
factory.createMethod(
- factory.createType("Ljava/lang/AssertionError;"),
+ factory.createType("Ljava/lang/RuntimeException;"),
factory.createProto(
factory.voidType, factory.stringType, factory.throwableType),
factory.createString("<init>")),
@@ -844,7 +941,7 @@
Arrays.asList(FrameType.initializedNonNullReference(factory.throwableType)))),
new CfStore(ValueType.OBJECT, 2),
label8,
- new CfNew(factory.createType("Ljava/lang/AssertionError;")),
+ new CfNew(factory.createType("Ljava/lang/RuntimeException;")),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
new CfNew(factory.stringBuilderType),
new CfStackInstruction(CfStackInstruction.Opcode.Dup),
@@ -889,7 +986,7 @@
new CfInvoke(
183,
factory.createMethod(
- factory.createType("Ljava/lang/AssertionError;"),
+ factory.createType("Ljava/lang/RuntimeException;"),
factory.createProto(
factory.voidType, factory.stringType, factory.throwableType),
factory.createString("<init>")),
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 520533b..a1a8d29 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
@@ -91,7 +91,7 @@
(k, v) -> {
for (DexMethod dexMethod : v) {
if (dexMethod != null) {
- registerClassType(dexMethod.getHolderType());
+ registerType(dexMethod.getHolderType());
}
}
});
@@ -159,9 +159,13 @@
type.descriptor.withNewPrefix(prefix, k, appInfo.dexItemFactory());
DexString rewrittenTypeDescriptor =
type.descriptor.withNewPrefix(prefix, v, appInfo.dexItemFactory());
+ DexType newKey = appInfo.dexItemFactory().createType(typeDescriptor);
+ assert appInfo.definitionForWithoutExistenceAssert(newKey) == null
+ : "Trying to rewrite a type "
+ + newKey
+ + " with different prefix that already exists.";
builder.rewriteType(
- appInfo.dexItemFactory().createType(typeDescriptor),
- appInfo.dexItemFactory().createType(rewrittenTypeDescriptor));
+ newKey, appInfo.dexItemFactory().createType(rewrittenTypeDescriptor));
});
usedPrefix.add(prefix);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index c0b124b..b251add 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -67,7 +67,7 @@
Map<ApiLevelRange, HumanRewritingFlags> libraryFlags =
convertRewritingFlagMap(legacySpec.getLibraryFlags(), app, origin);
- legacyLibraryFlagHacks(libraryFlags, app, origin);
+ legacyLibraryFlagHacks(humanTopLevelFlags.getIdentifier(), libraryFlags, app, origin);
reportWarnings(app.options.reporter);
MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
@@ -94,12 +94,12 @@
Origin origin = Origin.unknown();
HumanRewritingFlags humanRewritingFlags =
convertRewritingFlags(legacySpec.getRewritingFlags(), app, origin);
- if (app.options.getMinApiLevel().isLessThanOrEqualTo(LEGACY_HACK_LEVEL)
- && legacySpec.isLibraryCompilation()) {
+ if (legacySpec.isLibraryCompilation()) {
timing.begin("Legacy hacks");
HumanRewritingFlags.Builder builder =
humanRewritingFlags.newBuilder(app.options.reporter, origin);
- legacyLibraryFlagHacks(app.dexItemFactory(), builder);
+ legacyLibraryFlagHacks(
+ legacySpec.getIdentifier(), app.dexItemFactory(), app.options.getMinApiLevel(), builder);
humanRewritingFlags = builder.build();
timing.end();
}
@@ -128,7 +128,10 @@
}
private void legacyLibraryFlagHacks(
- Map<ApiLevelRange, HumanRewritingFlags> libraryFlags, DexApplication app, Origin origin) {
+ String identifier,
+ Map<ApiLevelRange, HumanRewritingFlags> libraryFlags,
+ DexApplication app,
+ Origin origin) {
ApiLevelRange range = new ApiLevelRange(LEGACY_HACK_LEVEL.getLevel());
HumanRewritingFlags humanRewritingFlags = libraryFlags.get(range);
if (humanRewritingFlags == null) {
@@ -137,39 +140,52 @@
}
HumanRewritingFlags.Builder builder =
humanRewritingFlags.newBuilder(app.options.reporter, origin);
- legacyLibraryFlagHacks(app.dexItemFactory(), builder);
+ legacyLibraryFlagHacks(identifier, app.dexItemFactory(), LEGACY_HACK_LEVEL, builder);
libraryFlags.put(range, builder.build());
}
private void legacyLibraryFlagHacks(
- DexItemFactory itemFactory, HumanRewritingFlags.Builder builder) {
+ String identifier,
+ DexItemFactory itemFactory,
+ AndroidApiLevel apiLevel,
+ HumanRewritingFlags.Builder builder) {
- // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
- // to j.u.DesugarArrays.deepEquals0.
- DexString name = itemFactory.createString("deepEquals0");
- DexProto proto =
- itemFactory.createProto(
- itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
- DexMethod source =
- itemFactory.createMethod(itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
- DexType target = itemFactory.createType("Ljava/util/DesugarArrays;");
- builder.retargetMethod(source, target);
+ if (apiLevel.isLessThanOrEqualTo(LEGACY_HACK_LEVEL)) {
+ // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
+ // to j.u.DesugarArrays.deepEquals0.
+ DexString name = itemFactory.createString("deepEquals0");
+ DexProto proto =
+ itemFactory.createProto(
+ itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
+ DexMethod source =
+ itemFactory.createMethod(
+ itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
+ DexType target = itemFactory.createType("Ljava/util/DesugarArrays;");
+ builder.retargetMethod(source, target);
- builder.amendLibraryMethod(
- source,
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PRIVATE | Constants.ACC_STATIC, false));
+ builder.amendLibraryMethod(
+ source,
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PRIVATE | Constants.ACC_STATIC, false));
- // TODO(b/181629049): This is only a workaround rewriting invokes of
- // j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
- name = itemFactory.createString("getTimeZone");
- proto =
- itemFactory.createProto(
- itemFactory.createType("Ljava/util/TimeZone;"),
- itemFactory.createType("Ljava/time/ZoneId;"));
- source = itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
- target = itemFactory.createType("Ljava/util/DesugarTimeZone;");
- builder.retargetMethod(source, target);
+ // TODO(b/181629049): This is only a workaround rewriting invokes of
+ // j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
+ name = itemFactory.createString("getTimeZone");
+ proto =
+ itemFactory.createProto(
+ itemFactory.createType("Ljava/util/TimeZone;"),
+ itemFactory.createType("Ljava/time/ZoneId;"));
+ source =
+ itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
+ target = itemFactory.createType("Ljava/util/DesugarTimeZone;");
+ builder.retargetMethod(source, target);
+ }
+ // Required by
+ // https://github.com/google/desugar_jdk_libs/commit/485071cd09a3691549d065ba9e323d07edccf085.
+ if (identifier.contains(":1.2")) {
+ builder.putRewriteDerivedPrefix(
+ "sun.misc.Desugar", "jdk.internal.misc.", "j$.sun.misc.Desugar");
+ }
}
private Map<ApiLevelRange, HumanRewritingFlags> convertRewritingFlagMap(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
new file mode 100644
index 0000000..06d9d8f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionErrorTwoArgsConstructorRewriter.java
@@ -0,0 +1,109 @@
+// 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.optimize;
+
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+public class AssertionErrorTwoArgsConstructorRewriter {
+
+ private final AppView<?> appView;
+ private final DexItemFactory dexItemFactory;
+ private final InternalOptions options;
+
+ public AssertionErrorTwoArgsConstructorRewriter(AppView<?> appView) {
+ this.appView = appView;
+ this.options = appView.options();
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ public void rewrite(IRCode code, MethodProcessingContext methodProcessingContext) {
+ if (options.canUseAssertionErrorTwoArgumentConstructor()) {
+ return;
+ }
+
+ ListIterator<BasicBlock> blockIterator = code.listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ InstructionListIterator insnIterator = block.listIterator(code);
+ while (insnIterator.hasNext()) {
+ Instruction current = insnIterator.next();
+ if (current.isInvokeMethod()) {
+ DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
+ if (invokedMethod == dexItemFactory.assertionErrorMethods.initMessageAndCause) {
+ List<Value> inValues = current.inValues();
+ assert inValues.size() == 3; // receiver, message, cause
+
+ Value assertionError =
+ code.createValue(
+ TypeElement.fromDexType(
+ dexItemFactory.assertionErrorType,
+ Nullability.definitelyNotNull(),
+ appView));
+ Instruction invoke =
+ new InvokeStatic(
+ createSynthetic(methodProcessingContext).getReference(),
+ assertionError,
+ inValues.subList(1, 3));
+ insnIterator.replaceCurrentInstruction(invoke);
+ inValues.get(0).replaceUsers(assertionError);
+ inValues.get(0).definition.removeOrReplaceByDebugLocalRead(code);
+ }
+ }
+ }
+ }
+ assert code.isConsistentSSA(appView);
+ }
+
+ private final List<ProgramMethod> synthesizedMethods = new ArrayList<>();
+
+ public List<ProgramMethod> getSynthesizedMethods() {
+ return synthesizedMethods;
+ }
+
+ private ProgramMethod createSynthetic(MethodProcessingContext methodProcessingContext) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto proto =
+ factory.createProto(factory.assertionErrorType, factory.stringType, factory.throwableType);
+ ProgramMethod method =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ kinds -> kinds.BACKPORT,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setApiLevelForCode(appView.computedMinApiLevel())
+ .setProto(proto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ methodSig ->
+ BackportedMethods.AssertionErrorMethods_createAssertionError(
+ factory, methodSig)));
+ synchronized (synthesizedMethods) {
+ synthesizedMethods.add(method);
+ }
+ return method;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 36b96dc..4d31397 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -138,7 +138,6 @@
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
@@ -3424,55 +3423,6 @@
assert code.isConsistentSSA(appView);
}
- public void rewriteAssertionErrorTwoArgumentConstructor(IRCode code, InternalOptions options) {
- if (options.canUseAssertionErrorTwoArgumentConstructor()) {
- return;
- }
-
- ListIterator<BasicBlock> blockIterator = code.listIterator();
- while (blockIterator.hasNext()) {
- BasicBlock block = blockIterator.next();
- InstructionListIterator insnIterator = block.listIterator(code);
- while (insnIterator.hasNext()) {
- Instruction current = insnIterator.next();
- if (current.isInvokeMethod()) {
- DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
- if (invokedMethod == dexItemFactory.assertionErrorMethods.initMessageAndCause) {
- // Rewrite calls to new AssertionError(message, cause) to new AssertionError(message)
- // and then initCause(cause).
- List<Value> inValues = current.inValues();
- assert inValues.size() == 3; // receiver, message, cause
-
- // Remove cause from the constructor call
- List<Value> newInitInValues = inValues.subList(0, 2);
- insnIterator.replaceCurrentInstruction(
- new InvokeDirect(
- dexItemFactory.assertionErrorMethods.initMessage, null, newInitInValues));
-
- // On API 15 and older we cannot use initCause because of a bug in AssertionError.
- if (options.canInitCauseAfterAssertionErrorObjectConstructor()) {
- // Add a call to Throwable.initCause(cause)
- if (block.hasCatchHandlers()) {
- insnIterator = insnIterator.split(code, blockIterator).listIterator(code);
- }
- List<Value> initCauseArguments = Arrays.asList(inValues.get(0), inValues.get(2));
- InvokeVirtual initCause =
- new InvokeVirtual(
- dexItemFactory.throwableMethods.initCause,
- code.createValue(
- TypeElement.fromDexType(
- dexItemFactory.throwableType, maybeNull(), appView)),
- initCauseArguments);
- initCause.setPosition(current.getPosition());
- insnIterator.add(initCause);
- }
- }
- }
- }
- }
- assert code.isConsistentSSA(appView);
- }
-
/**
* Remove moves that are not actually used by instructions in exiting paths. These moves can arise
* due to debug local info needing a particular value and the live-interval for it then moves it
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index 5275a80..eb7671a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -139,11 +139,9 @@
private Set<BasicBlock> computeDirectAndIndirectCatchHandlerBlocks() {
WorkList<BasicBlock> catchHandlerBlocks = WorkList.newIdentityWorkList();
- for (BasicBlock block : code.getBlocks()) {
- if (block.entry().isMoveException()) {
- catchHandlerBlocks.addIfNotSeen(block);
- }
- }
+ code.getBlocks()
+ .forEach(
+ block -> catchHandlerBlocks.addIfNotSeen(block.getCatchHandlers().getAllTargets()));
while (catchHandlerBlocks.hasNext()) {
BasicBlock block = catchHandlerBlocks.next();
catchHandlerBlocks.addIfNotSeen(block.getSuccessors());
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 ac4c0ef..69d51be 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
@@ -446,26 +446,6 @@
}
@Override
- public boolean allowInliningOfInvokeInInlinee(
- InlineAction action,
- int inliningDepth,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- assert inliningDepth > 0;
-
- if (action.reason.mustBeInlined()) {
- return true;
- }
-
- int threshold = inlinerOptions.applyInliningToInlineeMaxDepth;
- if (inliningDepth <= threshold) {
- return true;
- }
-
- whyAreYouNotInliningReporter.reportWillExceedMaxInliningDepth(inliningDepth, threshold);
- return false;
- }
-
- @Override
public boolean canInlineInstanceInitializer(
IRCode code,
InvokeDirect invoke,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
index be64b57..e37d406 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -101,15 +101,6 @@
}
@Override
- public boolean allowInliningOfInvokeInInlinee(
- InlineAction action,
- int inliningDepth,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- // The purpose of force inlining is generally to inline a given invoke-instruction in the IR.
- return false;
- }
-
- @Override
public boolean canInlineInstanceInitializer(
IRCode code,
InvokeDirect invoke,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 9a38d14..592b645 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -1014,13 +1014,6 @@
continue;
}
- if (!inlineeStack.isEmpty()
- && !strategy.allowInliningOfInvokeInInlinee(
- action, inlineeStack.size(), whyAreYouNotInliningReporter)) {
- assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
- continue;
- }
-
if (!strategy.stillHasBudget(action, whyAreYouNotInliningReporter)) {
assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag();
continue;
@@ -1084,17 +1077,16 @@
context.getDefinition().copyMetadata(appView, singleTargetMethod);
- if (inlineeMayHaveInvokeMethod && options.applyInliningToInlinee) {
- if (inlineeStack.size() + 1 > options.applyInliningToInlineeMaxDepth
- && appView.appInfo().hasNoAlwaysInlineMethods()) {
- continue;
+ if (inlineeMayHaveInvokeMethod) {
+ int inliningDepth = inlineeStack.size() + 1;
+ if (options.shouldApplyInliningToInlinee(appView, singleTarget, inliningDepth)) {
+ // Record that we will be inside the inlinee until the next block.
+ BasicBlock inlineeEnd = IteratorUtils.peekNext(blockIterator);
+ inlineeStack.push(inlineeEnd);
+ // Move the cursor back to where the first inlinee block was added.
+ IteratorUtils.previousUntil(blockIterator, previous -> previous == block);
+ blockIterator.next();
}
- // Record that we will be inside the inlinee until the next block.
- BasicBlock inlineeEnd = IteratorUtils.peekNext(blockIterator);
- inlineeStack.push(inlineeEnd);
- // Move the cursor back to where the first inlinee block was added.
- IteratorUtils.previousUntil(blockIterator, previous -> previous == block);
- blockIterator.next();
}
} else if (current.isAssume()) {
assumeRemover.removeIfMarked(current.asAssume(), iterator);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
index 564fcf8..b76544b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningStrategy.java
@@ -24,11 +24,6 @@
AppView<AppInfoWithLiveness> appView();
- boolean allowInliningOfInvokeInInlinee(
- InlineAction action,
- int inliningDepth,
- WhyAreYouNotInliningReporter whyAreYouNotInliningReporter);
-
boolean canInlineInstanceInitializer(
IRCode code,
InvokeDirect invoke,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
new file mode 100644
index 0000000..9aa717b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
@@ -0,0 +1,240 @@
+// 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.optimize;
+
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
+import com.android.tools.r8.ir.synthetic.NewInstanceSourceCode;
+import com.android.tools.r8.shaking.ComputeApiLevelUseRegistry;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The InstanceInitializerOutliner will outline instance initializers and their NewInstance source.
+ * Unlike the ApiInvokeOutlinerDesugaring that works on CF, this works on IR to properly replace the
+ * users of the NewInstance call.
+ */
+public class InstanceInitializerOutliner {
+
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
+
+ private final List<ProgramMethod> synthesizedMethods = new ArrayList<>();
+
+ public InstanceInitializerOutliner(AppView<?> appView) {
+ this.appView = appView;
+ this.factory = appView.dexItemFactory();
+ }
+
+ public List<ProgramMethod> getSynthesizedMethods() {
+ return synthesizedMethods;
+ }
+
+ public void rewriteInstanceInitializers(
+ IRCode code, ProgramMethod context, MethodProcessingContext methodProcessingContext) {
+ // Do not outline from already synthesized methods.
+ if (context.getDefinition().isD8R8Synthesized()) {
+ return;
+ }
+ Map<NewInstance, Value> rewrittenNewInstances = new IdentityHashMap<>();
+ ComputedApiLevel minApiLevel = appView.computedMinApiLevel();
+ InstructionListIterator iterator = code.instructionListIterator();
+ // Scan over the code to find <init> calls that needs to be outlined.
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ InvokeDirect invokeDirect = instruction.asInvokeDirect();
+ if (invokeDirect == null) {
+ continue;
+ }
+ DexMethod invokedConstructor = invokeDirect.getInvokedMethod();
+ if (!invokedConstructor.isInstanceInitializer(factory)) {
+ continue;
+ }
+ Value firstOperand = invokeDirect.getFirstOperand();
+ if (firstOperand.isPhi()) {
+ continue;
+ }
+ NewInstance newInstance = firstOperand.getDefinition().asNewInstance();
+ if (newInstance == null) {
+ // We could not find a new instance call associated with the init, this is probably a
+ // constructor call to the super class.
+ continue;
+ }
+ ComputedApiLevel apiReferenceLevel =
+ appView
+ .apiLevelCompute()
+ .computeApiLevelForLibraryReference(invokedConstructor, minApiLevel);
+ if (minApiLevel.isGreaterThanOrEqualTo(apiReferenceLevel)) {
+ continue;
+ }
+ DexEncodedMethod synthesizedInstanceInitializer =
+ createSynthesizedInstanceInitializer(
+ invokeDirect.getInvokedMethod(), apiReferenceLevel, methodProcessingContext);
+ List<Value> arguments = instruction.inValues();
+ InvokeStatic outlinedMethodInvoke =
+ InvokeStatic.builder()
+ .setMethod(synthesizedInstanceInitializer.getReference())
+ .setPosition(instruction)
+ .setFreshOutValue(code, newInstance.getOutType())
+ .setArguments(arguments.subList(1, arguments.size()))
+ .build();
+ iterator.replaceCurrentInstruction(outlinedMethodInvoke);
+ rewrittenNewInstances.put(newInstance, outlinedMethodInvoke.outValue());
+ }
+ if (rewrittenNewInstances.isEmpty()) {
+ return;
+ }
+ // Scan over NewInstance calls that needs to be outlined. We insert a call to a synthetic method
+ // with a NewInstance to preserve class-init semantics.
+ // TODO(b/244284945): If we know that arguments to an init cannot change class initializer
+ // semantics we can avoid inserting the NewInstance outline.
+ iterator = code.instructionListIterator();
+ Set<Value> newOutValues = Sets.newIdentityHashSet();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (!instruction.isNewInstance()) {
+ continue;
+ }
+ NewInstance newInstance = instruction.asNewInstance();
+ Value newOutlineInstanceValue = rewrittenNewInstances.get(newInstance);
+ if (newOutlineInstanceValue == null) {
+ continue;
+ }
+ ComputedApiLevel classApiLevel =
+ appView
+ .apiLevelCompute()
+ .computeApiLevelForLibraryReference(newInstance.getType(), minApiLevel);
+ assert classApiLevel.isKnownApiLevel();
+ DexEncodedMethod synthesizedNewInstance =
+ createSynthesizedNewInstance(
+ newInstance.getType(), classApiLevel, methodProcessingContext);
+ InvokeStatic outlinedStaticInit =
+ InvokeStatic.builder()
+ .setMethod(synthesizedNewInstance.getReference())
+ .setPosition(instruction)
+ .build();
+ newInstance.outValue().replaceUsers(newOutlineInstanceValue);
+ newOutValues.add(newOutlineInstanceValue);
+ iterator.replaceCurrentInstruction(outlinedStaticInit);
+ }
+ // We are changing a NewInstance to a method call where we loose that the type is not null.
+ assert !newOutValues.isEmpty();
+ new TypeAnalysis(appView).widening(newOutValues);
+
+ // Outlining of instance initializers will in most cases change the api level of the context
+ // since all other soft verification issues has been outlined. To ensure that we do not inline
+ // the outline again in R8 - but allow inlining of other calls to min api level methods, we have
+ // to recompute the api level.
+ recomputeApiLevel(context, code);
+ }
+
+ private void recomputeApiLevel(ProgramMethod context, IRCode code) {
+ DexEncodedMethod definition = context.getDefinition();
+ if (!definition.getApiLevelForCode().isKnownApiLevel()) {
+ // This is either D8 or the api level is unknown.
+ return;
+ }
+ ComputeApiLevelUseRegistry registry =
+ new ComputeApiLevelUseRegistry(appView, context, appView.apiLevelCompute());
+ code.registerCodeReferences(context, registry);
+ ComputedApiLevel maxApiReferenceLevel = registry.getMaxApiReferenceLevel();
+ assert maxApiReferenceLevel.isKnownApiLevel();
+ definition.setApiLevelForCode(maxApiReferenceLevel);
+ }
+
+ private DexEncodedMethod createSynthesizedNewInstance(
+ DexType targetType,
+ ComputedApiLevel computedApiLevel,
+ MethodProcessingContext methodProcessingContext) {
+ DexProto proto = appView.dexItemFactory().createProto(factory.voidType);
+ ProgramMethod method =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ kinds -> kinds.API_MODEL_OUTLINE,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setProto(proto)
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(computedApiLevel)
+ .setCode(
+ m ->
+ NewInstanceSourceCode.create(appView, m.getHolderType(), targetType)
+ .generateCfCode()));
+ synchronized (synthesizedMethods) {
+ synthesizedMethods.add(method);
+ }
+ return method.getDefinition();
+ }
+
+ private DexEncodedMethod createSynthesizedInstanceInitializer(
+ DexMethod targetMethod,
+ ComputedApiLevel computedApiLevel,
+ MethodProcessingContext methodProcessingContext) {
+ DexProto proto =
+ appView
+ .dexItemFactory()
+ .createProto(targetMethod.getHolderType(), targetMethod.getParameters());
+ ProgramMethod method =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ kinds -> kinds.API_MODEL_OUTLINE,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setProto(proto)
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(computedApiLevel)
+ .setCode(
+ m ->
+ ForwardMethodBuilder.builder(appView.dexItemFactory())
+ .setConstructorTargetWithNewInstance(targetMethod)
+ .setStaticSource(m)
+ .build()));
+
+ synchronized (synthesizedMethods) {
+ synthesizedMethods.add(method);
+ ClassTypeElement exactType =
+ targetMethod
+ .getHolderType()
+ .toTypeElement(appView, Nullability.definitelyNotNull())
+ .asClassType();
+ OptimizationFeedback.getSimpleFeedback()
+ .setDynamicReturnType(method, appView, DynamicType.createExact(exactType));
+ }
+ return method.getDefinition();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index e19e0b6..44eed32 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -125,7 +125,7 @@
localUtilityClass.getDefinition().setDirectMethods(localUtilityMethods);
localUtilityClass.getDefinition().setStaticFields(localUtilityFields);
} else {
- clazz.getMethodCollection().replaceMethods(method -> fixupEncodedMethod(clazz, method));
+ clazz.getMethodCollection().replaceMethods(this::fixupEncodedMethod);
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
@@ -561,7 +561,7 @@
&& !field.getDefinition().getOptimizationInfo().isDead());
}
- private DexEncodedMethod fixupEncodedMethod(DexProgramClass holder, DexEncodedMethod method) {
+ private DexEncodedMethod fixupEncodedMethod(DexEncodedMethod method) {
DexProto oldProto = method.getProto();
DexProto newProto = fixupProto(oldProto);
if (newProto == method.getProto()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java
index 12c5b37..0044499 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoFixer.java
@@ -10,14 +10,13 @@
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.enums.classification.EnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
-import com.android.tools.r8.ir.optimize.info.bridge.VirtualBridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.BitSet;
public abstract class MethodOptimizationInfoFixer {
- public abstract BridgeInfo fixupBridgeInfo(VirtualBridgeInfo bridgeInfo);
+ public abstract BridgeInfo fixupBridgeInfo(BridgeInfo bridgeInfo);
public abstract CallSiteOptimizationInfo fixupCallSiteOptimizationInfo(
ConcreteCallSiteOptimizationInfo callSiteOptimizationInfo);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 3491800..2e760d1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -396,8 +396,7 @@
public MutableMethodOptimizationInfo fixupBridgeInfo(MethodOptimizationInfoFixer fixer) {
if (bridgeInfo != null) {
- assert bridgeInfo.isVirtualBridgeInfo();
- bridgeInfo = fixer.fixupBridgeInfo(bridgeInfo.asVirtualBridgeInfo());
+ bridgeInfo = fixer.fixupBridgeInfo(bridgeInfo);
}
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
index 9eeb579..be806b4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
@@ -14,7 +15,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.utils.BooleanUtils;
@@ -23,8 +24,7 @@
/** Returns a {@link BridgeInfo} object describing this method if it is recognized as a bridge. */
public static BridgeInfo analyzeMethod(DexEncodedMethod method, IRCode code) {
- // TODO(b/154263783): Consider computing BridgeInfo also for non-declared bridges.
- if (!method.isBridge() || code.blocks.size() > 1) {
+ if (code.blocks.size() > 1) {
return failure();
}
@@ -32,7 +32,7 @@
// followed by a (possibly empty) sequence of CheckCast instructions, followed by a single
// InvokeMethod instruction, followed by an optional CheckCast instruction, followed by a Return
// instruction.
- InvokeVirtual uniqueInvoke = null;
+ InvokeMethodWithReceiver uniqueInvoke = null;
CheckCast uniqueReturnCast = null;
for (Instruction instruction : code.entryBlock().getInstructions()) {
switch (instruction.opcode()) {
@@ -59,13 +59,14 @@
break;
}
+ case INVOKE_DIRECT:
case INVOKE_VIRTUAL:
{
if (uniqueInvoke != null) {
return failure();
}
- InvokeVirtual invoke = instruction.asInvokeVirtual();
- if (!analyzeInvokeVirtual(invoke)) {
+ InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
+ if (!analyzeInvoke(invoke)) {
return failure();
}
// Record that we have seen the single invoke instruction.
@@ -85,7 +86,10 @@
}
assert uniqueInvoke != null;
- return new VirtualBridgeInfo(uniqueInvoke.getInvokedMethod());
+ assert uniqueInvoke.isInvokeDirect() || uniqueInvoke.isInvokeVirtual();
+ return uniqueInvoke.isInvokeDirect()
+ ? new DirectBridgeInfo(uniqueInvoke.getInvokedMethod())
+ : new VirtualBridgeInfo(uniqueInvoke.getInvokedMethod());
}
private static boolean analyzeCheckCast(
@@ -148,11 +152,12 @@
&& castValue.singleUniqueUser().isReturn();
}
- private static boolean analyzeInvokeVirtual(InvokeVirtual invoke) {
+ private static boolean analyzeInvoke(InvokeMethodWithReceiver invoke) {
// All of the forwarded arguments of the enclosing method must be in the same argument position.
for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) {
Value argument = invoke.getArgument(argumentIndex);
- if (argument.isArgument() && argumentIndex != argument.definition.asArgument().getIndex()) {
+ if (argument.isArgument()
+ && argumentIndex != argument.getDefinition().asArgument().getIndex()) {
return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeInfo.java
index 2683d2f..3848c7e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeInfo.java
@@ -9,6 +9,14 @@
*/
public abstract class BridgeInfo {
+ public boolean isDirectBridgeInfo() {
+ return false;
+ }
+
+ public DirectBridgeInfo asDirectBridgeInfo() {
+ return null;
+ }
+
public boolean isVirtualBridgeInfo() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/DirectBridgeInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/DirectBridgeInfo.java
new file mode 100644
index 0000000..3579edf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/DirectBridgeInfo.java
@@ -0,0 +1,49 @@
+// 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.optimize.info.bridge;
+
+import com.android.tools.r8.graph.DexMethod;
+
+/**
+ * Optimization info computed for bridge methods that use an invoke-direct instruction.
+ *
+ * <p>If the method is {@code String A.m(Object)} and {@link #invokedMethod} is {@code Object
+ * B.m(String)}, then the bridge is implemented as:
+ *
+ * <pre>
+ * java.lang.String A.m(java.lang.Object o) {
+ * v0 <- Argument
+ * v1 <- CheckCast v0, java.lang.String
+ * v2 <- InvokeDirect { v0, v1 }, java.lang.Object B.m(java.lang.String)
+ * v3 <- CheckCast v2, java.lang.String
+ * Return v3
+ * }
+ * </pre>
+ *
+ * <p>This currently does not allow any permutation of the argument order, and it also does not
+ * allow constants to be passed as arguments.
+ */
+public class DirectBridgeInfo extends BridgeInfo {
+
+ // The targeted method.
+ private final DexMethod invokedMethod;
+
+ public DirectBridgeInfo(DexMethod invokedMethod) {
+ this.invokedMethod = invokedMethod;
+ }
+
+ public DexMethod getInvokedMethod() {
+ return invokedMethod;
+ }
+
+ @Override
+ public boolean isDirectBridgeInfo() {
+ return true;
+ }
+
+ @Override
+ public DirectBridgeInfo asDirectBridgeInfo() {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
deleted file mode 100644
index 080b519..0000000
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.synthetic;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.Collections;
-
-// Source code representing simple forwarding method.
-public final class ExceptionThrowingSourceCode extends SyntheticSourceCode {
-
- private static final int register = 0;
- private final DexType exceptionType;
-
- public ExceptionThrowingSourceCode(
- DexType receiver, DexMethod method, Position callerPosition, DexType exceptionType) {
- super(receiver, method, callerPosition);
- this.exceptionType = exceptionType;
- }
-
- @Override
- protected void prepareInstructions() {
- add(builder -> build(builder, exceptionType));
- }
-
- public static void build(IRBuilder builder, DexType exceptionType) {
- DexItemFactory factory = builder.appView.dexItemFactory();
- DexProto initProto = factory.createProto(factory.voidType);
- DexMethod initMethod =
- factory.createMethod(exceptionType, initProto, factory.constructorMethodName);
- builder.addNewInstance(register, exceptionType);
- builder.addInvoke(
- Type.DIRECT,
- initMethod,
- initMethod.proto,
- Collections.singletonList(ValueType.OBJECT),
- Collections.singletonList(register),
- false /* isInterface */);
- builder.addThrow(register);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
index 90065e6..8d50c17 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -142,7 +142,7 @@
return this;
}
- public ForwardMethodBuilder setConstructorTarget(DexMethod method, DexItemFactory factory) {
+ public ForwardMethodBuilder setConstructorTargetWithNewInstance(DexMethod method) {
assert method.isInstanceInitializer(factory);
targetMethod = method;
isConstructorDelegate = true;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java
new file mode 100644
index 0000000..02b32bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java
@@ -0,0 +1,38 @@
+// 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.synthetic;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexType;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing a simple call to NewInstance.
+public final class NewInstanceSourceCode extends SyntheticCfCodeProvider {
+
+ private final DexType newInstanceType;
+
+ private NewInstanceSourceCode(AppView<?> appView, DexType holder, DexType newInstanceType) {
+ super(appView, holder);
+ this.newInstanceType = newInstanceType;
+ }
+
+ public static NewInstanceSourceCode create(
+ AppView<?> appView, DexType holder, DexType newInstanceType) {
+ return new NewInstanceSourceCode(appView, holder, newInstanceType);
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ List<CfInstruction> instructions = new ArrayList<>();
+ instructions.add(new CfNew(newInstanceType));
+ instructions.add(new CfReturnVoid());
+ return standardCfCodeFromInstructions(instructions);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 6586750..461dc2a 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -90,7 +90,7 @@
private final DexApplication application;
private final AppView<?> appView;
private final InternalOptions options;
- private final Marker marker;
+ private final Optional<Marker> marker;
private final Predicate<DexType> isTypeMissing;
private static final CfVersion MIN_VERSION_FOR_COMPILER_GENERATED_CODE = CfVersion.V1_6;
@@ -99,8 +99,7 @@
this.application = appView.appInfo().app();
this.appView = appView;
this.options = appView.options();
- assert marker != null;
- this.marker = marker;
+ this.marker = Optional.ofNullable(marker);
this.isTypeMissing =
PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
}
@@ -137,6 +136,7 @@
private void writeApplication(AndroidApp inputApp, ClassFileConsumer consumer) {
ProguardMapId proguardMapId = null;
if (options.proguardMapConsumer != null) {
+ assert marker.isPresent();
proguardMapId =
runAndWriteMap(
inputApp,
@@ -144,10 +144,9 @@
application.timing,
OriginalSourceFiles.fromClasses(),
DebugRepresentation.none(options));
- marker.setPgMapId(proguardMapId.getId());
+ marker.get().setPgMapId(proguardMapId.getId());
}
- Optional<String> markerString =
- includeMarker(marker) ? Optional.of(marker.toString()) : Optional.empty();
+ Optional<String> markerString = marker.filter(this::includeMarker).map(Marker::toString);
SourceFileEnvironment sourceFileEnvironment = null;
if (options.sourceFileProvider != null) {
sourceFileEnvironment = ApplicationWriter.createSourceFileEnvironment(proguardMapId);
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
index e91fd8a..4e148ee 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LIRConverter.java
@@ -3,43 +3,140 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.lightir;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.CatchHandlers;
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.Phi;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.lightir.LIRBuilder.BlockIndexGetter;
+import com.android.tools.r8.utils.ListUtils;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
+import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
public class IR2LIRConverter {
private IR2LIRConverter() {}
- public static LIRCode translate(IRCode irCode) {
+ public static LIRCode translate(IRCode irCode, DexItemFactory factory) {
+ irCode.traceBlocks();
+ Reference2IntMap<BasicBlock> blocks = new Reference2IntOpenHashMap<>();
Reference2IntMap<Value> values = new Reference2IntOpenHashMap<>();
- int index = 0;
- for (Instruction instruction : irCode.instructions()) {
- if (instruction.hasOutValue()) {
- values.put(instruction.outValue(), index);
+ int instructionIndex = 0;
+ int valueIndex = 0;
+ for (BasicBlock block : irCode.blocks) {
+ blocks.put(block, instructionIndex);
+ for (Phi phi : block.getPhis()) {
+ values.put(phi, valueIndex);
+ valueIndex++;
+ instructionIndex++;
}
- index++;
+ for (Instruction instruction : block.getInstructions()) {
+ if (instruction.hasOutValue()) {
+ values.put(instruction.outValue(), valueIndex);
+ }
+ valueIndex++;
+ if (!instruction.isArgument()) {
+ instructionIndex++;
+ }
+ }
}
- LIRBuilder<Value> builder =
- new LIRBuilder<Value>(irCode.context().getReference(), values::getInt)
+ LIRBuilder<Value, BasicBlock> builder =
+ new LIRBuilder<Value, BasicBlock>(
+ irCode.context().getReference(), values::getInt, blocks::getInt, factory)
.setMetadata(irCode.metadata());
BasicBlockIterator blockIt = irCode.listIterator();
while (blockIt.hasNext()) {
BasicBlock block = blockIt.next();
- // TODO(b/225838009): Support control flow.
- assert !block.hasPhis();
+ if (block.hasPhis()) {
+ // The block order of the predecessors may change, since the LIR does not encode the
+ // direct links, the block order is used to determine predecessor order.
+ int[] permutation = computePermutation(block.getPredecessors(), blocks::getInt);
+ Value[] operands = new Value[block.getPredecessors().size()];
+ for (Phi phi : block.getPhis()) {
+ permuteOperands(phi.getOperands(), permutation, operands);
+ builder.addPhi(phi.getType(), Arrays.asList(operands));
+ }
+ }
+ if (block.hasCatchHandlers()) {
+ CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
+ builder.addTryCatchHanders(
+ blocks.getInt(block),
+ new CatchHandlers<>(
+ handlers.getGuards(), ListUtils.map(handlers.getAllTargets(), blocks::getInt)));
+ }
InstructionIterator it = block.iterator();
while (it.hasNext()) {
Instruction instruction = it.next();
builder.setCurrentPosition(instruction.getPosition());
- instruction.buildLIR(builder);
+ if (instruction.isGoto()) {
+ BasicBlock nextBlock = blockIt.peekNext();
+ if (instruction.asGoto().getTarget() == nextBlock) {
+ builder.addFallthrough();
+ } else {
+ instruction.buildLIR(builder);
+ }
+ } else {
+ instruction.buildLIR(builder);
+ }
}
}
return builder.build();
}
+
+ private static void permuteOperands(List<Value> operands, int[] permutation, Value[] output) {
+ for (int i = 0; i < operands.size(); i++) {
+ Value operand = operands.get(i);
+ output[permutation[i]] = operand;
+ }
+ }
+
+ private static int[] computePermutation(
+ List<BasicBlock> originalPredecessors, BlockIndexGetter<BasicBlock> blockIndexGetter) {
+ int predecessorCount = originalPredecessors.size();
+ // The final predecessor list is sorted by block order.
+ List<BasicBlock> sortedPredecessors = new ArrayList<>(originalPredecessors);
+ sortedPredecessors.sort(Comparator.comparingInt(blockIndexGetter::getBlockIndex));
+ // Since predecessors are not unique, build a map from each unique block to its set of indices.
+ Reference2ReferenceMap<BasicBlock, IntList> mapping =
+ new Reference2ReferenceOpenHashMap<>(predecessorCount);
+ for (int originalIndex = 0; originalIndex < predecessorCount; originalIndex++) {
+ BasicBlock predecessor = originalPredecessors.get(originalIndex);
+ mapping.computeIfAbsent(predecessor, k -> new IntArrayList(1)).add(originalIndex);
+ }
+ // Assign an original index to each sorted index.
+ int[] permutation = new int[predecessorCount];
+ for (int sortedIndex = 0; sortedIndex < predecessorCount; ) {
+ BasicBlock predecessor = sortedPredecessors.get(sortedIndex);
+ IntList originalIndices = mapping.get(predecessor);
+ assert verifySameBlock(sortedPredecessors, sortedIndex, originalIndices.size());
+ for (int originalIndex : originalIndices) {
+ permutation[originalIndex] = sortedIndex++;
+ }
+ }
+ return permutation;
+ }
+
+ private static boolean verifySameBlock(List<BasicBlock> predecessors, int startIndex, int size) {
+ if (size == 1) {
+ return true;
+ }
+ BasicBlock block = predecessors.get(startIndex);
+ for (int i = startIndex + 1; i < startIndex + size; i++) {
+ BasicBlock other = predecessors.get(i);
+ assert block == other;
+ }
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
index 75a372b..056129b 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIR2IRConverter.java
@@ -12,15 +12,25 @@
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.ArrayLength;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.CatchHandlers;
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.Div;
+import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.If.Type;
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.MoveException;
import com.android.tools.r8.ir.code.NumberGenerator;
+import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.Phi.RegisterReadType;
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;
@@ -28,6 +38,10 @@
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 com.android.tools.r8.utils.ListUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -50,13 +64,15 @@
*/
private static class Parser extends LIRParsedInstructionCallback {
+ private static final int ENTRY_BLOCK_INDEX = -1;
+
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 final Int2ReferenceMap<BasicBlock> blocks = new Int2ReferenceOpenHashMap<>();
private BasicBlock currentBlock = null;
private int nextInstructionIndex = 0;
@@ -76,9 +92,30 @@
currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
}
+ private void closeCurrentBlock() {
+ currentBlock = null;
+ }
+
+ private void ensureCurrentBlock() {
+ // Control instructions must close the block, thus the current block is null iff the
+ // instruction denotes a new block.
+ if (currentBlock == null) {
+ currentBlock = blocks.computeIfAbsent(nextInstructionIndex, k -> new BasicBlock());
+ CatchHandlers<Integer> handlers =
+ code.getTryCatchTable().getHandlersForBlock(nextInstructionIndex);
+ if (handlers != null) {
+ List<BasicBlock> targets = ListUtils.map(handlers.getAllTargets(), this::getBasicBlock);
+ targets.forEach(currentBlock::link);
+ currentBlock.linkCatchSuccessors(handlers.getGuards(), targets);
+ }
+ } else {
+ assert !blocks.containsKey(nextInstructionIndex);
+ }
+ }
+
private void ensureCurrentPosition() {
if (nextPositionEntry != null
- && nextPositionEntry.fromInstructionIndex < nextInstructionIndex) {
+ && nextPositionEntry.fromInstructionIndex <= nextInstructionIndex) {
currentPosition = nextPositionEntry.position;
advanceNextPositionEntry();
}
@@ -92,28 +129,35 @@
}
public void parseArguments(ProgramMethod method) {
- currentBlock = new BasicBlock();
- currentBlock.setNumber(basicBlockNumberGenerator.next());
+ currentBlock = getBasicBlock(ENTRY_BLOCK_INDEX);
boolean hasReceiverArgument = !method.getDefinition().isStatic();
assert code.getArgumentCount()
== method.getParameters().size() + (hasReceiverArgument ? 1 : 0);
if (hasReceiverArgument) {
addThisArgument(method.getHolderType());
}
- method.getParameters().forEach(this::addArgument);
+ int index = hasReceiverArgument ? 1 : 0;
+ for (DexType parameter : method.getParameters()) {
+ addArgument(parameter, index++);
+ }
// Set up position state after adding arguments.
advanceNextPositionEntry();
}
public IRCode getIRCode(ProgramMethod method) {
- // TODO(b/225838009): Support control flow.
- currentBlock.setFilled();
- blocks.add(currentBlock);
+ LinkedList<BasicBlock> blockList = new LinkedList<>();
+ IntList blockIndices = new IntArrayList(blocks.keySet());
+ blockIndices.sort(Integer::compare);
+ for (int i = 0; i < blockIndices.size(); i++) {
+ BasicBlock block = blocks.get(blockIndices.getInt(i));
+ block.setFilled();
+ blockList.add(block);
+ }
return new IRCode(
appView.options(),
method,
Position.syntheticNone(),
- blocks,
+ blockList,
valueNumberGenerator,
basicBlockNumberGenerator,
code.getMetadata(),
@@ -121,7 +165,17 @@
new MutableMethodConversionOptions(appView.options()));
}
- public Value getSsaValue(int index) {
+ public BasicBlock getBasicBlock(int instructionIndex) {
+ return blocks.computeIfAbsent(
+ instructionIndex,
+ k -> {
+ BasicBlock block = new BasicBlock();
+ block.setNumber(basicBlockNumberGenerator.next());
+ return block;
+ });
+ }
+
+ public Value getValue(int index) {
Value value = values[index];
if (value == null) {
value = new Value(valueNumberGenerator.next(), TypeElement.getBottom(), null);
@@ -130,10 +184,10 @@
return value;
}
- public List<Value> getSsaValues(IntList indices) {
+ public List<Value> getValues(IntList indices) {
List<Value> arguments = new ArrayList<>(indices.size());
for (int i = 0; i < indices.size(); i++) {
- arguments.add(getSsaValue(indices.getInt(i)));
+ arguments.add(getValue(indices.getInt(i)));
}
return arguments;
}
@@ -145,7 +199,7 @@
public Value getOutValueForNextInstruction(TypeElement type) {
// TODO(b/225838009): Support debug locals.
DebugLocalInfo localInfo = null;
- int index = peekNextInstructionIndex();
+ int index = peekNextInstructionIndex() + code.getArgumentCount();
Value value = values[index];
if (value == null) {
value = new Value(valueNumberGenerator.next(), type, localInfo);
@@ -159,27 +213,59 @@
return value;
}
- private void addInstruction(Instruction instruction) {
+ public Phi getPhiForNextInstructionAndAdvanceState(TypeElement type) {
+ int index = peekNextInstructionIndex() + code.getArgumentCount();
+ // TODO(b/225838009): The phi constructor implicitly adds to the block, so we need to ensure
+ // the block. However, we must grab the index above. Find a way to clean this up so it is
+ // uniform with instructions.
+ advanceInstructionState();
+ // Creating the phi implicitly adds it to currentBlock.
+ DebugLocalInfo localInfo = null;
+ Phi phi =
+ new Phi(
+ valueNumberGenerator.next(), currentBlock, type, localInfo, RegisterReadType.NORMAL);
+ Value value = values[index];
+ if (value != null) {
+ // A fake ssa value has already been created, replace the users by the actual phi.
+ // TODO(b/225838009): We could consider encoding the value type as a bit in the value index
+ // and avoid the overhead of replacing users at phi-definition time.
+ assert !value.isPhi();
+ value.replaceUsers(phi);
+ }
+ values[index] = phi;
+ return phi;
+ }
+
+ private void advanceInstructionState() {
+ ensureCurrentBlock();
ensureCurrentPosition();
- instruction.setPosition(currentPosition);
- currentBlock.getInstructions().add(instruction);
- instruction.setBlock(currentBlock);
++nextInstructionIndex;
}
+ private void addInstruction(Instruction instruction) {
+ advanceInstructionState();
+ instruction.setPosition(currentPosition);
+ currentBlock.getInstructions().add(instruction);
+ instruction.setBlock(currentBlock);
+ }
+
private void addThisArgument(DexType type) {
- Argument argument = addArgument(type);
+ Argument argument = addArgument(type, 0);
argument.outValue().markAsThis();
}
- private Argument addArgument(DexType type) {
- Argument instruction =
- new Argument(
- getOutValueForNextInstruction(type.toTypeElement(appView)),
- peekNextInstructionIndex(),
- type.isBooleanType());
- addInstruction(instruction);
- return instruction;
+ private Argument addArgument(DexType type, int index) {
+ // Arguments are not included in the "instructions" so this does not call "addInstruction"
+ // which would otherwise advance the state.
+ Value dest = getValue(index);
+ dest.setType(type.toTypeElement(appView));
+ Argument argument = new Argument(dest, index, type.isBooleanType());
+ assert currentBlock != null;
+ assert currentPosition.isSyntheticPosition();
+ argument.setPosition(currentPosition);
+ currentBlock.getInstructions().add(argument);
+ argument.setBlock(currentBlock);
+ return argument;
}
@Override
@@ -189,16 +275,53 @@
}
@Override
+ public void onConstInt(int value) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ addInstruction(new ConstNumber(dest, value));
+ }
+
+ @Override
+ public void onDivInt(int leftValueIndex, int rightValueIndex) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ addInstruction(
+ new Div(NumericType.INT, dest, getValue(leftValueIndex), getValue(rightValueIndex)));
+ }
+
+ @Override
public void onConstString(DexString string) {
Value dest = getOutValueForNextInstruction(TypeElement.stringClassType(appView));
addInstruction(new ConstString(dest, string));
}
@Override
+ public void onIf(Type ifKind, int blockIndex, int valueIndex) {
+ BasicBlock targetBlock = getBasicBlock(blockIndex);
+ Value value = getValue(valueIndex);
+ addInstruction(new If(ifKind, value));
+ currentBlock.link(targetBlock);
+ currentBlock.link(getBasicBlock(nextInstructionIndex));
+ closeCurrentBlock();
+ }
+
+ @Override
+ public void onFallthrough() {
+ int nextBlockIndex = peekNextInstructionIndex() + 1;
+ onGoto(nextBlockIndex);
+ }
+
+ @Override
+ public void onGoto(int blockIndex) {
+ BasicBlock targetBlock = getBasicBlock(blockIndex);
+ addInstruction(new Goto());
+ currentBlock.link(targetBlock);
+ closeCurrentBlock();
+ }
+
+ @Override
public void onInvokeDirect(DexMethod target, IntList arguments) {
// TODO(b/225838009): Maintain is-interface bit.
Value dest = getInvokeInstructionOutputValue(target);
- List<Value> ssaArgumentValues = getSsaValues(arguments);
+ List<Value> ssaArgumentValues = getValues(arguments);
InvokeDirect instruction = new InvokeDirect(target, dest, ssaArgumentValues);
addInstruction(instruction);
}
@@ -207,7 +330,7 @@
public void onInvokeVirtual(DexMethod target, IntList arguments) {
// TODO(b/225838009): Maintain is-interface bit.
Value dest = getInvokeInstructionOutputValue(target);
- List<Value> ssaArgumentValues = getSsaValues(arguments);
+ List<Value> ssaArgumentValues = getValues(arguments);
InvokeVirtual instruction = new InvokeVirtual(target, dest, ssaArgumentValues);
addInstruction(instruction);
}
@@ -227,11 +350,35 @@
@Override
public void onReturnVoid() {
addInstruction(new Return());
+ closeCurrentBlock();
+ }
+
+ @Override
+ public void onArrayLength(int arrayIndex) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ Value arrayValue = getValue(arrayIndex);
+ addInstruction(new ArrayLength(dest, arrayValue));
}
@Override
public void onDebugPosition() {
addInstruction(new DebugPosition());
}
+
+ @Override
+ public void onPhi(DexType type, IntList operands) {
+ Phi phi = getPhiForNextInstructionAndAdvanceState(type.toTypeElement(appView));
+ List<Value> values = new ArrayList<>(operands.size());
+ for (int i = 0; i < operands.size(); i++) {
+ values.add(getValue(operands.getInt(i)));
+ }
+ phi.addOperands(values);
+ }
+
+ @Override
+ public void onMoveException(DexType exceptionType) {
+ Value dest = getOutValueForNextInstruction(exceptionType.toTypeElement(appView));
+ addInstruction(new MoveException(dest, exceptionType, appView.options()));
+ }
}
}
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 fe9e5b2..2971d2e 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRBuilder.java
@@ -4,18 +4,31 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
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.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.If.Type;
+import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.lightir.LIRCode.PositionEntry;
+import com.android.tools.r8.lightir.LIRCode.TryCatchTable;
import com.android.tools.r8.utils.ListUtils;
+import com.google.common.collect.ImmutableList;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -23,17 +36,27 @@
*
* @param <V> Type of SSA values. This is abstract to ensure that value internals are not used in
* building.
+ * @param <B> Type of basic blocks. This is abstract to ensure that basic block internals are not
+ * used in building.
*/
-public class LIRBuilder<V> {
+public class LIRBuilder<V, B> {
+ // Abstraction for the only accessible properties of an SSA value.
public interface ValueIndexGetter<V> {
int getValueIndex(V value);
}
+ // Abstraction for the only accessible properties of a basic block.
+ public interface BlockIndexGetter<B> {
+ int getBlockIndex(B block);
+ }
+
+ private final DexItemFactory factory;
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 BlockIndexGetter<B> blockIndexGetter;
private final List<PositionEntry> positionTable;
private int argumentCount = 0;
private int instructionCount = 0;
@@ -42,15 +65,33 @@
private Position currentPosition;
private Position flushedPosition;
- public LIRBuilder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
+ private final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchRanges =
+ new Int2ReferenceOpenHashMap<>();
+
+ // TODO(b/225838009): Reconsider this fixed space as the operand count for phis is much larger.
+ // Pre-allocated space for caching value indexes when writing instructions.
+ private static final int MAX_VALUE_COUNT = 10;
+ private int[] valueIndexBuffer = new int[MAX_VALUE_COUNT];
+
+ public LIRBuilder(
+ DexMethod method,
+ ValueIndexGetter<V> valueIndexGetter,
+ BlockIndexGetter<B> blockIndexGetter,
+ DexItemFactory factory) {
+ this.factory = factory;
constants = new Reference2IntOpenHashMap<>();
positionTable = new ArrayList<>();
this.valueIndexGetter = valueIndexGetter;
+ this.blockIndexGetter = blockIndexGetter;
currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
flushedPosition = currentPosition;
}
- public LIRBuilder<V> setCurrentPosition(Position position) {
+ public void addTryCatchHanders(int blockIndex, CatchHandlers<Integer> handlers) {
+ tryCatchRanges.put(blockIndex, handlers);
+ }
+
+ public LIRBuilder<V, B> setCurrentPosition(Position position) {
assert position != null;
assert position != Position.none();
currentPosition = position;
@@ -75,6 +116,7 @@
private void writeConstantIndex(DexItem item) {
int index = getConstantIndex(item);
+ assert constantIndexSize(item) == ByteUtils.intEncodingSize(index);
ByteUtils.writeEncodedInt(index, writer::writeOperand);
}
@@ -90,25 +132,31 @@
ByteUtils.writeEncodedInt(index, writer::writeOperand);
}
- public LIRBuilder<V> setMetadata(IRMetadata metadata) {
+ private int getBlockIndex(B block) {
+ return blockIndexGetter.getBlockIndex(block);
+ }
+
+ private int blockIndexSize(int index) {
+ return ByteUtils.intEncodingSize(index);
+ }
+
+ private void writeBlockIndex(int index) {
+ ByteUtils.writeEncodedInt(index, writer::writeOperand);
+ }
+
+ public LIRBuilder<V, B> setMetadata(IRMetadata metadata) {
this.metadata = metadata;
return this;
}
- 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) {
+ public LIRBuilder<V, B> 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() {
+ private void advanceInstructionState() {
if (!currentPosition.equals(flushedPosition)) {
setPositionIndex(instructionCount, currentPosition);
flushedPosition = currentPosition;
@@ -116,76 +164,236 @@
++instructionCount;
}
- public LIRBuilder<V> addConstNull() {
- addInstruction();
- writer.writeOneByteInstruction(LIROpcodes.ACONST_NULL);
+ private LIRBuilder<V, B> addNoOperandInstruction(int opcode) {
+ advanceInstructionState();
+ writer.writeOneByteInstruction(opcode);
return this;
}
- public LIRBuilder<V> addConstInt(int value) {
- addInstruction();
+ private LIRBuilder<V, B> addOneItemInstruction(int opcode, DexItem item) {
+ return addInstructionTemplate(opcode, Collections.singletonList(item), Collections.emptyList());
+ }
+
+ private LIRBuilder<V, B> addOneValueInstruction(int opcode, V value) {
+ return addInstructionTemplate(
+ opcode, Collections.emptyList(), Collections.singletonList(value));
+ }
+
+ private LIRBuilder<V, B> addInstructionTemplate(int opcode, List<DexItem> items, List<V> values) {
+ assert values.size() < MAX_VALUE_COUNT;
+ advanceInstructionState();
+ int operandSize = 0;
+ for (DexItem item : items) {
+ operandSize += constantIndexSize(item);
+ }
+ for (int i = 0; i < values.size(); i++) {
+ V value = values.get(i);
+ int valueIndex = getValueIndex(value);
+ operandSize += valueIndexSize(valueIndex);
+ valueIndexBuffer[i] = valueIndex;
+ }
+ writer.writeInstruction(opcode, operandSize);
+ for (DexItem item : items) {
+ writeConstantIndex(item);
+ }
+ for (int i = 0; i < values.size(); i++) {
+ writeValueIndex(valueIndexBuffer[i]);
+ }
+ return this;
+ }
+
+ public LIRBuilder<V, B> addConstNull() {
+ return addNoOperandInstruction(LIROpcodes.ACONST_NULL);
+ }
+
+ public LIRBuilder<V, B> addConstInt(int value) {
if (0 <= value && value <= 5) {
- writer.writeOneByteInstruction(LIROpcodes.ICONST_0 + value);
+ addNoOperandInstruction(LIROpcodes.ICONST_0 + value);
} else {
+ advanceInstructionState();
writer.writeInstruction(LIROpcodes.ICONST, ByteUtils.intEncodingSize(value));
ByteUtils.writeEncodedInt(value, writer::writeOperand);
}
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);
+ public LIRBuilder<V, B> addConstNumber(ValueType type, long value) {
+ switch (type) {
+ case OBJECT:
+ return addConstNull();
+ case INT:
+ {
+ int intValue = (int) value;
+ if (-1 <= intValue && intValue <= 5) {
+ int opcode = LIROpcodes.ICONST_0 + intValue;
+ return addNoOperandInstruction(opcode);
+ }
+ int opcode = LIROpcodes.ICONST;
+ int size = ByteUtils.intEncodingSize(intValue);
+ writer.writeInstruction(opcode, size);
+ ByteUtils.writeEncodedInt(intValue, writer::writeOperand);
+ return this;
+ }
+ case FLOAT:
+ case LONG:
+ case DOUBLE:
+ default:
+ throw new Unimplemented();
}
- writer.writeInstruction(opcode, argumentOprandSize);
- writeConstantIndex(method);
- for (int argumentIndex : argumentIndexes) {
- writeValueIndex(argumentIndex);
- }
- return this;
}
- public LIRBuilder<V> addInvokeDirect(DexMethod method, List<V> arguments) {
+ public LIRBuilder<V, B> addConstString(DexString string) {
+ return addOneItemInstruction(LIROpcodes.LDC, string);
+ }
+
+ public LIRBuilder<V, B> addDiv(NumericType type, V leftValue, V rightValue) {
+ switch (type) {
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ {
+ return addInstructionTemplate(
+ LIROpcodes.IDIV, Collections.emptyList(), ImmutableList.of(leftValue, rightValue));
+ }
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ default:
+ throw new Unimplemented();
+ }
+ }
+
+ public LIRBuilder<V, B> addArrayLength(V array) {
+ return addOneValueInstruction(LIROpcodes.ARRAYLENGTH, array);
+ }
+
+ public LIRBuilder<V, B> addStaticGet(DexField field) {
+ return addOneItemInstruction(LIROpcodes.GETSTATIC, field);
+ }
+
+ public LIRBuilder<V, B> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
+ return addInstructionTemplate(opcode, Collections.singletonList(method), arguments);
+ }
+
+ public LIRBuilder<V, B> addInvokeDirect(DexMethod method, List<V> arguments) {
return addInvokeInstruction(LIROpcodes.INVOKEDIRECT, method, arguments);
}
- public LIRBuilder<V> addInvokeVirtual(DexMethod method, List<V> arguments) {
+ public LIRBuilder<V, B> addInvokeVirtual(DexMethod method, List<V> arguments) {
return addInvokeInstruction(LIROpcodes.INVOKEVIRTUAL, method, arguments);
}
- public LIRBuilder<V> addReturn(V value) {
+ public LIRBuilder<V, B> addReturn(V value) {
throw new Unimplemented();
}
- public LIRBuilder<V> addReturnVoid() {
- addInstruction();
- writer.writeOneByteInstruction(LIROpcodes.RETURN);
+ public LIRBuilder<V, B> addReturnVoid() {
+ return addNoOperandInstruction(LIROpcodes.RETURN);
+ }
+
+ public LIRBuilder<V, B> addDebugPosition(Position position) {
+ assert currentPosition == position;
+ return addNoOperandInstruction(LIROpcodes.DEBUGPOS);
+ }
+
+ public void addFallthrough() {
+ addNoOperandInstruction(LIROpcodes.FALLTHROUGH);
+ }
+
+ public LIRBuilder<V, B> addGoto(B target) {
+ int targetIndex = getBlockIndex(target);
+ int operandSize = blockIndexSize(targetIndex);
+ advanceInstructionState();
+ writer.writeInstruction(LIROpcodes.GOTO, operandSize);
+ writeBlockIndex(targetIndex);
return this;
}
- public LIRBuilder<V> addDebugPosition(Position position) {
- assert currentPosition == position;
- addInstruction();
- writer.writeOneByteInstruction(LIROpcodes.DEBUGPOS);
+ public LIRBuilder<V, B> addIf(Type ifKind, ValueType valueType, V value, B trueTarget) {
+ int opcode;
+ switch (ifKind) {
+ case EQ:
+ opcode = valueType.isObject() ? LIROpcodes.IFNULL : LIROpcodes.IFEQ;
+ break;
+ case GE:
+ opcode = LIROpcodes.IFGE;
+ break;
+ case GT:
+ opcode = LIROpcodes.IFGT;
+ break;
+ case LE:
+ opcode = LIROpcodes.IFLE;
+ break;
+ case LT:
+ opcode = LIROpcodes.IFLT;
+ break;
+ case NE:
+ opcode = valueType.isObject() ? LIROpcodes.IFNONNULL : LIROpcodes.IFNE;
+ break;
+ default:
+ throw new Unreachable("Unexpected if kind: " + ifKind);
+ }
+ int targetIndex = getBlockIndex(trueTarget);
+ int valueIndex = getValueIndex(value);
+ int operandSize = blockIndexSize(targetIndex) + valueIndexSize(valueIndex);
+ advanceInstructionState();
+ writer.writeInstruction(opcode, operandSize);
+ writeBlockIndex(targetIndex);
+ writeValueIndex(valueIndex);
return this;
}
+ public LIRBuilder<V, B> addIfCmp(
+ Type ifKind, ValueType valueType, List<V> inValues, B trueTarget) {
+ int opcode;
+ switch (ifKind) {
+ case EQ:
+ opcode = valueType.isObject() ? LIROpcodes.IF_ACMPEQ : LIROpcodes.IF_ICMPEQ;
+ break;
+ case GE:
+ opcode = LIROpcodes.IF_ICMPGE;
+ break;
+ case GT:
+ opcode = LIROpcodes.IF_ICMPGT;
+ break;
+ case LE:
+ opcode = LIROpcodes.IF_ICMPLE;
+ break;
+ case LT:
+ opcode = LIROpcodes.IF_ICMPLT;
+ break;
+ case NE:
+ opcode = valueType.isObject() ? LIROpcodes.IF_ACMPNE : LIROpcodes.IF_ICMPNE;
+ break;
+ default:
+ throw new Unreachable("Unexpected if kind " + ifKind);
+ }
+ int targetIndex = getBlockIndex(trueTarget);
+ int valueOneIndex = getValueIndex(inValues.get(0));
+ int valueTwoIndex = getValueIndex(inValues.get(1));
+ int operandSize =
+ blockIndexSize(targetIndex) + valueIndexSize(valueOneIndex) + valueIndexSize(valueTwoIndex);
+ advanceInstructionState();
+ writer.writeInstruction(opcode, operandSize);
+ writeBlockIndex(targetIndex);
+ writeValueIndex(valueOneIndex);
+ writeValueIndex(valueTwoIndex);
+ return this;
+ }
+
+ public LIRBuilder<V, B> addMoveException(DexType exceptionType) {
+ return addOneItemInstruction(LIROpcodes.MOVEEXCEPTION, exceptionType);
+ }
+
+ public LIRBuilder<V, B> addPhi(TypeElement type, List<V> operands) {
+ DexType dexType =
+ type.isPrimitiveType()
+ ? type.asPrimitiveType().toDexType(factory)
+ : type.asReferenceType().toDexType(factory);
+ return addInstructionTemplate(LIROpcodes.PHI, Collections.singletonList(dexType), operands);
+ }
+
public LIRCode build() {
assert metadata != null;
int constantsCount = constants.size();
@@ -197,6 +405,7 @@
positionTable.toArray(new PositionEntry[positionTable.size()]),
argumentCount,
byteWriter.toByteArray(),
- instructionCount);
+ instructionCount,
+ new TryCatchTable(tryCatchRanges));
}
}
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 04dffd4..8caa8d6 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRCode.java
@@ -4,12 +4,16 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.graph.DexItem;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.code.IRMetadata;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.lightir.LIRBuilder.BlockIndexGetter;
import com.android.tools.r8.lightir.LIRBuilder.ValueIndexGetter;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.Arrays;
public class LIRCode implements Iterable<LIRInstructionView> {
@@ -24,6 +28,18 @@
}
}
+ public static class TryCatchTable {
+ final Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers;
+
+ public TryCatchTable(Int2ReferenceMap<CatchHandlers<Integer>> tryCatchHandlers) {
+ this.tryCatchHandlers = tryCatchHandlers;
+ }
+
+ public CatchHandlers<Integer> getHandlersForBlock(int blockIndex) {
+ return tryCatchHandlers.get(blockIndex);
+ }
+ }
+
private final IRMetadata metadata;
/** Constant pool of items. */
@@ -34,14 +50,21 @@
/** Full number of arguments (including receiver for non-static methods). */
private final int argumentCount;
- /** Byte encoding of the instructions (including phis). */
+ /** Byte encoding of the instructions (excludes arguments, includes phis). */
private final byte[] instructions;
- /** Cached value for the number of logical instructions (including phis). */
+ /** Cached value for the number of logical instructions (excludes arguments, includes phis). */
private final int instructionCount;
- public static <V> LIRBuilder<V> builder(DexMethod method, ValueIndexGetter<V> valueIndexGetter) {
- return new LIRBuilder<V>(method, valueIndexGetter);
+ /** Table of try-catch handlers for each basic block. */
+ private final TryCatchTable tryCatchTable;
+
+ public static <V, B> LIRBuilder<V, B> builder(
+ DexMethod method,
+ ValueIndexGetter<V> valueIndexGetter,
+ BlockIndexGetter<B> blockIndexGetter,
+ DexItemFactory factory) {
+ return new LIRBuilder<V, B>(method, valueIndexGetter, blockIndexGetter, factory);
}
// Should be constructed using LIRBuilder.
@@ -51,13 +74,15 @@
PositionEntry[] positions,
int argumentCount,
byte[] instructions,
- int instructionCount) {
+ int instructionCount,
+ TryCatchTable tryCatchTable) {
this.metadata = metadata;
this.constants = constants;
this.positionTable = positions;
this.argumentCount = argumentCount;
this.instructions = instructions;
this.instructionCount = instructionCount;
+ this.tryCatchTable = tryCatchTable;
}
public int getArgumentCount() {
@@ -84,6 +109,10 @@
return positionTable;
}
+ public TryCatchTable getTryCatchTable() {
+ return tryCatchTable;
+ }
+
@Override
public LIRIterator iterator() {
return new LIRIterator(new ByteArrayIterator(instructions));
@@ -102,6 +131,7 @@
.append("):{");
int index = 0;
for (LIRInstructionView view : this) {
+ builder.append(index).append(':');
builder.append(LIROpcodes.toString(view.getOpcode()));
if (view.getRemainingOperandSizeInBytes() > 0) {
builder.append("(size:").append(1 + view.getRemainingOperandSizeInBytes()).append(")");
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 f42abdb..428c01f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRInstructionView.java
@@ -15,6 +15,9 @@
/** Convenience method to forward control to a callback. */
void accept(LIRInstructionCallback eventCallback);
+ /** Get the instruction index. */
+ int getInstructionIndex();
+
/** The opcode of the instruction (See {@code LIROpcodes} for values). */
int getOpcode();
@@ -24,9 +27,15 @@
/** True if the instruction has any operands that have not yet been parsed. */
boolean hasMoreOperands();
+ /** Get the next operand as an encoded integer */
+ int getNextIntegerOperand();
+
/** Get the next operand as a constant-pool index. */
int getNextConstantOperand();
/** Get the next operand as an SSA value index. */
int getNextValueOperand();
+
+ /** Get the next operand as a basic-block index. */
+ int getNextBlockOperand();
}
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 9659aa5..882e960 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRIterator.java
@@ -16,10 +16,14 @@
private final ByteIterator iterator;
+ // State of the byte offsets into the iterator.
private int currentByteIndex = 0;
- private int currentOpcode = -1;
private int endOfCurrentInstruction = 0;
+ // State of the instruction interpretation.
+ private int currentInstructionIndex = -1;
+ private int currentOpcode = -1;
+
public LIRIterator(ByteIterator iterator) {
this.iterator = iterator;
}
@@ -39,6 +43,7 @@
@Override
public LIRInstructionView next() {
skipRemainingOperands();
+ ++currentInstructionIndex;
currentOpcode = u1();
if (LIROpcodes.isOneByteInstruction(currentOpcode)) {
endOfCurrentInstruction = currentByteIndex;
@@ -57,6 +62,11 @@
}
@Override
+ public int getInstructionIndex() {
+ return currentInstructionIndex;
+ }
+
+ @Override
public int getOpcode() {
return currentOpcode;
}
@@ -72,15 +82,24 @@
}
@Override
- public int getNextConstantOperand() {
+ public int getNextIntegerOperand() {
assert hasMoreOperands();
return u4();
}
@Override
+ public int getNextConstantOperand() {
+ return getNextIntegerOperand();
+ }
+
+ @Override
public int getNextValueOperand() {
- assert hasMoreOperands();
- return u4();
+ return getNextIntegerOperand();
+ }
+
+ @Override
+ public int getNextBlockOperand() {
+ return getNextIntegerOperand();
}
private void skip(int i) {
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 089469f..0b0dfc8 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIROpcodes.java
@@ -14,7 +14,7 @@
static boolean isOneByteInstruction(int opcode) {
assert opcode >= ACONST_NULL;
- return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS;
+ return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS || opcode == FALLTHROUGH;
}
// Instructions maintaining the same opcode as defined in CF.
@@ -183,6 +183,9 @@
int DCONST = 203;
int INVOKEDIRECT = 204;
int DEBUGPOS = 205;
+ int PHI = 206;
+ int FALLTHROUGH = 207;
+ int MOVEEXCEPTION = 208;
static String toString(int opcode) {
switch (opcode) {
@@ -489,6 +492,12 @@
return "INVOKEDIRECT";
case DEBUGPOS:
return "DEBUGPOS";
+ case PHI:
+ return "PHI";
+ case FALLTHROUGH:
+ return "FALLTHROUGH";
+ case MOVEEXCEPTION:
+ return "MOVEEXCEPTION";
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
index 317eeee..5c87faf 100644
--- a/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LIRParsedInstructionCallback.java
@@ -8,6 +8,9 @@
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.graph.DexType;
+import com.android.tools.r8.ir.code.If;
+import com.android.tools.r8.ir.code.NumericType;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
@@ -32,8 +35,28 @@
public void onConstNull() {}
+ public void onConstNumber(NumericType type, long value) {}
+
+ public void onConstInt(int value) {
+ onConstNumber(NumericType.INT, value);
+ }
+
public void onConstString(DexString string) {}
+ public void onDiv(NumericType type, int leftValueIndex, int rightValueIndex) {}
+
+ public void onDivInt(int leftValueIndex, int rightValueIndex) {
+ onDiv(NumericType.INT, leftValueIndex, rightValueIndex);
+ }
+
+ public void onIf(If.Type ifKind, int blockIndex, int valueIndex) {}
+
+ public void onGoto(int blockIndex) {}
+
+ public void onFallthrough() {}
+
+ public void onMoveException(DexType exceptionType) {}
+
public void onInvokeMethodInstruction(DexMethod method, IntList arguments) {}
public void onInvokeDirect(DexMethod method, IntList arguments) {
@@ -54,60 +77,130 @@
public void onReturnVoid() {}
+ public void onArrayLength(int arrayIndex) {}
+
public void onDebugPosition() {}
+ public void onPhi(DexType type, IntList operands) {}
+
private DexItem getConstantItem(int index) {
return code.getConstantItem(index);
}
@Override
public final void onInstructionView(LIRInstructionView view) {
- switch (view.getOpcode()) {
+ int opcode = view.getOpcode();
+ switch (opcode) {
case LIROpcodes.ACONST_NULL:
{
onConstNull();
- break;
+ return;
}
case LIROpcodes.LDC:
{
DexItem item = getConstantItem(view.getNextConstantOperand());
if (item instanceof DexString) {
onConstString((DexString) item);
+ return;
}
- break;
+ throw new Unimplemented();
+ }
+ case LIROpcodes.ICONST_M1:
+ case LIROpcodes.ICONST_0:
+ case LIROpcodes.ICONST_1:
+ case LIROpcodes.ICONST_2:
+ case LIROpcodes.ICONST_3:
+ case LIROpcodes.ICONST_4:
+ case LIROpcodes.ICONST_5:
+ {
+ int value = opcode - LIROpcodes.ICONST_0;
+ onConstInt(value);
+ return;
+ }
+ case LIROpcodes.ICONST:
+ {
+ int value = view.getNextIntegerOperand();
+ onConstInt(value);
+ return;
+ }
+ case LIROpcodes.IDIV:
+ {
+ int leftValueIndex = view.getNextValueOperand();
+ int rightValueIndex = view.getNextValueOperand();
+ onDivInt(leftValueIndex, rightValueIndex);
+ return;
+ }
+ case LIROpcodes.IFNE:
+ {
+ int blockIndex = view.getNextBlockOperand();
+ int valueIndex = view.getNextValueOperand();
+ onIf(If.Type.NE, blockIndex, valueIndex);
+ return;
+ }
+ case LIROpcodes.GOTO:
+ {
+ int blockIndex = view.getNextBlockOperand();
+ onGoto(blockIndex);
+ return;
}
case LIROpcodes.INVOKEDIRECT:
{
DexMethod target = getInvokeInstructionTarget(view);
IntList arguments = getInvokeInstructionArguments(view);
onInvokeDirect(target, arguments);
- break;
+ return;
}
case LIROpcodes.INVOKEVIRTUAL:
{
DexMethod target = getInvokeInstructionTarget(view);
IntList arguments = getInvokeInstructionArguments(view);
onInvokeVirtual(target, arguments);
- break;
+ return;
}
case LIROpcodes.GETSTATIC:
{
DexField field = (DexField) getConstantItem(view.getNextConstantOperand());
onStaticGet(field);
- break;
+ return;
}
case LIROpcodes.RETURN:
{
onReturnVoid();
- break;
+ return;
+ }
+ case LIROpcodes.ARRAYLENGTH:
+ {
+ onArrayLength(view.getNextValueOperand());
+ return;
}
case LIROpcodes.DEBUGPOS:
{
onDebugPosition();
- break;
+ return;
+ }
+ case LIROpcodes.PHI:
+ {
+ DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+ IntList operands = new IntArrayList();
+ while (view.hasMoreOperands()) {
+ operands.add(view.getNextValueOperand());
+ }
+ onPhi(type, operands);
+ return;
+ }
+ case LIROpcodes.FALLTHROUGH:
+ {
+ onFallthrough();
+ return;
+ }
+ case LIROpcodes.MOVEEXCEPTION:
+ {
+ DexType type = (DexType) getConstantItem(view.getNextConstantOperand());
+ onMoveException(type);
+ return;
}
default:
- throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(view.getOpcode()));
+ throw new Unimplemented("No dispatch for opcode " + LIROpcodes.toString(opcode));
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
index 77ea56c..11e1377 100644
--- a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
+++ b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
@@ -55,7 +55,7 @@
@Override
public void registerInvokeDirect(DexMethod method) {
- invalid();
+ setTarget(method, InvokeKind.DIRECT);
}
@Override
@@ -109,6 +109,7 @@
}
public enum InvokeKind {
+ DIRECT,
VIRTUAL,
STATIC,
SUPER,
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 6782981..e5b23f5 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -109,6 +109,9 @@
@Override
public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
+ if (this == applied) {
+ return originalMethod;
+ }
return getPrevious().getRenamedMethodSignature(originalMethod, applied);
}
@@ -152,25 +155,32 @@
}
public MemberRebindingIdentityLens toRewrittenMemberRebindingIdentityLens(
- AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ GraphLens lens,
+ NonIdentityGraphLens appliedMemberRebindingLens) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
Builder builder = builder(appView, getIdentityLens());
nonReboundFieldReferenceToDefinitionMap.forEach(
(nonReboundFieldReference, reboundFieldReference) -> {
- DexField rewrittenReboundFieldReference = lens.lookupField(reboundFieldReference);
+ DexField rewrittenReboundFieldReference =
+ lens.lookupField(reboundFieldReference, appliedMemberRebindingLens);
DexField rewrittenNonReboundFieldReference =
rewrittenReboundFieldReference.withHolder(
- lens.lookupType(nonReboundFieldReference.getHolderType()), dexItemFactory);
+ lens.lookupType(
+ nonReboundFieldReference.getHolderType(), appliedMemberRebindingLens),
+ dexItemFactory);
builder.recordNonReboundFieldAccess(
rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
});
nonReboundMethodReferenceToDefinitionMap.forEach(
(nonReboundMethodReference, reboundMethodReference) -> {
DexMethod rewrittenReboundMethodReference =
- lens.getRenamedMethodSignature(reboundMethodReference);
+ lens.getRenamedMethodSignature(reboundMethodReference, appliedMemberRebindingLens);
DexMethod rewrittenNonReboundMethodReference =
rewrittenReboundMethodReference.withHolder(
- lens.lookupType(nonReboundMethodReference.getHolderType()), dexItemFactory);
+ lens.lookupType(
+ nonReboundMethodReference.getHolderType(), appliedMemberRebindingLens),
+ dexItemFactory);
builder.recordNonReboundMethodAccess(
rewrittenNonReboundMethodReference, rewrittenReboundMethodReference);
});
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index 5065a75..306d53e 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -144,16 +144,20 @@
}
public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
- AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ GraphLens lens,
+ NonIdentityGraphLens appliedMemberRebindingLens) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
FieldRebindingIdentityLens.Builder builder = FieldRebindingIdentityLens.builder();
nonReboundFieldReferenceToDefinitionMap.forEach(
(nonReboundFieldReference, reboundFieldReference) -> {
DexField rewrittenReboundFieldReference =
- lens.getRenamedFieldSignature(reboundFieldReference);
+ lens.getRenamedFieldSignature(reboundFieldReference, appliedMemberRebindingLens);
DexField rewrittenNonReboundFieldReference =
rewrittenReboundFieldReference.withHolder(
- lens.lookupType(nonReboundFieldReference.getHolderType()), dexItemFactory);
+ lens.lookupType(
+ nonReboundFieldReference.getHolderType(), appliedMemberRebindingLens),
+ dexItemFactory);
builder.recordDefinitionForNonReboundFieldReference(
rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
});
diff --git a/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
new file mode 100644
index 0000000..6c2af55
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/RedundantBridgeRemover.java
@@ -0,0 +1,147 @@
+// Copyright (c) 2017, 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.optimize;
+
+import static com.android.tools.r8.utils.ThreadUtils.processItems;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
+import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class RedundantBridgeRemover {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ private boolean isRedundantBridge(ProgramMethod method) {
+ // Clean-up the predicate check.
+ if (appView.appInfo().isPinned(method.getReference())) {
+ return false;
+ }
+ DexEncodedMethod definition = method.getDefinition();
+ // TODO(b/197490164): Remove if method is abstract.
+ BridgeInfo bridgeInfo = definition.getOptimizationInfo().getBridgeInfo();
+ boolean isBridge = definition.isBridge() || bridgeInfo != null;
+ if (!isBridge || definition.isAbstract()) {
+ return false;
+ }
+ InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor(appView, method);
+ method.registerCodeReferences(targetExtractor);
+ DexMethod target = targetExtractor.getTarget();
+ // javac-generated visibility forward bridge method has same descriptor (name, signature and
+ // return type).
+ if (target == null || !target.match(method.getReference())) {
+ return false;
+ }
+ if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
+ return false;
+ }
+ // This is a visibility forward, so check for the direct target.
+ ProgramMethod targetMethod =
+ appView
+ .appInfo()
+ .unsafeResolveMethodDueToDexFormatLegacy(target)
+ .getResolvedProgramMethod();
+ if (targetMethod == null) {
+ return false;
+ }
+ if (method.getAccessFlags().isPublic()) {
+ if (!targetMethod.getAccessFlags().isPublic()) {
+ return false;
+ }
+ } else {
+ if (targetMethod.getAccessFlags().isProtected()
+ && !targetMethod.getHolderType().isSamePackage(method.getHolderType())) {
+ return false;
+ }
+ if (targetMethod.getAccessFlags().isPrivate()) {
+ return false;
+ }
+ }
+ if (definition.isStatic()
+ && method.getHolder().hasClassInitializer()
+ && method
+ .getHolder()
+ .classInitializationMayHaveSideEffectsInContext(appView, targetMethod)) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isTargetingSuperMethod(ProgramMethod method, InvokeKind kind, DexMethod target) {
+ if (kind == InvokeKind.ILLEGAL) {
+ return false;
+ }
+ if (kind == InvokeKind.DIRECT) {
+ return method.getDefinition().isInstanceInitializer()
+ && appView.options().canHaveNonReboundConstructorInvoke()
+ && appView.testing().enableRedundantConstructorBridgeRemoval
+ && appView.appInfo().isStrictSubtypeOf(method.getHolderType(), target.getHolderType());
+ }
+ assert !method.getAccessFlags().isPrivate();
+ assert !method.getDefinition().isInstanceInitializer();
+ if (kind == InvokeKind.SUPER) {
+ return true;
+ }
+ if (kind == InvokeKind.STATIC) {
+ return appView.appInfo().isStrictSubtypeOf(method.getHolderType(), target.holder);
+ }
+ assert false : "Unexpected invoke-kind for visibility bridge: " + kind;
+ return false;
+ }
+
+ public void run(ExecutorService executorService) throws ExecutionException {
+ // Collect all redundant bridges to remove.
+ Map<DexProgramClass, ProgramMethodSet> bridgesToRemove =
+ computeBridgesToRemove(executorService);
+ pruneApp(bridgesToRemove, executorService);
+ }
+
+ private Map<DexProgramClass, ProgramMethodSet> computeBridgesToRemove(
+ ExecutorService executorService) throws ExecutionException {
+ Map<DexProgramClass, ProgramMethodSet> bridgesToRemove = new ConcurrentHashMap<>();
+ processItems(
+ appView.appInfo().classes(),
+ clazz -> {
+ ProgramMethodSet bridgesToRemoveForClass = ProgramMethodSet.create();
+ clazz.forEachProgramMethod(
+ method -> {
+ if (isRedundantBridge(method)) {
+ bridgesToRemoveForClass.add(method);
+ }
+ });
+ if (!bridgesToRemoveForClass.isEmpty()) {
+ bridgesToRemove.put(clazz, bridgesToRemoveForClass);
+ }
+ },
+ executorService);
+ return bridgesToRemove;
+ }
+
+ private void pruneApp(
+ Map<DexProgramClass, ProgramMethodSet> bridgesToRemove, ExecutorService executorService)
+ throws ExecutionException {
+ PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder().setPrunedApp(appView.app());
+ bridgesToRemove.forEach(
+ (clazz, methods) -> {
+ clazz.getMethodCollection().removeMethods(methods.toDefinitionSet());
+ methods.forEach(method -> prunedItemsBuilder.addRemovedMethod(method.getReference()));
+ });
+ appView.pruneItems(prunedItemsBuilder.build(), executorService);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
deleted file mode 100644
index 3dd736b..0000000
--- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2017, 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.optimize;
-
-import static com.android.tools.r8.utils.ThreadUtils.processItems;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.logging.Log;
-import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Sets;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-
-public class VisibilityBridgeRemover {
-
- private final AppView<AppInfoWithLiveness> appView;
-
- public VisibilityBridgeRemover(AppView<AppInfoWithLiveness> appView) {
- this.appView = appView;
- }
-
- private boolean isUnneededVisibilityBridge(ProgramMethod method) {
- // Clean-up the predicate check.
- if (appView.appInfo().isPinned(method.getReference())) {
- return false;
- }
- DexEncodedMethod definition = method.getDefinition();
- // TODO(b/198133259): Extend to definitions that are not defined as bridges.
- // TODO(b/197490164): Remove if method is abstract.
- if (!definition.isBridge() || definition.isAbstract()) {
- return false;
- }
- InvokeSingleTargetExtractor targetExtractor = new InvokeSingleTargetExtractor(appView, method);
- method.registerCodeReferences(targetExtractor);
- DexMethod target = targetExtractor.getTarget();
- // javac-generated visibility forward bridge method has same descriptor (name, signature and
- // return type).
- if (target == null || !target.match(method.getReference())) {
- return false;
- }
- assert !definition.isPrivate() && !definition.isInstanceInitializer();
- if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
- return false;
- }
- // This is a visibility forward, so check for the direct target.
- ProgramMethod targetMethod =
- appView
- .appInfo()
- .unsafeResolveMethodDueToDexFormatLegacy(target)
- .getResolvedProgramMethod();
- if (targetMethod == null || !targetMethod.getAccessFlags().isPublic()) {
- return false;
- }
- if (definition.isStatic()
- && method.getHolder().hasClassInitializer()
- && method
- .getHolder()
- .classInitializationMayHaveSideEffectsInContext(appView, targetMethod)) {
- return false;
- }
- if (Log.ENABLED) {
- Log.info(
- getClass(),
- "Removing visibility forwarding %s -> %s",
- method,
- targetMethod.getReference());
- }
- return true;
- }
-
- private boolean isTargetingSuperMethod(ProgramMethod method, InvokeKind kind, DexMethod target) {
- if (kind == InvokeKind.ILLEGAL) {
- return false;
- }
- if (kind == InvokeKind.SUPER) {
- return true;
- }
- if (kind == InvokeKind.STATIC) {
- return appView.appInfo().isStrictSubtypeOf(method.getHolderType(), target.holder);
- }
- assert false : "Unexpected invoke-kind for visibility bridge: " + kind;
- return false;
- }
-
- public void run(ExecutorService executorService) throws ExecutionException {
- // Collect all visibility bridges to remove.
- if (!appView.options().enableVisibilityBridgeRemoval) {
- return;
- }
- ConcurrentHashMap<DexProgramClass, Set<DexEncodedMethod>> visibilityBridgesToRemove =
- new ConcurrentHashMap<>();
- processItems(
- appView.appInfo().classes(),
- clazz -> {
- Set<DexEncodedMethod> bridgesToRemoveForClass = Sets.newIdentityHashSet();
- clazz.forEachProgramMethod(
- method -> {
- if (isUnneededVisibilityBridge(method)) {
- bridgesToRemoveForClass.add(method.getDefinition());
- }
- });
- if (!bridgesToRemoveForClass.isEmpty()) {
- visibilityBridgesToRemove.put(clazz, bridgesToRemoveForClass);
- }
- },
- executorService);
- // Remove all bridges found.
- PrunedItems.Builder builder = PrunedItems.builder();
- visibilityBridgesToRemove.forEach(
- (clazz, methods) -> {
- clazz.getMethodCollection().removeMethods(methods);
- methods.forEach(method -> builder.addRemovedMethod(method.getReference()));
- });
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index 09966ca..e6df4a0 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -125,8 +125,8 @@
Set<DexProgramClass> subclasses = new TreeSet<>(Comparator.comparing(DexClass::getType));
for (DexType subtype : subtypes) {
DexProgramClass subclass = asProgramClassOrNull(appView.definitionFor(subtype));
- if (subclass == null) {
- return;
+ if (subclass == null || !appView.testing().isEligibleForBridgeHoisting.test(subclass)) {
+ continue;
}
subclasses.add(subclass);
}
@@ -141,7 +141,7 @@
for (DexProgramClass subclass : subclasses) {
for (DexEncodedMethod method : subclass.virtualMethods()) {
BridgeInfo bridgeInfo = method.getOptimizationInfo().getBridgeInfo();
- if (bridgeInfo != null) {
+ if (bridgeInfo != null && bridgeInfo.isVirtualBridgeInfo()) {
candidates.add(equivalence.wrap(method.getReference()));
}
}
@@ -158,6 +158,18 @@
return;
}
+ // Bail out if the bridge is also declared in the parent class. In that case, hoisting would
+ // change the behavior of calling the bridge on an instance of the parent class.
+ MethodResolutionResult res =
+ appView.appInfo().resolveMethodOnClass(clazz.getSuperType(), method);
+ if (res.isSingleResolution()) {
+ if (!res.getResolvedMethod().isAbstract()) {
+ return;
+ }
+ } else if (res.isMultiMethodResolutionResult()) {
+ return;
+ }
+
// Go through each of the subclasses and find the bridges that can be hoisted. The bridge holder
// classes are stored in buckets grouped by the behavior of the body of the bridge (which is
// implicitly defined by the signature of the invoke-virtual instruction).
@@ -187,7 +199,7 @@
}
BridgeInfo currentBridgeInfo = definition.getOptimizationInfo().getBridgeInfo();
- if (currentBridgeInfo == null) {
+ if (currentBridgeInfo == null || currentBridgeInfo.isDirectBridgeInfo()) {
// This is not a bridge, so the method needs to remain on the subclass.
continue;
}
@@ -196,14 +208,27 @@
VirtualBridgeInfo currentVirtualBridgeInfo = currentBridgeInfo.asVirtualBridgeInfo();
DexMethod invokedMethod = currentVirtualBridgeInfo.getInvokedMethod();
+
+ if (!clazz.getType().isSamePackage(subclass.getType())) {
+ DexEncodedMethod resolvedMethod =
+ appView.appInfo().resolveMethodOnClass(clazz, invokedMethod).getSingleTarget();
+ if (resolvedMethod == null || resolvedMethod.getAccessFlags().isPackagePrivate()) {
+ // After hoisting this bridge would now dispatch to another method, namely the package
+ // private method in the parent class.
+ continue;
+ }
+ }
+
Wrapper<DexMethod> wrapper = MethodSignatureEquivalence.get().wrap(invokedMethod);
eligibleVirtualInvokeBridges
.computeIfAbsent(wrapper, ignore -> new ArrayList<>())
.add(subclass);
}
- // There should be at least one method that is eligible for hoisting.
- assert !eligibleVirtualInvokeBridges.isEmpty();
+ // Check if any bridges may be eligible for hoisting.
+ if (eligibleVirtualInvokeBridges.isEmpty()) {
+ return;
+ }
Entry<Wrapper<DexMethod>, List<DexProgramClass>> mostFrequentBridge =
findMostFrequentBridge(eligibleVirtualInvokeBridges);
@@ -217,7 +242,7 @@
ProgramMethod representative = eligibleBridgeMethods.iterator().next();
// Guard against accessibility issues.
- if (mayBecomeInaccessibleAfterHoisting(clazz, representative)) {
+ if (mayBecomeInaccessibleAfterHoisting(clazz, eligibleBridgeMethods, representative)) {
return;
}
@@ -287,11 +312,21 @@
}
private boolean mayBecomeInaccessibleAfterHoisting(
- DexProgramClass clazz, ProgramMethod representative) {
- if (clazz.type.isSamePackage(representative.getHolder().type)) {
- return false;
+ DexProgramClass clazz,
+ List<ProgramMethod> eligibleBridgeMethods,
+ ProgramMethod representative) {
+ int representativeVisibility = representative.getAccessFlags().getVisibilityOrdinal();
+ for (ProgramMethod eligibleBridgeMethod : eligibleBridgeMethods) {
+ if (eligibleBridgeMethod.getAccessFlags().getVisibilityOrdinal()
+ != representativeVisibility) {
+ return true;
+ }
+ if (!clazz.getType().isSamePackage(eligibleBridgeMethod.getHolderType())
+ && !eligibleBridgeMethod.getDefinition().isPublic()) {
+ return true;
+ }
}
- return !representative.getDefinition().isPublic();
+ return false;
}
private Code createCodeForVirtualBridge(ProgramMethod representative, DexMethod methodToInvoke) {
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
new file mode 100644
index 0000000..11d080b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfile.java
@@ -0,0 +1,213 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.TextOutputStream;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class ArtProfile {
+
+ private final List<ArtProfileRule> rules;
+
+ ArtProfile(List<ArtProfileRule> rules) {
+ assert !rules.isEmpty();
+ this.rules = rules;
+ }
+
+ public static Builder builder(ArtProfileProvider artProfileProvider, InternalOptions options) {
+ return new Builder(artProfileProvider, options);
+ }
+
+ public ArtProfile rewrittenWithLens(GraphLens lens) {
+ return transform(
+ (classRule, builderFactory) -> builderFactory.accept(lens.lookupType(classRule.getType())),
+ (methodRule, builderFactory) ->
+ builderFactory
+ .apply(lens.getRenamedMethodSignature(methodRule.getMethod()))
+ .acceptMethodRuleInfoBuilder(
+ methodRuleInfoBuilder ->
+ methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo())));
+ }
+
+ public ArtProfile rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
+ assert !lens.isIdentityLens();
+ return transform(
+ (classRule, builderFactory) ->
+ builderFactory.accept(lens.lookupType(classRule.getType(), dexItemFactory)),
+ (methodRule, builderFactory) ->
+ builderFactory
+ .apply(lens.lookupMethod(methodRule.getMethod(), dexItemFactory))
+ .acceptMethodRuleInfoBuilder(
+ methodRuleInfoBuilder ->
+ methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo())));
+ }
+
+ public ArtProfile withoutPrunedItems(PrunedItems prunedItems) {
+ return transform(
+ (classRule, builderFactory) -> {
+ if (!prunedItems.isRemoved(classRule.getType())) {
+ builderFactory.accept(classRule.getType());
+ }
+ },
+ (methodRule, builderFactory) -> {
+ if (!prunedItems.isRemoved(methodRule.getMethod())) {
+ builderFactory
+ .apply(methodRule.getMethod())
+ .acceptMethodRuleInfoBuilder(
+ methodRuleInfoBuilder ->
+ methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo()));
+ }
+ });
+ }
+
+ private ArtProfile transform(
+ BiConsumer<ArtProfileClassRule, Consumer<DexType>> classTransformation,
+ BiConsumer<ArtProfileMethodRule, Function<DexMethod, ArtProfileMethodRule.Builder>>
+ methodTransformation) {
+ Map<DexReference, ArtProfileRule.Builder> ruleBuilders = new LinkedHashMap<>();
+ for (ArtProfileRule rule : rules) {
+ if (rule.isClassRule()) {
+ // Supply a factory method for creating a builder. If the current rule should be included in
+ // the rewritten profile, the caller should call the provided builder factory method to
+ // create a class rule builder. If two rules are mapped to the same reference, the same rule
+ // builder is reused so that the two rules are merged into a single rule (with their flags
+ // merged).
+ classTransformation.accept(
+ rule.asClassRule(),
+ newType ->
+ ruleBuilders
+ .computeIfAbsent(
+ newType, ignoreKey(() -> ArtProfileClassRule.builder().setType(newType)))
+ .asClassRuleBuilder());
+ } else {
+ // As above.
+ assert rule.isMethodRule();
+ methodTransformation.accept(
+ rule.asMethodRule(),
+ newMethod ->
+ ruleBuilders
+ .computeIfAbsent(
+ newMethod,
+ ignoreKey(() -> ArtProfileMethodRule.builder().setMethod(newMethod)))
+ .asMethodRuleBuilder());
+ }
+ }
+ ImmutableList.Builder<ArtProfileRule> newRules =
+ ImmutableList.builderWithExpectedSize(ruleBuilders.size());
+ ruleBuilders.values().forEach(ruleBuilder -> newRules.add(ruleBuilder.build()));
+ return new ArtProfile(newRules.build());
+ }
+
+ public void supplyConsumer(ArtProfileConsumer consumer, Reporter reporter) {
+ if (consumer != null) {
+ TextOutputStream textOutputStream = consumer.getHumanReadableArtProfileConsumer();
+ if (textOutputStream != null) {
+ supplyHumanReadableArtProfileConsumer(textOutputStream);
+ }
+ ArtProfileRuleConsumer ruleConsumer = consumer.getRuleConsumer();
+ if (ruleConsumer != null) {
+ supplyRuleConsumer(ruleConsumer);
+ }
+ consumer.finished(reporter);
+ }
+ }
+
+ private void supplyHumanReadableArtProfileConsumer(TextOutputStream textOutputStream) {
+ try {
+ try (OutputStreamWriter outputStreamWriter =
+ new OutputStreamWriter(
+ textOutputStream.getOutputStream(), textOutputStream.getCharset())) {
+ for (ArtProfileRule rule : rules) {
+ rule.writeHumanReadableRuleString(outputStreamWriter);
+ outputStreamWriter.write('\n');
+ }
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private void supplyRuleConsumer(ArtProfileRuleConsumer ruleConsumer) {
+ for (ArtProfileRule rule : rules) {
+ rule.accept(
+ classRule ->
+ ruleConsumer.acceptClassRule(
+ classRule.getClassReference(), classRule.getClassRuleInfo()),
+ methodRule ->
+ ruleConsumer.acceptMethodRule(
+ methodRule.getMethodReference(), methodRule.getMethodRuleInfo()));
+ }
+ }
+
+ public static class Builder implements ArtProfileBuilder {
+
+ private final ArtProfileProvider artProfileProvider;
+ private final DexItemFactory dexItemFactory;
+ private Reporter reporter;
+ private final List<ArtProfileRule> rules = new ArrayList<>();
+
+ Builder(ArtProfileProvider artProfileProvider, InternalOptions options) {
+ this.artProfileProvider = artProfileProvider;
+ this.dexItemFactory = options.dexItemFactory();
+ this.reporter = options.reporter;
+ }
+
+ @Override
+ public ArtProfileBuilder addClassRule(
+ Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
+ ArtProfileClassRule.Builder classRuleBuilder = ArtProfileClassRule.builder(dexItemFactory);
+ classRuleBuilderConsumer.accept(classRuleBuilder);
+ rules.add(classRuleBuilder.build());
+ return this;
+ }
+
+ @Override
+ public ArtProfileBuilder addMethodRule(
+ Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
+ ArtProfileMethodRule.Builder methodRuleBuilder = ArtProfileMethodRule.builder(dexItemFactory);
+ methodRuleBuilderConsumer.accept(methodRuleBuilder);
+ rules.add(methodRuleBuilder.build());
+ return this;
+ }
+
+ @Override
+ public ArtProfileBuilder addHumanReadableArtProfile(
+ TextInputStream textInputStream,
+ Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
+ HumanReadableArtProfileParser.Builder parserBuilder =
+ HumanReadableArtProfileParser.builder().setReporter(reporter).setProfileBuilder(this);
+ parserBuilderConsumer.accept(parserBuilder);
+ HumanReadableArtProfileParser parser = parserBuilder.build();
+ parser.parse(textInputStream, artProfileProvider.getOrigin());
+ return this;
+ }
+
+ public ArtProfile build() {
+ return new ArtProfile(rules);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilder.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilder.java
index fbe9d78..57a7856 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilder.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilder.java
@@ -4,12 +4,27 @@
package com.android.tools.r8.profile.art;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.TextInputStream;
+import java.util.function.Consumer;
+/** API for building an ART profile. */
+@Keep
public interface ArtProfileBuilder {
- void addClassRule(ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo);
+ /** API for adding information about a class rule to the compiler. */
+ ArtProfileBuilder addClassRule(Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer);
- void addMethodRule(MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo);
+ /** API for adding information about a method rule to the compiler. */
+ ArtProfileBuilder addMethodRule(Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer);
+
+ /**
+ * Adds the rules from the given human-readable ART profile and then closes the stream.
+ *
+ * @see <a href="https://developer.android.com/topic/performance/baselineprofiles">ART Baseline
+ * Profiles</a>
+ */
+ ArtProfileBuilder addHumanReadableArtProfile(
+ TextInputStream textInputStream,
+ Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
index 3426e7b..77d0005 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileBuilderUtils.java
@@ -7,10 +7,16 @@
import static com.android.tools.r8.synthesis.SyntheticNaming.COMPANION_CLASS_SUFFIX;
import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.function.Consumer;
public class ArtProfileBuilderUtils {
@@ -67,47 +73,120 @@
*/
public static ArtProfileBuilder createBuilderForArtProfileToStartupProfileConversion(
StartupProfileBuilder startupProfileBuilder,
- ArtProfileRulePredicate rulePredicate,
SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
return new ArtProfileBuilder() {
@Override
- public void addClassRule(
- ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
- if (rulePredicate.testClassRule(classReference, classRuleInfo)) {
- ClassReference syntheticContextReference =
- syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
- classReference);
- if (syntheticContextReference == null) {
- startupProfileBuilder.addStartupClass(
- startupClassBuilder -> startupClassBuilder.setClassReference(classReference));
- } else {
- startupProfileBuilder.addSyntheticStartupMethod(
- syntheticStartupMethodBuilder ->
- syntheticStartupMethodBuilder.setSyntheticContextReference(
- syntheticContextReference));
- }
+ public ArtProfileBuilder addClassRule(
+ Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
+ MutableArtProfileClassRule classRule = new MutableArtProfileClassRule();
+ classRuleBuilderConsumer.accept(classRule);
+ ClassReference syntheticContextReference =
+ syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
+ classRule.getClassReference());
+ if (syntheticContextReference == null) {
+ startupProfileBuilder.addStartupClass(
+ startupClassBuilder ->
+ startupClassBuilder.setClassReference(classRule.getClassReference()));
+ } else {
+ startupProfileBuilder.addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ syntheticContextReference));
}
+ return this;
}
@Override
- public void addMethodRule(
- MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
- if (rulePredicate.testMethodRule(methodReference, methodRuleInfo)) {
- ClassReference syntheticContextReference =
- syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
- methodReference.getHolderClass());
- if (syntheticContextReference == null) {
- startupProfileBuilder.addStartupMethod(
- startupMethodBuilder -> startupMethodBuilder.setMethodReference(methodReference));
- } else {
- startupProfileBuilder.addSyntheticStartupMethod(
- syntheticStartupMethodBuilder ->
- syntheticStartupMethodBuilder.setSyntheticContextReference(
- syntheticContextReference));
- }
+ public ArtProfileBuilder addMethodRule(
+ Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
+ MutableArtProfileMethodRule methodRule = new MutableArtProfileMethodRule();
+ methodRuleBuilderConsumer.accept(methodRule);
+ ClassReference syntheticContextReference =
+ syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
+ methodRule.getMethodReference().getHolderClass());
+ if (syntheticContextReference == null) {
+ startupProfileBuilder.addStartupMethod(
+ startupMethodBuilder ->
+ startupMethodBuilder.setMethodReference(methodRule.getMethodReference()));
+ } else {
+ startupProfileBuilder.addSyntheticStartupMethod(
+ syntheticStartupMethodBuilder ->
+ syntheticStartupMethodBuilder.setSyntheticContextReference(
+ syntheticContextReference));
}
+ return this;
+ }
+
+ @Override
+ public ArtProfileBuilder addHumanReadableArtProfile(
+ TextInputStream textInputStream,
+ Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
+ // The ART profile parser never calls addHumanReadableArtProfile().
+ throw new Unreachable();
}
};
}
+
+ static class MutableArtProfileClassRule implements ArtProfileClassRuleBuilder {
+
+ private ClassReference classReference;
+
+ MutableArtProfileClassRule() {}
+
+ public ClassReference getClassReference() {
+ return classReference;
+ }
+
+ @Override
+ public ArtProfileClassRuleBuilder setClassReference(ClassReference classReference) {
+ this.classReference = classReference;
+ return this;
+ }
+
+ public ArtProfileClassRuleInfo getClassRuleInfo() {
+ return ArtProfileClassRuleInfoImpl.empty();
+ }
+
+ public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
+ writer.write(classReference.getDescriptor());
+ }
+ }
+
+ static class MutableArtProfileMethodRule implements ArtProfileMethodRuleBuilder {
+
+ private MethodReference methodReference;
+ private ArtProfileMethodRuleInfoImpl methodRuleInfo = ArtProfileMethodRuleInfoImpl.empty();
+
+ MutableArtProfileMethodRule() {}
+
+ public MethodReference getMethodReference() {
+ return methodReference;
+ }
+
+ public ArtProfileMethodRuleInfo getMethodRuleInfo() {
+ return methodRuleInfo;
+ }
+
+ @Override
+ public ArtProfileMethodRuleBuilder setMethodReference(MethodReference methodReference) {
+ this.methodReference = methodReference;
+ return this;
+ }
+
+ @Override
+ public ArtProfileMethodRuleBuilder setMethodRuleInfo(
+ Consumer<ArtProfileMethodRuleInfoBuilder> methodRuleInfoBuilderConsumer) {
+ ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
+ ArtProfileMethodRuleInfoImpl.builder();
+ methodRuleInfoBuilderConsumer.accept(methodRuleInfoBuilder);
+ methodRuleInfo = methodRuleInfoBuilder.build();
+ return this;
+ }
+
+ public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
+ methodRuleInfo.writeHumanReadableFlags(writer);
+ writer.write(MethodReferenceUtils.toSmaliString(methodReference));
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
new file mode 100644
index 0000000..932ecb8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRule.java
@@ -0,0 +1,126 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.function.Consumer;
+
+public class ArtProfileClassRule extends ArtProfileRule {
+
+ private final DexType type;
+
+ ArtProfileClassRule(DexType type) {
+ this.type = type;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder builder(DexItemFactory dexItemFactory) {
+ return new Builder(dexItemFactory);
+ }
+
+ @Override
+ public void accept(
+ Consumer<ArtProfileClassRule> classRuleConsumer,
+ Consumer<ArtProfileMethodRule> methodRuleConsumer) {
+ classRuleConsumer.accept(this);
+ }
+
+ public ClassReference getClassReference() {
+ return Reference.classFromDescriptor(type.toDescriptorString());
+ }
+
+ public ArtProfileClassRuleInfo getClassRuleInfo() {
+ return ArtProfileClassRuleInfoImpl.empty();
+ }
+
+ public DexType getType() {
+ return type;
+ }
+
+ @Override
+ public boolean isClassRule() {
+ return true;
+ }
+
+ @Override
+ public ArtProfileClassRule asClassRule() {
+ return this;
+ }
+
+ @Override
+ public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
+ writer.write(type.toDescriptorString());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArtProfileClassRule that = (ArtProfileClassRule) o;
+ return type == that.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return type.toDescriptorString();
+ }
+
+ public static class Builder extends ArtProfileRule.Builder implements ArtProfileClassRuleBuilder {
+
+ private final DexItemFactory dexItemFactory;
+ private DexType type;
+
+ Builder() {
+ this(null);
+ }
+
+ Builder(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ @Override
+ public boolean isClassRuleBuilder() {
+ return true;
+ }
+
+ @Override
+ Builder asClassRuleBuilder() {
+ return this;
+ }
+
+ @Override
+ public Builder setClassReference(ClassReference classReference) {
+ assert dexItemFactory != null;
+ return setType(dexItemFactory.createType(classReference.getDescriptor()));
+ }
+
+ public Builder setType(DexType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public ArtProfileClassRule build() {
+ return new ArtProfileClassRule(type);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleBuilder.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleBuilder.java
new file mode 100644
index 0000000..eaeab93
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleBuilder.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+
+/** API for defining a class rule for an ART profile. */
+@Keep
+public interface ArtProfileClassRuleBuilder {
+
+ ArtProfileClassRuleBuilder setClassReference(ClassReference classReference);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
index 7be8869..9fa3001 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileClassRuleInfoImpl.java
@@ -13,4 +13,14 @@
public static ArtProfileClassRuleInfoImpl empty() {
return INSTANCE;
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return this == obj;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
new file mode 100644
index 0000000..3a885d9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public abstract class ArtProfileCollection {
+
+ public static ArtProfileCollection createInitialArtProfileCollection(InternalOptions options) {
+ ArtProfileOptions artProfileOptions = options.getArtProfileOptions();
+ Collection<ArtProfileForRewriting> artProfilesForRewriting =
+ artProfileOptions.getArtProfilesForRewriting();
+ if (artProfilesForRewriting.isEmpty()) {
+ return empty();
+ }
+ if (artProfileOptions.isPassthrough()) {
+ return passthrough();
+ }
+ List<ArtProfile> artProfiles = new ArrayList<>(artProfilesForRewriting.size());
+ for (ArtProfileForRewriting artProfileForRewriting :
+ options.getArtProfileOptions().getArtProfilesForRewriting()) {
+ ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
+ ArtProfile.Builder artProfileBuilder = ArtProfile.builder(artProfileProvider, options);
+ artProfileForRewriting.getArtProfileProvider().getArtProfile(artProfileBuilder);
+ artProfiles.add(artProfileBuilder.build());
+ }
+ return new NonEmptyArtProfileCollection(artProfiles);
+ }
+
+ public static EmptyArtProfileCollection empty() {
+ return EmptyArtProfileCollection.getInstance();
+ }
+
+ public static PassthroughArtProfileCollection passthrough() {
+ return PassthroughArtProfileCollection.getInstance();
+ }
+
+ public abstract ArtProfileCollection rewrittenWithLens(GraphLens lens);
+
+ public abstract ArtProfileCollection rewrittenWithLens(
+ NamingLens lens, DexItemFactory dexItemFactory);
+
+ public abstract void supplyConsumers(AppView<?> appView);
+
+ public abstract ArtProfileCollection withoutPrunedItems(PrunedItems prunedItems);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileConsumer.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileConsumer.java
new file mode 100644
index 0000000..b395a12
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileConsumer.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.TextOutputStream;
+
+/**
+ * API for consuming an ART profile provided by the compiler, which has been rewritten to match the
+ * residual, optimized app.
+ */
+@Keep
+public interface ArtProfileConsumer {
+
+ /**
+ * Returns a {@link TextOutputStream} that will receive the rules of the residual ART profile in
+ * the human-readable ART baseline profile format. This may return <code>null</code> to specify
+ * that the compiler should not emit the residual ART profile in the human-readable ART baseline
+ * profile format.
+ *
+ * @see <a href="https://developer.android.com/topic/performance/baselineprofiles">ART Baseline
+ * Profiles</a>
+ */
+ default TextOutputStream getHumanReadableArtProfileConsumer() {
+ return null;
+ }
+
+ /**
+ * Returns an {@link ArtProfileRuleConsumer} that will receive the rules of the residual ART
+ * profile. If this returns <code>null</code> no rules will be emitted.
+ */
+ default ArtProfileRuleConsumer getRuleConsumer() {
+ return null;
+ }
+
+ /**
+ * Callback signifying that all rules of the residual ART profile have been emitted to the rule
+ * consumer.
+ *
+ * @param handler Diagnostics handler for reporting.
+ */
+ void finished(DiagnosticsHandler handler);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileForRewriting.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileForRewriting.java
new file mode 100644
index 0000000..7065f86
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileForRewriting.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+/** Internal pair of an {@link ArtProfileProvider} and {@link ArtProfileConsumer}. */
+public class ArtProfileForRewriting {
+
+ private final ArtProfileProvider artProfileProvider;
+ private final ArtProfileConsumer residualArtProfileConsumer;
+
+ public ArtProfileForRewriting(
+ ArtProfileProvider artProfileProvider, ArtProfileConsumer residualArtProfileConsumer) {
+ this.artProfileProvider = artProfileProvider;
+ this.residualArtProfileConsumer = residualArtProfileConsumer;
+ }
+
+ /** Specifies a provider that performs callbacks to a given {@link ArtProfileBuilder}. */
+ public ArtProfileProvider getArtProfileProvider() {
+ return artProfileProvider;
+ }
+
+ /**
+ * Specifies a consumer that should receive the ART profile after it has been rewritten to match
+ * the residual, optimized application.
+ */
+ public ArtProfileConsumer getResidualArtProfileConsumer() {
+ return residualArtProfileConsumer;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
new file mode 100644
index 0000000..f9380b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRule.java
@@ -0,0 +1,148 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.function.Consumer;
+
+public class ArtProfileMethodRule extends ArtProfileRule {
+
+ private final DexMethod method;
+ private final ArtProfileMethodRuleInfoImpl info;
+
+ ArtProfileMethodRule(DexMethod method, ArtProfileMethodRuleInfoImpl info) {
+ this.method = method;
+ this.info = info;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder builder(DexItemFactory dexItemFactory) {
+ return new Builder(dexItemFactory);
+ }
+
+ @Override
+ public void accept(
+ Consumer<ArtProfileClassRule> classRuleConsumer,
+ Consumer<ArtProfileMethodRule> methodRuleConsumer) {
+ methodRuleConsumer.accept(this);
+ }
+
+ public DexMethod getMethod() {
+ return method;
+ }
+
+ public MethodReference getMethodReference() {
+ return method.asMethodReference();
+ }
+
+ public ArtProfileMethodRuleInfo getMethodRuleInfo() {
+ return info;
+ }
+
+ @Override
+ public boolean isMethodRule() {
+ return true;
+ }
+
+ @Override
+ public ArtProfileMethodRule asMethodRule() {
+ return this;
+ }
+
+ @Override
+ public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
+ info.writeHumanReadableFlags(writer);
+ writer.write(method.toSmaliString());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArtProfileMethodRule that = (ArtProfileMethodRule) o;
+ return method.equals(that.method) && info.equals(that.info);
+ }
+
+ @Override
+ public int hashCode() {
+ // A profile does not have two rules with the same reference but different flags, so no need to
+ // include the flags in the hash.
+ return method.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return info.toString() + method.toSmaliString();
+ }
+
+ public static class Builder extends ArtProfileRule.Builder
+ implements ArtProfileMethodRuleBuilder {
+
+ private final DexItemFactory dexItemFactory;
+
+ private DexMethod method;
+ private ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
+ ArtProfileMethodRuleInfoImpl.builder();
+
+ Builder() {
+ this(null);
+ }
+
+ Builder(DexItemFactory dexItemFactory) {
+ this.dexItemFactory = dexItemFactory;
+ }
+
+ @Override
+ public boolean isMethodRuleBuilder() {
+ return true;
+ }
+
+ @Override
+ Builder asMethodRuleBuilder() {
+ return this;
+ }
+
+ @Override
+ public Builder setMethodReference(MethodReference methodReference) {
+ assert dexItemFactory != null;
+ return setMethod(MethodReferenceUtils.toDexMethod(methodReference, dexItemFactory));
+ }
+
+ public Builder setMethod(DexMethod method) {
+ this.method = method;
+ return this;
+ }
+
+ @Override
+ public Builder setMethodRuleInfo(
+ Consumer<ArtProfileMethodRuleInfoBuilder> methodRuleInfoBuilderConsumer) {
+ methodRuleInfoBuilder.clear();
+ return acceptMethodRuleInfoBuilder(methodRuleInfoBuilderConsumer);
+ }
+
+ public Builder acceptMethodRuleInfoBuilder(
+ Consumer<? super ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) {
+ methodRuleInfoBuilderConsumer.accept(methodRuleInfoBuilder);
+ return this;
+ }
+
+ @Override
+ public ArtProfileMethodRule build() {
+ return new ArtProfileMethodRule(method, methodRuleInfoBuilder.build());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleBuilder.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleBuilder.java
new file mode 100644
index 0000000..4bae3b3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleBuilder.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.MethodReference;
+import java.util.function.Consumer;
+
+/** API for defining a method rule for an ART profile. */
+@Keep
+public interface ArtProfileMethodRuleBuilder {
+
+ ArtProfileMethodRuleBuilder setMethodReference(MethodReference methodReference);
+
+ ArtProfileMethodRuleBuilder setMethodRuleInfo(
+ Consumer<ArtProfileMethodRuleInfoBuilder> methodRuleInfoBuilderConsumer);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleFlagsUtils.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleFlagsUtils.java
new file mode 100644
index 0000000..31f9b10
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleFlagsUtils.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+public class ArtProfileMethodRuleFlagsUtils {
+
+ private static final int FLAG_HOT = 1;
+ private static final int FLAG_STARTUP = 2;
+ private static final int FLAG_POST_STARTUP = 4;
+
+ // Getters.
+
+ public static boolean isHot(int flags) {
+ return isFlagSet(flags, FLAG_HOT);
+ }
+
+ public static boolean isStartup(int flags) {
+ return isFlagSet(flags, FLAG_STARTUP);
+ }
+
+ public static boolean isPostStartup(int flags) {
+ return isFlagSet(flags, FLAG_POST_STARTUP);
+ }
+
+ private static boolean isFlagSet(int flags, int flag) {
+ return (flags & flag) != 0;
+ }
+
+ // Setters.
+
+ public static int setIsHot(int flags, boolean isHot) {
+ return isHot ? setFlag(flags, FLAG_HOT) : unsetFlag(flags, FLAG_HOT);
+ }
+
+ public static int setIsStartup(int flags, boolean isStartup) {
+ return isStartup ? setFlag(flags, FLAG_STARTUP) : unsetFlag(flags, FLAG_STARTUP);
+ }
+
+ public static int setIsPostStartup(int flags, boolean isPostStartup) {
+ return isPostStartup ? setFlag(flags, FLAG_POST_STARTUP) : unsetFlag(flags, FLAG_POST_STARTUP);
+ }
+
+ private static int setFlag(int flags, int flag) {
+ return flags | flag;
+ }
+
+ private static int unsetFlag(int flags, int flag) {
+ return flags & ~flag;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoBuilder.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoBuilder.java
new file mode 100644
index 0000000..11f8b42
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoBuilder.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.Keep;
+
+/** API for providing metadata related to a method rule for an ART profile. */
+@Keep
+public interface ArtProfileMethodRuleInfoBuilder {
+
+ ArtProfileMethodRuleInfoBuilder setIsHot(boolean isHot);
+
+ ArtProfileMethodRuleInfoBuilder setIsStartup(boolean isStartup);
+
+ ArtProfileMethodRuleInfoBuilder setIsPostStartup(boolean isPostStartup);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
index ca2d241..86d4c84 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileMethodRuleInfoImpl.java
@@ -4,11 +4,12 @@
package com.android.tools.r8.profile.art;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
public class ArtProfileMethodRuleInfoImpl implements ArtProfileMethodRuleInfo {
- private static final int FLAG_HOT = 1;
- private static final int FLAG_STARTUP = 2;
- private static final int FLAG_POST_STARTUP = 4;
+ private static final ArtProfileMethodRuleInfoImpl EMPTY = new ArtProfileMethodRuleInfoImpl(0);
private final int flags;
@@ -20,41 +21,122 @@
return new Builder();
}
+ public static ArtProfileMethodRuleInfoImpl empty() {
+ return EMPTY;
+ }
+
public boolean isEmpty() {
return flags == 0;
}
@Override
public boolean isHot() {
- return (flags & FLAG_HOT) != 0;
+ return ArtProfileMethodRuleFlagsUtils.isHot(flags);
}
@Override
public boolean isStartup() {
- return (flags & FLAG_STARTUP) != 0;
+ return ArtProfileMethodRuleFlagsUtils.isStartup(flags);
}
@Override
public boolean isPostStartup() {
- return (flags & FLAG_POST_STARTUP) != 0;
+ return ArtProfileMethodRuleFlagsUtils.isPostStartup(flags);
}
- public static class Builder {
+ public void writeHumanReadableFlags(OutputStreamWriter writer) throws IOException {
+ if (isHot()) {
+ writer.write('H');
+ }
+ if (isStartup()) {
+ writer.write('S');
+ }
+ if (isPostStartup()) {
+ writer.write('P');
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ArtProfileMethodRuleInfoImpl that = (ArtProfileMethodRuleInfoImpl) o;
+ return flags == that.flags;
+ }
+
+ @Override
+ public int hashCode() {
+ return flags;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ if (isHot()) {
+ builder.append('H');
+ }
+ if (isStartup()) {
+ builder.append('S');
+ }
+ if (isPostStartup()) {
+ builder.append('P');
+ }
+ return builder.toString();
+ }
+
+ public static class Builder implements ArtProfileMethodRuleInfoBuilder {
private int flags;
- public Builder setHot() {
- flags |= FLAG_HOT;
+ Builder clear() {
+ flags = 0;
return this;
}
- public Builder setStartup() {
- flags |= FLAG_STARTUP;
+ public Builder merge(ArtProfileMethodRuleInfo methodRuleInfo) {
+ if (methodRuleInfo.isHot()) {
+ setIsHot();
+ }
+ if (methodRuleInfo.isStartup()) {
+ setIsStartup();
+ }
+ if (methodRuleInfo.isPostStartup()) {
+ setIsPostStartup();
+ }
return this;
}
- public Builder setPostStartup() {
- flags |= FLAG_POST_STARTUP;
+ public Builder setIsHot() {
+ return setIsHot(true);
+ }
+
+ @Override
+ public Builder setIsHot(boolean isHot) {
+ flags = ArtProfileMethodRuleFlagsUtils.setIsHot(flags, isHot);
+ return this;
+ }
+
+ public Builder setIsStartup() {
+ return setIsStartup(true);
+ }
+
+ @Override
+ public Builder setIsStartup(boolean isStartup) {
+ flags = ArtProfileMethodRuleFlagsUtils.setIsStartup(flags, isStartup);
+ return this;
+ }
+
+ public Builder setIsPostStartup() {
+ return setIsPostStartup(true);
+ }
+
+ @Override
+ public Builder setIsPostStartup(boolean isPostStartup) {
+ flags = ArtProfileMethodRuleFlagsUtils.setIsPostStartup(flags, isPostStartup);
return this;
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
new file mode 100644
index 0000000..231a2d6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class ArtProfileOptions {
+
+ private Collection<ArtProfileForRewriting> artProfilesForRewriting = Collections.emptyList();
+ private boolean passthrough;
+
+ public ArtProfileOptions() {}
+
+ public Collection<ArtProfileForRewriting> getArtProfilesForRewriting() {
+ return artProfilesForRewriting;
+ }
+
+ public ArtProfileOptions setArtProfilesForRewriting(Collection<ArtProfileForRewriting> inputs) {
+ this.artProfilesForRewriting = inputs;
+ return this;
+ }
+
+ public boolean isPassthrough() {
+ return passthrough;
+ }
+
+ public ArtProfileOptions setPassthrough(boolean passthrough) {
+ this.passthrough = passthrough;
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileProvider.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileProvider.java
new file mode 100644
index 0000000..87450e5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileProvider.java
@@ -0,0 +1,15 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.Resource;
+
+/** API for providing an ART profile to the compiler. */
+@Keep
+public interface ArtProfileProvider extends Resource {
+
+ void getArtProfile(ArtProfileBuilder profileBuilder);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
new file mode 100644
index 0000000..f05167c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRule.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.util.function.Consumer;
+
+public abstract class ArtProfileRule {
+
+ public abstract void accept(
+ Consumer<ArtProfileClassRule> classRuleConsumer,
+ Consumer<ArtProfileMethodRule> methodRuleConsumer);
+
+ public boolean isClassRule() {
+ return false;
+ }
+
+ public ArtProfileClassRule asClassRule() {
+ return null;
+ }
+
+ public boolean isMethodRule() {
+ return false;
+ }
+
+ public ArtProfileMethodRule asMethodRule() {
+ return null;
+ }
+
+ public abstract void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException;
+
+ public abstract static class Builder {
+
+ public boolean isClassRuleBuilder() {
+ return false;
+ }
+
+ ArtProfileClassRule.Builder asClassRuleBuilder() {
+ return null;
+ }
+
+ public boolean isMethodRuleBuilder() {
+ return false;
+ }
+
+ ArtProfileMethodRule.Builder asMethodRuleBuilder() {
+ return null;
+ }
+
+ public abstract ArtProfileRule build();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileRuleConsumer.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRuleConsumer.java
new file mode 100644
index 0000000..beab144
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileRuleConsumer.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+
+/**
+ * API for consuming rules from an ART profile. The compiler will call the methods {@link
+ * #acceptClassRule} and {@link #acceptMethodRule} for each class and method rule (respectively) in
+ * the profile.
+ */
+@Keep
+public interface ArtProfileRuleConsumer {
+
+ /** Provides information about a specific class rule to the consumer. */
+ void acceptClassRule(ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo);
+
+ /** Provides information about a specific method rule to the consumer. */
+ void acceptMethodRule(MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo);
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
new file mode 100644
index 0000000..f0313ef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileCollection.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+
+public class EmptyArtProfileCollection extends ArtProfileCollection {
+
+ private static final EmptyArtProfileCollection INSTANCE = new EmptyArtProfileCollection();
+
+ private EmptyArtProfileCollection() {}
+
+ static EmptyArtProfileCollection getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public ArtProfileCollection rewrittenWithLens(GraphLens lens) {
+ return this;
+ }
+
+ @Override
+ public ArtProfileCollection rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
+ return this;
+ }
+
+ @Override
+ public void supplyConsumers(AppView<?> appView) {
+ assert appView.options().getArtProfileOptions().getArtProfilesForRewriting().isEmpty();
+ }
+
+ @Override
+ public ArtProfileCollection withoutPrunedItems(PrunedItems prunedItems) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileConsumer.java b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileConsumer.java
new file mode 100644
index 0000000..2b18ce6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileConsumer.java
@@ -0,0 +1,32 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.DiagnosticsHandler;
+
+public class EmptyArtProfileConsumer implements ArtProfileConsumer {
+
+ private static final EmptyArtProfileConsumer INSTANCE = new EmptyArtProfileConsumer();
+
+ private EmptyArtProfileConsumer() {}
+
+ public static EmptyArtProfileConsumer getInstance() {
+ return INSTANCE;
+ }
+
+ public static ArtProfileConsumer orEmpty(ArtProfileConsumer artProfileConsumer) {
+ return artProfileConsumer != null ? artProfileConsumer : getInstance();
+ }
+
+ @Override
+ public ArtProfileRuleConsumer getRuleConsumer() {
+ return EmptyArtProfileRuleConsumer.getInstance();
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ // Intentionally empty.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileRuleConsumer.java b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileRuleConsumer.java
new file mode 100644
index 0000000..77d520f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/EmptyArtProfileRuleConsumer.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+
+public class EmptyArtProfileRuleConsumer implements ArtProfileRuleConsumer {
+
+ private static final EmptyArtProfileRuleConsumer INSTANCE = new EmptyArtProfileRuleConsumer();
+
+ private EmptyArtProfileRuleConsumer() {}
+
+ public static EmptyArtProfileRuleConsumer getInstance() {
+ return INSTANCE;
+ }
+
+ public static ArtProfileRuleConsumer orEmpty(ArtProfileRuleConsumer ruleConsumer) {
+ return ruleConsumer != null ? ruleConsumer : getInstance();
+ }
+
+ @Override
+ public void acceptClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ // Intentionally empty.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
index 5f0c92b..3abbbca 100644
--- a/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
+++ b/src/main/java/com/android/tools/r8/profile/art/HumanReadableArtProfileParser.java
@@ -21,10 +21,13 @@
public class HumanReadableArtProfileParser {
private final ArtProfileBuilder profileBuilder;
+ private final ArtProfileRulePredicate rulePredicate;
private final Reporter reporter;
- HumanReadableArtProfileParser(ArtProfileBuilder profileBuilder, Reporter reporter) {
+ HumanReadableArtProfileParser(
+ ArtProfileBuilder profileBuilder, ArtProfileRulePredicate rulePredicate, Reporter reporter) {
this.profileBuilder = profileBuilder;
+ this.rulePredicate = rulePredicate;
this.reporter = reporter;
}
@@ -64,9 +67,9 @@
public boolean parseRule(String rule) {
ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
ArtProfileMethodRuleInfoImpl.builder();
- rule = parseFlag(rule, 'H', methodRuleInfoBuilder::setHot);
- rule = parseFlag(rule, 'S', methodRuleInfoBuilder::setStartup);
- rule = parseFlag(rule, 'P', methodRuleInfoBuilder::setPostStartup);
+ rule = parseFlag(rule, 'H', methodRuleInfoBuilder::setIsHot);
+ rule = parseFlag(rule, 'S', methodRuleInfoBuilder::setIsStartup);
+ rule = parseFlag(rule, 'P', methodRuleInfoBuilder::setIsPostStartup);
return parseClassOrMethodDescriptor(rule, methodRuleInfoBuilder.build());
}
@@ -95,7 +98,10 @@
if (classReference == null) {
return false;
}
- profileBuilder.addClassRule(classReference, ArtProfileClassRuleInfoImpl.empty());
+ if (rulePredicate.testClassRule(classReference, ArtProfileClassRuleInfoImpl.empty())) {
+ profileBuilder.addClassRule(
+ classRuleBuilder -> classRuleBuilder.setClassReference(classReference));
+ }
return true;
}
@@ -106,13 +112,25 @@
if (methodReference == null) {
return false;
}
- profileBuilder.addMethodRule(methodReference, methodRuleInfo);
+ if (rulePredicate.testMethodRule(methodReference, methodRuleInfo)) {
+ profileBuilder.addMethodRule(
+ methodRuleBuilder ->
+ methodRuleBuilder
+ .setMethodReference(methodReference)
+ .setMethodRuleInfo(
+ methodRuleInfoBuilder ->
+ methodRuleInfoBuilder
+ .setIsHot(methodRuleInfo.isHot())
+ .setIsStartup(methodRuleInfo.isStartup())
+ .setIsPostStartup(methodRuleInfo.isPostStartup())));
+ }
return true;
}
- public static class Builder {
+ public static class Builder implements HumanReadableArtProfileParserBuilder {
private ArtProfileBuilder profileBuilder;
+ private ArtProfileRulePredicate rulePredicate = new AlwaysTrueArtProfileRulePredicate();
private Reporter reporter;
public Builder setReporter(Reporter reporter) {
@@ -120,13 +138,19 @@
return this;
}
+ @Override
+ public Builder setRulePredicate(ArtProfileRulePredicate rulePredicate) {
+ this.rulePredicate = rulePredicate;
+ return this;
+ }
+
public Builder setProfileBuilder(ArtProfileBuilder profileBuilder) {
this.profileBuilder = profileBuilder;
return this;
}
public HumanReadableArtProfileParser build() {
- return new HumanReadableArtProfileParser(profileBuilder, reporter);
+ return new HumanReadableArtProfileParser(profileBuilder, rulePredicate, reporter);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
new file mode 100644
index 0000000..e3cbaf7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Function;
+
+public class NonEmptyArtProfileCollection extends ArtProfileCollection {
+
+ private final Collection<ArtProfile> artProfiles;
+
+ NonEmptyArtProfileCollection(Collection<ArtProfile> artProfiles) {
+ this.artProfiles = artProfiles;
+ }
+
+ @Override
+ public NonEmptyArtProfileCollection rewrittenWithLens(GraphLens lens) {
+ return map(artProfile -> artProfile.rewrittenWithLens(lens));
+ }
+
+ @Override
+ public NonEmptyArtProfileCollection rewrittenWithLens(
+ NamingLens lens, DexItemFactory dexItemFactory) {
+ assert !lens.isIdentityLens();
+ return map(artProfile -> artProfile.rewrittenWithLens(lens, dexItemFactory));
+ }
+
+ @Override
+ public void supplyConsumers(AppView<?> appView) {
+ NonEmptyArtProfileCollection collection =
+ appView.getNamingLens().isIdentityLens()
+ ? this
+ : rewrittenWithLens(appView.getNamingLens(), appView.dexItemFactory());
+ InternalOptions options = appView.options();
+ Collection<ArtProfileForRewriting> inputs =
+ options.getArtProfileOptions().getArtProfilesForRewriting();
+ assert !inputs.isEmpty();
+ assert collection.artProfiles.size() == inputs.size();
+ Iterator<ArtProfileForRewriting> inputIterator = inputs.iterator();
+ for (ArtProfile artProfile : collection.artProfiles) {
+ ArtProfileForRewriting input = inputIterator.next();
+ artProfile.supplyConsumer(input.getResidualArtProfileConsumer(), options.reporter);
+ }
+ }
+
+ @Override
+ public NonEmptyArtProfileCollection withoutPrunedItems(PrunedItems prunedItems) {
+ return map(artProfile -> artProfile.withoutPrunedItems(prunedItems));
+ }
+
+ private NonEmptyArtProfileCollection map(Function<ArtProfile, ArtProfile> fn) {
+ ImmutableList.Builder<ArtProfile> newArtProfiles =
+ ImmutableList.builderWithExpectedSize(artProfiles.size());
+ for (ArtProfile artProfile : artProfiles) {
+ newArtProfiles.add(fn.apply(artProfile));
+ }
+ return new NonEmptyArtProfileCollection(newArtProfiles.build());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
new file mode 100644
index 0000000..2198da6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
@@ -0,0 +1,158 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.TextOutputStream;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.MutableArtProfileClassRule;
+import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.MutableArtProfileMethodRule;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.util.function.Consumer;
+
+public class PassthroughArtProfileCollection extends ArtProfileCollection {
+
+ private static final PassthroughArtProfileCollection INSTANCE =
+ new PassthroughArtProfileCollection();
+
+ private PassthroughArtProfileCollection() {}
+
+ static PassthroughArtProfileCollection getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public ArtProfileCollection rewrittenWithLens(GraphLens lens) {
+ return this;
+ }
+
+ @Override
+ public ArtProfileCollection rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
+ return this;
+ }
+
+ @Override
+ public void supplyConsumers(AppView<?> appView) {
+ for (ArtProfileForRewriting artProfileForRewriting :
+ appView.options().getArtProfileOptions().getArtProfilesForRewriting()) {
+ ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
+ ArtProfileConsumer artProfileConsumer =
+ EmptyArtProfileConsumer.orEmpty(artProfileForRewriting.getResidualArtProfileConsumer());
+ supplyArtProfileConsumer(appView, artProfileConsumer, artProfileProvider);
+ artProfileConsumer.finished(appView.reporter());
+ }
+ }
+
+ private void supplyArtProfileConsumer(
+ AppView<?> appView,
+ ArtProfileConsumer artProfileConsumer,
+ ArtProfileProvider artProfileProvider) {
+ ArtProfileConsumerSupplier artProfileConsumerSupplier =
+ new ArtProfileConsumerSupplier(artProfileConsumer);
+ try {
+ ArtProfileRuleConsumer ruleConsumer =
+ EmptyArtProfileRuleConsumer.orEmpty(artProfileConsumer.getRuleConsumer());
+ artProfileProvider.getArtProfile(
+ new ArtProfileBuilder() {
+
+ @Override
+ public ArtProfileBuilder addClassRule(
+ Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
+ MutableArtProfileClassRule classRule = new MutableArtProfileClassRule();
+ classRuleBuilderConsumer.accept(classRule);
+ ruleConsumer.acceptClassRule(
+ classRule.getClassReference(), classRule.getClassRuleInfo());
+ artProfileConsumerSupplier.supply(classRule);
+ return this;
+ }
+
+ @Override
+ public ArtProfileBuilder addMethodRule(
+ Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
+ MutableArtProfileMethodRule methodRule = new MutableArtProfileMethodRule();
+ methodRuleBuilderConsumer.accept(methodRule);
+ ruleConsumer.acceptMethodRule(
+ methodRule.getMethodReference(), methodRule.getMethodRuleInfo());
+ artProfileConsumerSupplier.supply(methodRule);
+ return this;
+ }
+
+ @Override
+ public ArtProfileBuilder addHumanReadableArtProfile(
+ TextInputStream textInputStream,
+ Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
+ HumanReadableArtProfileParser.Builder parserBuilder =
+ HumanReadableArtProfileParser.builder()
+ .setReporter(appView.reporter())
+ .setProfileBuilder(this);
+ parserBuilderConsumer.accept(parserBuilder);
+ HumanReadableArtProfileParser parser = parserBuilder.build();
+ parser.parse(textInputStream, artProfileProvider.getOrigin());
+ return this;
+ }
+ });
+ } finally {
+ artProfileConsumerSupplier.close();
+ }
+ }
+
+ @Override
+ public ArtProfileCollection withoutPrunedItems(PrunedItems prunedItems) {
+ return this;
+ }
+
+ private static class ArtProfileConsumerSupplier {
+
+ private final OutputStreamWriter outputStreamWriter;
+
+ ArtProfileConsumerSupplier(ArtProfileConsumer artProfileConsumer) {
+ TextOutputStream textOutputStream = artProfileConsumer.getHumanReadableArtProfileConsumer();
+ this.outputStreamWriter =
+ textOutputStream != null
+ ? new OutputStreamWriter(
+ textOutputStream.getOutputStream(), textOutputStream.getCharset())
+ : null;
+ ;
+ }
+
+ void supply(MutableArtProfileClassRule classRule) {
+ if (outputStreamWriter != null) {
+ try {
+ classRule.writeHumanReadableRuleString(outputStreamWriter);
+ outputStreamWriter.write('\n');
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+
+ void supply(MutableArtProfileMethodRule methodRule) {
+ if (outputStreamWriter != null) {
+ try {
+ methodRule.writeHumanReadableRuleString(outputStreamWriter);
+ outputStreamWriter.write('\n');
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+
+ void close() {
+ if (outputStreamWriter != null) {
+ try {
+ outputStreamWriter.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index f4e27a1..a8058ac 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -234,7 +234,12 @@
Set<DexType> lockCandidates,
Map<DexType, Visibility> initClassReferences,
Set<DexMethod> recordFieldValuesReferences) {
- super(committedItems, classToFeatureSplitMap, mainDexInfo, missingClasses, startupOrder);
+ super(
+ committedItems,
+ classToFeatureSplitMap,
+ mainDexInfo,
+ missingClasses,
+ startupOrder);
this.deadProtoTypes = deadProtoTypes;
this.liveTypes = liveTypes;
this.targetedMethods = targetedMethods;
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index 19bbe92..813328d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -87,6 +87,8 @@
.createField(clazz.type, clinitField.type, clinitField.name))
.setAccessFlags(accessFlags)
.setApiLevel(appView.computedMinApiLevel())
+ .disableAndroidApiLevelCheckIf(
+ !appView.options().apiModelingOptions().isApiCallerIdentificationEnabled())
.build();
clazz.appendStaticField(encodedClinitField);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
new file mode 100644
index 0000000..fcd894d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.dex.code.CfOrDexInstruction;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import java.util.ListIterator;
+
+public class ComputeApiLevelUseRegistry extends UseRegistry<ProgramMethod> {
+
+ protected final AppView<?> appView;
+ private final AndroidApiLevelCompute apiLevelCompute;
+ private final boolean isEnabled;
+ private ComputedApiLevel maxApiReferenceLevel;
+
+ public ComputeApiLevelUseRegistry(
+ AppView<?> appView, ProgramMethod context, AndroidApiLevelCompute apiLevelCompute) {
+ super(appView, context);
+ this.appView = appView;
+ this.apiLevelCompute = apiLevelCompute;
+ isEnabled = apiLevelCompute.isEnabled();
+ maxApiReferenceLevel = appView.computedMinApiLevel();
+ }
+
+ @Override
+ public void registerInitClass(DexType clazz) {
+ // Intentionally empty since init class as synthesized.
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeDirect(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerInstanceFieldReadFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWriteFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ setMaxApiReferenceLevel(type);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerStaticFieldReadFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerStaticFieldWriteFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerConstClass(
+ DexType type,
+ ListIterator<? extends CfOrDexInstruction> iterator,
+ boolean ignoreCompatRules) {
+ setMaxApiReferenceLevel(type);
+ }
+
+ @Override
+ public void registerCheckCast(DexType type, boolean ignoreCompatRules) {
+ // CheckCast are not causing soft verification issues
+ }
+
+ @Override
+ public void registerSafeCheckCast(DexType type) {
+ // CheckCast are not causing soft verification issues
+ }
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ // Type references are OK as long as we do not have a use on them
+ }
+
+ @Override
+ public void registerInstanceOf(DexType type) {
+ // InstanceOf are not causing soft verification issues
+ }
+
+ @Override
+ public void registerExceptionGuard(DexType guard) {
+ setMaxApiReferenceLevel(guard);
+ }
+
+ @Override
+ public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
+ super.registerMethodHandle(methodHandle, use);
+ }
+
+ private void setMaxApiReferenceLevel(DexReference reference) {
+ if (isEnabled) {
+ maxApiReferenceLevel =
+ maxApiReferenceLevel.max(
+ apiLevelCompute.computeApiLevelForLibraryReference(
+ reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
+ }
+ }
+
+ public ComputedApiLevel getMaxApiReferenceLevel() {
+ return maxApiReferenceLevel;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 192b16a..f7ffc42 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
-import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.dex.code.CfOrDexInstruction;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -18,29 +17,23 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
import java.util.ListIterator;
-public class DefaultEnqueuerUseRegistry extends UseRegistry<ProgramMethod> {
+public class DefaultEnqueuerUseRegistry extends ComputeApiLevelUseRegistry {
protected final AppView<? extends AppInfoWithClassHierarchy> appView;
protected final Enqueuer enqueuer;
- private final AndroidApiLevelCompute apiLevelCompute;
- private ComputedApiLevel maxApiReferenceLevel;
public DefaultEnqueuerUseRegistry(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod context,
Enqueuer enqueuer,
AndroidApiLevelCompute apiLevelCompute) {
- super(appView, context);
+ super(appView, context, apiLevelCompute);
this.appView = appView;
this.enqueuer = enqueuer;
- this.apiLevelCompute = apiLevelCompute;
- maxApiReferenceLevel = appView.computedMinApiLevel();
}
public DexProgramClass getContextHolder() {
@@ -53,6 +46,7 @@
@Override
public void registerInitClass(DexType clazz) {
+ super.registerInitClass(clazz);
enqueuer.traceInitClass(clazz, getContext());
}
@@ -64,90 +58,90 @@
@Override
public void registerInvokeVirtual(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeVirtual(invokedMethod);
enqueuer.traceInvokeVirtual(invokedMethod, getContext());
}
@Override
public void registerInvokeDirect(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeDirect(invokedMethod);
enqueuer.traceInvokeDirect(invokedMethod, getContext());
}
@Override
public void registerInvokeStatic(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeStatic(invokedMethod);
enqueuer.traceInvokeStatic(invokedMethod, getContext());
}
@Override
public void registerInvokeInterface(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeInterface(invokedMethod);
enqueuer.traceInvokeInterface(invokedMethod, getContext());
}
@Override
public void registerInvokeSuper(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeSuper(invokedMethod);
enqueuer.traceInvokeSuper(invokedMethod, getContext());
}
@Override
public void registerInstanceFieldRead(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldRead(field);
enqueuer.traceInstanceFieldRead(field, getContext());
}
@Override
public void registerInstanceFieldReadFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldReadFromMethodHandle(field);
enqueuer.traceInstanceFieldReadFromMethodHandle(field, getContext());
}
private void registerInstanceFieldReadFromRecordMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldWriteFromMethodHandle(field);
enqueuer.traceInstanceFieldReadFromRecordMethodHandle(field, getContext());
}
@Override
public void registerInstanceFieldWrite(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldWrite(field);
enqueuer.traceInstanceFieldWrite(field, getContext());
}
@Override
public void registerInstanceFieldWriteFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldWriteFromMethodHandle(field);
enqueuer.traceInstanceFieldWriteFromMethodHandle(field, getContext());
}
@Override
public void registerNewInstance(DexType type) {
- setMaxApiReferenceLevel(type);
+ super.registerNewInstance(type);
enqueuer.traceNewInstance(type, getContext());
}
@Override
public void registerStaticFieldRead(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldRead(field);
enqueuer.traceStaticFieldRead(field, getContext());
}
@Override
public void registerStaticFieldReadFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldReadFromMethodHandle(field);
enqueuer.traceStaticFieldReadFromMethodHandle(field, getContext());
}
@Override
public void registerStaticFieldWrite(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldWrite(field);
enqueuer.traceStaticFieldWrite(field, getContext());
}
@Override
public void registerStaticFieldWriteFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldWriteFromMethodHandle(field);
enqueuer.traceStaticFieldWriteFromMethodHandle(field, getContext());
}
@@ -156,32 +150,37 @@
DexType type,
ListIterator<? extends CfOrDexInstruction> iterator,
boolean ignoreCompatRules) {
+ super.registerConstClass(type, iterator, ignoreCompatRules);
enqueuer.traceConstClass(type, getContext(), iterator, ignoreCompatRules);
}
@Override
public void registerCheckCast(DexType type, boolean ignoreCompatRules) {
+ super.registerCheckCast(type, ignoreCompatRules);
enqueuer.traceCheckCast(type, getContext(), ignoreCompatRules);
}
@Override
public void registerSafeCheckCast(DexType type) {
+ super.registerSafeCheckCast(type);
enqueuer.traceSafeCheckCast(type, getContext());
}
@Override
public void registerTypeReference(DexType type) {
+ super.registerTypeReference(type);
enqueuer.traceTypeReference(type, getContext());
}
@Override
public void registerInstanceOf(DexType type) {
+ super.registerInstanceOf(type);
enqueuer.traceInstanceOf(type, getContext());
}
@Override
public void registerExceptionGuard(DexType guard) {
- setMaxApiReferenceLevel(guard);
+ super.registerExceptionGuard(guard);
enqueuer.traceExceptionGuard(guard, getContext());
}
@@ -219,15 +218,4 @@
}
}
}
-
- private void setMaxApiReferenceLevel(DexReference reference) {
- maxApiReferenceLevel =
- maxApiReferenceLevel.max(
- apiLevelCompute.computeApiLevelForLibraryReference(
- reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
- }
-
- public ComputedApiLevel getMaxApiReferenceLevel() {
- return maxApiReferenceLevel;
- }
}
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 90b9dfb..74202d3 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2508,39 +2508,41 @@
private void handleInvokeOfDirectTarget(
DexMethod reference, ProgramDefinition context, KeepReason reason) {
- DexType holder = reference.holder;
- DexProgramClass clazz = getProgramClassOrNull(holder, context);
- if (clazz == null) {
- recordMethodReference(reference, context);
- return;
- }
- // TODO(zerny): Is it ok that we lookup in both the direct and virtual pool here?
- DexEncodedMethod encodedMethod = clazz.lookupMethod(reference);
- if (encodedMethod == null) {
- failedMethodResolutionTargets.add(reference);
- return;
- }
+ resolveMethod(reference, context, reason)
+ .forEachMethodResolutionResult(
+ resolutionResult -> {
+ if (resolutionResult.isFailedResolution()) {
+ failedMethodResolutionTargets.add(reference);
+ return;
+ }
- ProgramMethod method = new ProgramMethod(clazz, encodedMethod);
+ if (!resolutionResult.isSingleResolution()
+ || !resolutionResult.getResolvedHolder().isProgramClass()) {
+ return;
+ }
- // We have to mark the resolved method as targeted even if it cannot actually be invoked
- // to make sure the invocation will keep failing in the appropriate way.
- markMethodAsTargeted(method, reason);
+ ProgramMethod resolvedMethod =
+ resolutionResult.asSingleResolution().getResolvedProgramMethod();
- // Only mark methods for which invocation will succeed at runtime live.
- if (encodedMethod.isStatic()) {
- return;
- }
+ // We have to mark the resolved method as targeted even if it cannot actually be
+ // invoked to make sure the invocation will keep failing in the appropriate way.
+ markMethodAsTargeted(resolvedMethod, reason);
- markDirectStaticOrConstructorMethodAsLive(method, reason);
+ // Only mark methods for which invocation will succeed at runtime live.
+ if (resolvedMethod.getAccessFlags().isStatic()) {
+ return;
+ }
- // It is valid to have an invoke-direct instruction in a default interface method that
- // targets another default method in the same interface (see testInvokeSpecialToDefault-
- // Method). In a class, that would lead to a verification error.
- if (encodedMethod.isNonPrivateVirtualMethod()
- && virtualMethodsTargetedByInvokeDirect.add(encodedMethod.getReference())) {
- workList.enqueueMarkMethodLiveAction(method, context, reason);
- }
+ markDirectStaticOrConstructorMethodAsLive(resolvedMethod, reason);
+
+ // It is valid to have an invoke-direct instruction in a default interface method that
+ // targets another default method in the same interface. In a class, that would lead
+ // to a verification error. See also testInvokeSpecialToDefaultMethod.
+ if (resolvedMethod.getDefinition().isNonPrivateVirtualMethod()
+ && virtualMethodsTargetedByInvokeDirect.add(resolvedMethod.getReference())) {
+ workList.enqueueMarkMethodLiveAction(resolvedMethod, context, reason);
+ }
+ });
}
private void ensureFromLibraryOrThrow(DexType type, DexLibraryClass context) {
@@ -3575,7 +3577,9 @@
&& appView.options().getProguardConfiguration().getKeepAttributes().signature) {
registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
}
- registerAnalysis(new ApiModelAnalysis(appView));
+ if (options.apiModelingOptions().enableLibraryApiModeling) {
+ registerAnalysis(new ApiModelAnalysis(appView));
+ }
// Transfer the minimum keep info from the root set into the Enqueuer state.
includeMinimumKeepInfo(rootSet);
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index 0173fb2..41ec85a 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -29,6 +29,7 @@
private final boolean allowConstantArgumentOptimization;
private final boolean allowInlining;
private final boolean allowMethodStaticizing;
+ private final boolean allowParameterRemoval;
private final boolean allowParameterReordering;
private final boolean allowParameterTypeStrengthening;
private final boolean allowReturnTypeStrengthening;
@@ -42,6 +43,7 @@
this.allowConstantArgumentOptimization = builder.isConstantArgumentOptimizationAllowed();
this.allowInlining = builder.isInliningAllowed();
this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
+ this.allowParameterRemoval = builder.isParameterRemovalAllowed();
this.allowParameterReordering = builder.isParameterReorderingAllowed();
this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
@@ -60,13 +62,6 @@
return isParameterRemovalAllowed(configuration);
}
- public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
- return isClosedWorldReasoningAllowed(configuration)
- && isOptimizationAllowed(configuration)
- && isShrinkingAllowed(configuration)
- && !isCheckDiscardedEnabled(configuration);
- }
-
public boolean isClassInliningAllowed(GlobalKeepInfoConfiguration configuration) {
return isOptimizationAllowed(configuration) && internalIsClassInliningAllowed();
}
@@ -111,6 +106,18 @@
return allowMethodStaticizing;
}
+ public boolean isParameterRemovalAllowed(GlobalKeepInfoConfiguration configuration) {
+ return isClosedWorldReasoningAllowed(configuration)
+ && isOptimizationAllowed(configuration)
+ && isShrinkingAllowed(configuration)
+ && !isCheckDiscardedEnabled(configuration)
+ && internalIsParameterRemovalAllowed();
+ }
+
+ boolean internalIsParameterRemovalAllowed() {
+ return allowParameterRemoval;
+ }
+
public boolean isParameterReorderingAllowed(GlobalKeepInfoConfiguration configuration) {
return isClosedWorldReasoningAllowed(configuration)
&& isOptimizationAllowed(configuration)
@@ -188,6 +195,7 @@
private boolean allowConstantArgumentOptimization;
private boolean allowInlining;
private boolean allowMethodStaticizing;
+ private boolean allowParameterRemoval;
private boolean allowParameterReordering;
private boolean allowParameterTypeStrengthening;
private boolean allowReturnTypeStrengthening;
@@ -205,6 +213,7 @@
allowConstantArgumentOptimization = original.internalIsConstantArgumentOptimizationAllowed();
allowInlining = original.internalIsInliningAllowed();
allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
+ allowParameterRemoval = original.internalIsParameterRemovalAllowed();
allowParameterReordering = original.internalIsParameterReorderingAllowed();
allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
@@ -308,6 +317,25 @@
return setAllowMethodStaticizing(false);
}
+ // Parameter removal.
+
+ public boolean isParameterRemovalAllowed() {
+ return allowParameterRemoval;
+ }
+
+ public Builder setAllowParameterRemoval(boolean allowParameterRemoval) {
+ this.allowParameterRemoval = allowParameterRemoval;
+ return self();
+ }
+
+ public Builder allowParameterRemoval() {
+ return setAllowParameterRemoval(true);
+ }
+
+ public Builder disallowParameterRemoval() {
+ return setAllowParameterRemoval(false);
+ }
+
// Parameter reordering.
public boolean isParameterReorderingAllowed() {
@@ -433,6 +461,7 @@
== other.internalIsConstantArgumentOptimizationAllowed()
&& isInliningAllowed() == other.internalIsInliningAllowed()
&& isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
+ && isParameterRemovalAllowed() == other.internalIsParameterRemovalAllowed()
&& isParameterReorderingAllowed() == other.internalIsParameterReorderingAllowed()
&& isParameterTypeStrengtheningAllowed()
== other.internalIsParameterTypeStrengtheningAllowed()
@@ -456,6 +485,7 @@
.disallowConstantArgumentOptimization()
.disallowInlining()
.disallowMethodStaticizing()
+ .disallowParameterRemoval()
.disallowParameterReordering()
.disallowParameterTypeStrengthening()
.disallowReturnTypeStrengthening()
@@ -471,6 +501,7 @@
.allowConstantArgumentOptimization()
.allowInlining()
.allowMethodStaticizing()
+ .allowParameterRemoval()
.allowParameterReordering()
.allowParameterTypeStrengthening()
.allowReturnTypeStrengthening()
@@ -510,6 +541,11 @@
return self();
}
+ public Joiner disallowParameterRemoval() {
+ builder.disallowParameterRemoval();
+ return self();
+ }
+
public Joiner disallowParameterReordering() {
builder.disallowParameterReordering();
return self();
@@ -552,6 +588,7 @@
Joiner::disallowConstantArgumentOptimization)
.applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
.applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
+ .applyIf(!joiner.builder.isParameterRemovalAllowed(), Joiner::disallowParameterRemoval)
.applyIf(
!joiner.builder.isParameterReorderingAllowed(), Joiner::disallowParameterReordering)
.applyIf(
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 7275e72..fddac9e 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1706,7 +1706,8 @@
}
private boolean isInterfaceMethodNeedingDesugaring(ProgramDefinition item) {
- return options.isInterfaceMethodDesugaringEnabled()
+ return !isMainDexRootSetBuilder()
+ && options.isInterfaceMethodDesugaringEnabled()
&& item.isMethod()
&& item.asMethod().getHolder().isInterface()
&& !item.asMethod().getDefinition().isClassInitializer()
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 8760fb8..bc1ce29 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -533,7 +533,7 @@
}
// Only merge if api reference level of source class is equal to target class. The check is
// somewhat expensive.
- if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
+ if (appView.options().apiModelingOptions().isApiCallerIdentificationEnabled()) {
ComputedApiLevel sourceApiLevel =
getApiReferenceLevelForMerging(appView, apiLevelCompute, sourceClass);
ComputedApiLevel targetApiLevel =
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 2c3b88b..06c4094 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -36,7 +36,7 @@
ProgramMethod inlinee,
InternalOptions options,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
- if (!options.apiModelingOptions().enableApiCallerIdentification) {
+ if (!options.apiModelingOptions().isApiCallerIdentificationEnabled()) {
return true;
}
if (caller.getHolderType() == inlinee.getHolderType()) {
@@ -90,13 +90,19 @@
DexMethod original,
AndroidApiLevelCompute androidApiLevelCompute,
InternalOptions options) {
+ // If we are not using the api database and we have the platform build, then we assume we are
+ // running with boot class path as min api and all definitions are accessible at runtime.
+ if (!androidApiLevelCompute.isEnabled()) {
+ assert !options.apiModelingOptions().enableLibraryApiModeling;
+ return options.isAndroidPlatformBuild();
+ }
+ assert options.apiModelingOptions().enableLibraryApiModeling;
ComputedApiLevel apiLevel =
androidApiLevelCompute.computeApiLevelForLibraryReference(
method.getReference(), ComputedApiLevel.unknown());
if (apiLevel.isUnknownApiLevel()) {
return false;
}
- assert options.apiModelingOptions().enableApiCallerIdentification;
ComputedApiLevel apiLevelOfOriginal =
androidApiLevelCompute.computeApiLevelForLibraryReference(
original, ComputedApiLevel.unknown());
@@ -107,15 +113,21 @@
}
public static boolean isApiSafeForReference(LibraryDefinition definition, AppView<?> appView) {
- return isApiSafeForReference(definition, appView.apiLevelCompute(), appView.options());
+ if (appView.options().isAndroidPlatformBuild()) {
+ assert definition != null;
+ return true;
+ }
+ return isApiSafeForReference(
+ definition, appView.apiLevelCompute(), appView.options(), appView.dexItemFactory());
}
private static boolean isApiSafeForReference(
LibraryDefinition definition,
AndroidApiLevelCompute androidApiLevelCompute,
- InternalOptions options) {
+ InternalOptions options,
+ DexItemFactory factory) {
if (!options.apiModelingOptions().enableApiCallerIdentification) {
- return false;
+ return factory.libraryTypesAssumedToBePresent.contains(definition.getContextType());
}
ComputedApiLevel apiLevel =
androidApiLevelCompute.computeApiLevelForLibraryReference(
@@ -163,7 +175,7 @@
// Program and classpath classes are not api level dependent.
return true;
}
- if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
+ if (!appView.options().apiModelingOptions().isApiCallerIdentificationEnabled()) {
// Conservatively bail out if we don't have api modeling.
return false;
}
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index b30c961..919d998 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -249,13 +249,20 @@
}
}
- private static Collection<?> createStartupProfileProviders(List<Path> startupProfileFiles) {
+ private static Collection<Object> createStartupProfileProviders(List<Path> startupProfileFiles) {
List<Object> startupProfileProviders = new ArrayList<>();
for (Path startupProfileFile : startupProfileFiles) {
- callReflectiveUtilsMethod(
- "createStartupProfileProviderFromDumpFile",
- new Class<?>[] {Path.class},
- fn -> startupProfileProviders.add(fn.apply(new Object[] {startupProfileFile})));
+ boolean found =
+ callReflectiveUtilsMethod(
+ "createStartupProfileProviderFromDumpFile",
+ new Class<?>[] {Path.class},
+ fn -> startupProfileProviders.add(fn.apply(new Object[] {startupProfileFile})));
+ if (!found) {
+ System.out.println(
+ "Unable to add startup profiles as input. "
+ + "Method createStartupProfileProviderFromDumpFile() was not found.");
+ break;
+ }
}
return startupProfileProviders;
}
@@ -278,20 +285,20 @@
}
}
- private static void callReflectiveUtilsMethod(
+ private static boolean callReflectiveUtilsMethod(
String methodName, Class<?>[] parameters, Consumer<Function<Object[], Object>> fnConsumer) {
Class<?> utilsClass;
try {
utilsClass = Class.forName("com.android.tools.r8.utils.CompileDumpUtils");
} catch (ClassNotFoundException e) {
- return;
+ return false;
}
Method declaredMethod;
try {
declaredMethod = utilsClass.getMethod(methodName, parameters);
} catch (NoSuchMethodException e) {
- return;
+ return false;
}
fnConsumer.accept(
@@ -302,6 +309,7 @@
throw new RuntimeException(e);
}
});
+ return true;
}
// We cannot use StringResource since this class is added to the class path and has access only
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
index 32892d7..613701c 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpD8.java
@@ -20,7 +20,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
-import java.util.stream.Collectors;
+import java.util.function.Function;
/**
* Wrapper to make it easy to call D8 mode when compiling a dump file.
@@ -161,12 +161,7 @@
getReflectiveBuilderMethod(commandBuilder, "setAndroidPlatformBuild", boolean.class)
.accept(new Object[] {androidPlatformBuild});
getReflectiveBuilderMethod(commandBuilder, "addStartupProfileProviders", Collection.class)
- .accept(
- new Object[] {
- startupProfileFiles.stream()
- .map(CompileDumpUtils::createStartupProfileProviderFromDumpFile)
- .collect(Collectors.toList())
- });
+ .accept(new Object[] {createStartupProfileProviders(startupProfileFiles)});
if (desugaredLibJson != null) {
commandBuilder.addDesugaredLibraryConfiguration(readAllBytesJava7(desugaredLibJson));
}
@@ -184,6 +179,24 @@
}
}
+ private static Collection<Object> createStartupProfileProviders(List<Path> startupProfileFiles) {
+ List<Object> startupProfileProviders = new ArrayList<>();
+ for (Path startupProfileFile : startupProfileFiles) {
+ boolean found =
+ callReflectiveUtilsMethod(
+ "createStartupProfileProviderFromDumpFile",
+ new Class<?>[] {Path.class},
+ fn -> startupProfileProviders.add(fn.apply(new Object[] {startupProfileFile})));
+ if (!found) {
+ System.out.println(
+ "Unable to add startup profiles as input. "
+ + "Method createStartupProfileProviderFromDumpFile() was not found.");
+ break;
+ }
+ }
+ return startupProfileProviders;
+ }
+
private static Consumer<Object[]> getReflectiveBuilderMethod(
D8Command.Builder builder, String setter, Class<?>... parameters) {
try {
@@ -202,6 +215,33 @@
}
}
+ private static boolean callReflectiveUtilsMethod(
+ String methodName, Class<?>[] parameters, Consumer<Function<Object[], Object>> fnConsumer) {
+ Class<?> utilsClass;
+ try {
+ utilsClass = Class.forName("com.android.tools.r8.utils.CompileDumpUtils");
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+
+ Method declaredMethod;
+ try {
+ declaredMethod = utilsClass.getMethod(methodName, parameters);
+ } catch (NoSuchMethodException e) {
+ return false;
+ }
+
+ fnConsumer.accept(
+ args -> {
+ try {
+ return declaredMethod.invoke(null, args);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ return true;
+ }
+
// We cannot use StringResource since this class is added to the class path and has access only
// to the public APIs.
private static String readAllBytesJava7(Path filePath) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 07322dc..82d7e77 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -59,6 +59,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.Policy;
import com.android.tools.r8.inspector.internal.InspectorImpl;
+import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.desugar.TypeRewriter;
@@ -72,6 +73,7 @@
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
+import com.android.tools.r8.profile.art.ArtProfileOptions;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
@@ -89,6 +91,7 @@
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
@@ -238,7 +241,6 @@
}
void enableProtoShrinking() {
- inlinerOptions.applyInliningToInlinee = true;
enableFieldBitAccessAnalysis = true;
protoShrinking.enableGeneratedMessageLiteShrinking = true;
protoShrinking.enableGeneratedMessageLiteBuilderShrinking = true;
@@ -275,10 +277,14 @@
androidPlatformBuild = isAndroidPlatformBuild;
// Configure options according to platform build assumptions.
// See go/r8platformflag and b/232073181.
- apiModelingOptions().disableMissingApiModeling();
+ apiModelingOptions().disableApiModeling();
enableBackportMethods = false;
}
+ public boolean isAndroidPlatformBuild() {
+ return androidPlatformBuild;
+ }
+
public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
// To print memory one also have to enable printtimes.
public boolean printMemory = System.getProperty("com.android.tools.r8.printmemory") != null;
@@ -342,7 +348,6 @@
public boolean readDebugSetFileEvent = false;
public boolean disableL8AnnotationRemoval =
System.getProperty("com.android.tools.r8.disableL8AnnotationRemoval") != null;
- public boolean enableVisibilityBridgeRemoval = true;
public int callGraphLikelySpuriousCallEdgeThreshold = 50;
@@ -815,7 +820,8 @@
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
- private final StartupOptions startupOptions = new StartupOptions(this);
+ private final ArtProfileOptions artProfileOptions = new ArtProfileOptions();
+ private final StartupOptions startupOptions = new StartupOptions();
private final StartupInstrumentationOptions startupInstrumentationOptions =
new StartupInstrumentationOptions();
public final TestingOptions testing = new TestingOptions();
@@ -877,6 +883,10 @@
return openClosedInterfacesOptions;
}
+ public ArtProfileOptions getArtProfileOptions() {
+ return artProfileOptions;
+ }
+
public StartupOptions getStartupOptions() {
return startupOptions;
}
@@ -1450,6 +1460,11 @@
}
}
+ public interface ApplyInliningToInlineePredicate {
+
+ boolean test(AppView<?> appView, ProgramMethod method, int inliningDepth);
+ }
+
public class InlinerOptions {
public boolean enableInlining =
@@ -1475,15 +1490,12 @@
// GMS Core.
public int inliningControlFlowResolutionBlocksThreshold = 15;
- // TODO(b/141451716): Evaluate the effect of allowing inlining in the inlinee.
- public boolean applyInliningToInlinee =
- System.getProperty("com.android.tools.r8.applyInliningToInlinee") != null;
- public int applyInliningToInlineeMaxDepth = 0;
-
public boolean enableInliningOfInvokesWithClassInitializationSideEffects = true;
public boolean enableInliningOfInvokesWithNullableReceivers = true;
public boolean disableInliningOfLibraryMethodOverrides = true;
+ public ApplyInliningToInlineePredicate applyInliningToInlineePredicateForTesting = null;
+
public int getSimpleInliningInstructionLimit() {
// If a custom simple inlining instruction limit is set, then use that.
if (simpleInliningInstructionLimit >= 0) {
@@ -1497,6 +1509,17 @@
assert isGeneratingDex();
return 5;
}
+
+ public boolean shouldApplyInliningToInlinee(
+ AppView<?> appView, ProgramMethod inlinee, int inliningDepth) {
+ if (applyInliningToInlineePredicateForTesting != null) {
+ return applyInliningToInlineePredicateForTesting.test(appView, inlinee, inliningDepth);
+ }
+ if (protoShrinking.shouldApplyInliningToInlinee(appView, inlinee, inliningDepth)) {
+ return true;
+ }
+ return false;
+ }
}
public class HorizontalClassMergerOptions {
@@ -1699,6 +1722,14 @@
public static class ApiModelTestingOptions {
+ // Flag to specify if we should load the database or not. The api database is used for
+ // library member rebinding.
+ public boolean enableLibraryApiModeling =
+ System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+
+ // The flag enableApiCallerIdentification controls if we can inline or merge targets with
+ // different api levels. It is also the flag that specifies if we assign api levels to
+ // references.
public boolean enableApiCallerIdentification =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public boolean checkAllApiReferencesAreSet =
@@ -1736,11 +1767,31 @@
});
}
+ public boolean isApiLibraryModelingEnabled() {
+ return enableLibraryApiModeling;
+ }
+
+ public boolean isCheckAllApiReferencesAreSet() {
+ return enableLibraryApiModeling && checkAllApiReferencesAreSet;
+ }
+
+ public boolean isApiCallerIdentificationEnabled() {
+ return enableLibraryApiModeling && enableApiCallerIdentification;
+ }
+
+ public void disableApiModeling() {
+ enableLibraryApiModeling = false;
+ enableApiCallerIdentification = false;
+ enableOutliningOfMethods = false;
+ enableStubbingOfClasses = false;
+ checkAllApiReferencesAreSet = false;
+ }
+
/**
* Disable the workarounds for missing APIs. This does not disable the use of the database, just
* the introduction of soft-verification workarounds for potentially missing API references.
*/
- public void disableMissingApiModeling() {
+ public void disableOutliningAndStubbing() {
enableOutliningOfMethods = false;
enableStubbingOfClasses = false;
}
@@ -1749,7 +1800,7 @@
enableApiCallerIdentification = false;
}
- public void disableSubbingOfClasses() {
+ public void disableStubbingOfClasses() {
enableStubbingOfClasses = false;
}
}
@@ -1787,6 +1838,15 @@
public boolean isEnumLiteProtoShrinkingEnabled() {
return enableEnumLiteProtoShrinking;
}
+
+ public boolean shouldApplyInliningToInlinee(
+ AppView<?> appView, ProgramMethod inlinee, int inliningDepth) {
+ if (isProtoShrinkingEnabled() && inliningDepth == 1) {
+ ProtoReferences protoReferences = appView.protoShrinker().getProtoReferences();
+ return inlinee.getHolderType() == protoReferences.generatedMessageLiteType;
+ }
+ return false;
+ }
}
public static class TestingOptions {
@@ -1836,6 +1896,8 @@
public ArgumentPropagatorEventConsumer argumentPropagatorEventConsumer =
ArgumentPropagatorEventConsumer.emptyConsumer();
+ public Predicate<DexProgramClass> isEligibleForBridgeHoisting = Predicates.alwaysTrue();
+
// Force writing the specified bytes as the DEX version content.
public byte[] forceDexVersionBytes = null;
@@ -1910,6 +1972,7 @@
public boolean enableDeadSwitchCaseElimination = true;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableMultiANewArrayDesugaringForClassFiles = false;
+ public boolean enableRedundantConstructorBridgeRemoval = false;
public boolean enableSwitchToIfRewriting = true;
public boolean enableEnumUnboxingDebugLogs =
System.getProperty("com.android.tools.r8.enableEnumUnboxingDebugLogs") != null;
@@ -2692,4 +2755,8 @@
public boolean canHaveDalvikEmptyAnnotationSetBug() {
return canHaveBugPresentUntil(AndroidApiLevel.J_MR1);
}
+
+ public boolean canHaveNonReboundConstructorInvoke() {
+ return isGeneratingDex() && minApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ }
}
diff --git a/src/test/debugTestResources/ClassInitializerEmpty.java b/src/test/debugTestResources/ClassInitializerEmpty.java
deleted file mode 100644
index 9062d62..0000000
--- a/src/test/debugTestResources/ClassInitializerEmpty.java
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2017, 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.
-
-public class ClassInitializerEmpty {
-
- static {
- }
-
- public static void main(String[] args) {
- }
-}
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index 8f465ff..de0565f 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.benchmarks.BenchmarkResults;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -29,6 +31,7 @@
}
private StringBuilder proguardMapOutputBuilder = null;
+ private boolean enableMissingLibraryApiModeling = true;
@Override
public boolean isD8TestBuilder() {
@@ -80,6 +83,7 @@
BenchmarkResults benchmarkResults)
throws CompilationFailedException {
libraryDesugaringTestConfiguration.configure(builder);
+ builder.setEnableExperimentalMissingLibraryApiModeling(enableMissingLibraryApiModeling);
ToolHelper.runAndBenchmarkD8(builder, optionsConsumer, benchmarkResults);
return new D8TestCompileResult(
getState(),
@@ -130,6 +134,12 @@
return self();
}
+ public D8TestBuilder addArtProfileForRewriting(
+ ArtProfileProvider artProfileProvider, ArtProfileConsumer residualArtProfileConsumer) {
+ builder.addArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer);
+ return self();
+ }
+
public D8TestBuilder addStartupProfileProviders(
StartupProfileProvider... startupProfileProviders) {
builder.addStartupProfileProviders(startupProfileProviders);
diff --git a/src/test/java/com/android/tools/r8/D8TestCompileResult.java b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
index 6d7634c..84550ca 100644
--- a/src/test/java/com/android/tools/r8/D8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestCompileResult.java
@@ -52,6 +52,6 @@
@Override
public D8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
- return new D8TestRunResult(app, runtime, result, proguardMap);
+ return new D8TestRunResult(app, runtime, result, proguardMap, state);
}
}
diff --git a/src/test/java/com/android/tools/r8/D8TestRunResult.java b/src/test/java/com/android/tools/r8/D8TestRunResult.java
index 4e54359..313a39e 100644
--- a/src/test/java/com/android/tools/r8/D8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestRunResult.java
@@ -17,8 +17,12 @@
private final String proguardMap;
public D8TestRunResult(
- AndroidApp app, TestRuntime runtime, ProcessResult result, String proguardMap) {
- super(app, runtime, result);
+ AndroidApp app,
+ TestRuntime runtime,
+ ProcessResult result,
+ String proguardMap,
+ TestState state) {
+ super(app, runtime, result, state);
this.proguardMap = proguardMap;
}
diff --git a/src/test/java/com/android/tools/r8/DXTestCompileResult.java b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
index a3cf644..d333632 100644
--- a/src/test/java/com/android/tools/r8/DXTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestCompileResult.java
@@ -41,6 +41,6 @@
@Override
public DXTestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
- return new DXTestRunResult(app, runtime, result);
+ return new DXTestRunResult(app, runtime, result, state);
}
}
diff --git a/src/test/java/com/android/tools/r8/DXTestRunResult.java b/src/test/java/com/android/tools/r8/DXTestRunResult.java
index b395e90..23ba769 100644
--- a/src/test/java/com/android/tools/r8/DXTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/DXTestRunResult.java
@@ -9,8 +9,9 @@
public class DXTestRunResult extends SingleTestRunResult<DXTestRunResult> {
- public DXTestRunResult(AndroidApp app, TestRuntime runtime, ProcessResult result) {
- super(app, runtime, result);
+ public DXTestRunResult(
+ AndroidApp app, TestRuntime runtime, ProcessResult result, TestState state) {
+ super(app, runtime, result, state);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/Dex2OatTestRunResult.java b/src/test/java/com/android/tools/r8/Dex2OatTestRunResult.java
index 145a48c..6918607 100644
--- a/src/test/java/com/android/tools/r8/Dex2OatTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/Dex2OatTestRunResult.java
@@ -13,8 +13,9 @@
public class Dex2OatTestRunResult extends SingleTestRunResult<Dex2OatTestRunResult> {
- public Dex2OatTestRunResult(AndroidApp app, TestRuntime runtime, ProcessResult result) {
- super(app, runtime, result);
+ public Dex2OatTestRunResult(
+ AndroidApp app, TestRuntime runtime, ProcessResult result, TestState state) {
+ super(app, runtime, result, state);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
index 8c2623a..3212c9d 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestCompileResult.java
@@ -73,6 +73,6 @@
@Override
protected ExternalR8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
- return new ExternalR8TestRunResult(app, outputJar, proguardMap, runtime, result);
+ return new ExternalR8TestRunResult(app, outputJar, proguardMap, runtime, result, state);
}
}
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestRunResult.java b/src/test/java/com/android/tools/r8/ExternalR8TestRunResult.java
index 469e5b5..1a9bfbd 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestRunResult.java
@@ -23,8 +23,9 @@
Path outputJar,
String proguardMap,
TestRuntime runtime,
- ProcessResult result) {
- super(app, runtime, result);
+ ProcessResult result,
+ TestState state) {
+ super(app, runtime, result, state);
this.outputJar = outputJar;
this.proguardMap = proguardMap;
}
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java b/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
index 853c057..9f4b5c5 100644
--- a/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
+++ b/src/test/java/com/android/tools/r8/GenerateMainDexListRunResult.java
@@ -17,7 +17,7 @@
List<String> mainDexList;
public GenerateMainDexListRunResult(List<String> mainDexList, TestState state) {
- super(null, null, null);
+ super(null, null, null, state);
this.mainDexList = mainDexList;
this.state = state;
}
diff --git a/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
index c0665cf..b9f008f 100644
--- a/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/GenerateMainDexListTestBuilder.java
@@ -55,11 +55,6 @@
}
@Override
- public DebugTestConfig debugConfig() {
- throw new Unimplemented("No support for debug configuration");
- }
-
- @Override
public GenerateMainDexListTestBuilder addRunClasspathFiles(Collection<Path> files) {
throw new Unimplemented("No support for run class path");
}
diff --git a/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java b/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java
index d018732..d3ff163 100644
--- a/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/IntermediateCfD8TestBuilder.java
@@ -50,11 +50,6 @@
}
@Override
- public DebugTestConfig debugConfig() {
- throw new Unimplemented("Unsupported debug config as of now...");
- }
-
- @Override
public IntermediateCfD8TestBuilder addProgramFiles(Collection<Path> files) {
cf2cf.addProgramFiles(files);
return self();
diff --git a/src/test/java/com/android/tools/r8/JvmTestBuilder.java b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
index 718cf4d..e1ab7a6 100644
--- a/src/test/java/com/android/tools/r8/JvmTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/JvmTestBuilder.java
@@ -4,8 +4,6 @@
package com.android.tools.r8;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.debug.CfDebugTestConfig;
-import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApp;
@@ -63,12 +61,7 @@
ProcessResult result =
ToolHelper.runJava(
runtime.asCf(), vmArguments, classpath, ObjectArrays.concat(mainClass, args));
- return new JvmTestRunResult(builder.build(), runtime, result);
- }
-
- @Override
- public DebugTestConfig debugConfig() {
- return new CfDebugTestConfig().addPaths(classpath);
+ return new JvmTestRunResult(builder.build(), runtime, result, getState());
}
@Override
diff --git a/src/test/java/com/android/tools/r8/JvmTestRunResult.java b/src/test/java/com/android/tools/r8/JvmTestRunResult.java
index fdd2580..2c911d2 100644
--- a/src/test/java/com/android/tools/r8/JvmTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/JvmTestRunResult.java
@@ -9,8 +9,9 @@
public class JvmTestRunResult extends SingleTestRunResult<JvmTestRunResult> {
- public JvmTestRunResult(AndroidApp app, TestRuntime runtime, ProcessResult result) {
- super(app, runtime, result);
+ public JvmTestRunResult(
+ AndroidApp app, TestRuntime runtime, ProcessResult result, TestState state) {
+ super(app, runtime, result, state);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 3050989..4a22769 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -11,6 +11,8 @@
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.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidAppConsumers;
import com.android.tools.r8.utils.ConsumerUtils;
@@ -33,6 +35,7 @@
private final AndroidApiLevel apiLevel;
private final Backend backend;
+ private final L8Command.Builder l8Builder;
private final TestState state;
private CompilationMode mode = CompilationMode.RELEASE;
@@ -50,6 +53,7 @@
this.apiLevel = apiLevel;
this.backend = backend;
this.state = state;
+ this.l8Builder = L8Command.builder(state.getDiagnosticsHandler());
}
public static L8TestBuilder create(AndroidApiLevel apiLevel, Backend backend, TestState state) {
@@ -167,15 +171,14 @@
throws IOException, CompilationFailedException, ExecutionException {
// We wrap exceptions in a RuntimeException to call this from a lambda.
AndroidAppConsumers sink = new AndroidAppConsumers();
- L8Command.Builder l8Builder =
- L8Command.builder(state.getDiagnosticsHandler())
- .addProgramFiles(programFiles)
- .addLibraryFiles(getLibraryFiles())
- .setMode(mode)
- .setIncludeClassesChecksum(true)
- .addDesugaredLibraryConfiguration(desugaredLibrarySpecification)
- .setMinApiLevel(apiLevel.getLevel())
- .setProgramConsumer(computeProgramConsumer(sink));
+ l8Builder
+ .addProgramFiles(programFiles)
+ .addLibraryFiles(getLibraryFiles())
+ .setMode(mode)
+ .setIncludeClassesChecksum(true)
+ .addDesugaredLibraryConfiguration(desugaredLibrarySpecification)
+ .setMinApiLevel(apiLevel.getLevel())
+ .setProgramConsumer(computeProgramConsumer(sink));
addProgramClassFileData(l8Builder);
Path mapping = null;
ImmutableList<String> allKeepRules = null;
@@ -264,4 +267,10 @@
private Collection<Path> getLibraryFiles() {
return libraryFiles;
}
+
+ public L8TestBuilder addArtProfileForRewriting(
+ ArtProfileProvider artProfileProvider, ArtProfileConsumer residualArtProfileConsumer) {
+ l8Builder.addArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer);
+ return this;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index dbfd4c6..9b80d3b 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -280,11 +280,6 @@
}
@Override
- public DebugTestConfig debugConfig() {
- throw new Unimplemented("No support for debug config");
- }
-
- @Override
public ProguardTestBuilder addOptionsModification(Consumer<InternalOptions> optionsConsumer) {
throw new Unimplemented("No support for changing internal options");
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index faf07a6..01cc662 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -63,6 +63,6 @@
@Override
public ProguardTestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
- return new ProguardTestRunResult(app, runtime, result, proguardMap);
+ return new ProguardTestRunResult(app, runtime, result, proguardMap, state);
}
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestRunResult.java b/src/test/java/com/android/tools/r8/ProguardTestRunResult.java
index c5cccbe..4c5f121 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestRunResult.java
@@ -18,8 +18,12 @@
private final String proguardMap;
public ProguardTestRunResult(
- AndroidApp app, TestRuntime runtime, ProcessResult result, String proguardMap) {
- super(app, runtime, result);
+ AndroidApp app,
+ TestRuntime runtime,
+ ProcessResult result,
+ String proguardMap,
+ TestState state) {
+ super(app, runtime, result, state);
this.proguardMap = proguardMap;
}
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index 1c7a5ea..ac12923 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -905,7 +905,7 @@
@Test
public void defaultApiModelingState() throws Exception {
ApiModelTestingOptions options = parse("").getInternalOptions().apiModelingOptions();
- assertTrue(options.enableApiCallerIdentification);
+ assertTrue(options.isApiCallerIdentificationEnabled());
assertTrue(options.enableOutliningOfMethods);
assertTrue(options.enableStubbingOfClasses);
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 7b5d60b..f3f7d40 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -190,6 +190,8 @@
.withProguardCompatibilityMode(enableProguardCompatibilityMode)
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withInterfaceMethodDesugaring(OffOrAuto.Auto)
+ .withBuilderTransformation(
+ builder -> builder.setEnableExperimentalMissingLibraryApiModeling(true))
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
@@ -233,6 +235,8 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(ToolHelper::allowTestProguardOptions)
.withBuilderTransformation(
+ builder -> builder.setEnableExperimentalMissingLibraryApiModeling(true))
+ .withBuilderTransformation(
b ->
b.addProguardConfiguration(
getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 53ff395..a844c1f 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -15,6 +15,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
import com.android.tools.r8.shaking.CheckEnumUnboxedRule;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
import com.android.tools.r8.shaking.KeepUnusedReturnValueRule;
@@ -64,6 +66,7 @@
private AllowedDiagnosticMessages allowedDiagnosticMessages = AllowedDiagnosticMessages.NONE;
private boolean allowUnusedProguardConfigurationRules = false;
+ private boolean enableMissingLibraryApiModeling = true;
private CollectingGraphConsumer graphConsumer = null;
private List<String> keepRules = new ArrayList<>();
private List<Path> mainDexRulesFiles = new ArrayList<>();
@@ -138,6 +141,7 @@
ToolHelper.addSyntheticProguardRulesConsumerForTesting(
builder, rules -> box.syntheticProguardRules = rules);
libraryDesugaringTestConfiguration.configure(builder);
+ builder.setEnableExperimentalMissingLibraryApiModeling(enableMissingLibraryApiModeling);
ToolHelper.runAndBenchmarkR8WithoutResult(
builder,
optionsConsumer.andThen(
@@ -778,6 +782,12 @@
return self();
}
+ public T addArtProfileForRewriting(
+ ArtProfileProvider artProfileProvider, ArtProfileConsumer residualArtProfileConsumer) {
+ builder.addArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer);
+ return self();
+ }
+
public T addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
builder.addStartupProfileProviders(startupProfileProviders);
return self();
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index be01cef..2d25b7c 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -151,7 +151,7 @@
@Override
public R8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
- return new R8TestRunResult(app, runtime, result, proguardMap, this::graphInspector);
+ return new R8TestRunResult(app, runtime, result, proguardMap, this::graphInspector, state);
}
public R8TestCompileResult addFeatureSplitsToRunClasspathFiles() {
diff --git a/src/test/java/com/android/tools/r8/R8TestRunResult.java b/src/test/java/com/android/tools/r8/R8TestRunResult.java
index 716971d..8734544 100644
--- a/src/test/java/com/android/tools/r8/R8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestRunResult.java
@@ -30,8 +30,9 @@
TestRuntime runtime,
ProcessResult result,
String proguardMap,
- GraphInspectorSupplier graphInspector) {
- super(app, runtime, result);
+ GraphInspectorSupplier graphInspector,
+ TestState state) {
+ super(app, runtime, result, state);
this.proguardMap = proguardMap;
this.graphInspector = graphInspector;
}
diff --git a/src/test/java/com/android/tools/r8/SingleTestRunResult.java b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
index 68e132a..e94bfcf 100644
--- a/src/test/java/com/android/tools/r8/SingleTestRunResult.java
+++ b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
@@ -10,12 +10,14 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.naming.retrace.StackTrace;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.io.IOException;
import java.io.PrintStream;
+import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -23,12 +25,15 @@
public abstract class SingleTestRunResult<RR extends SingleTestRunResult<RR>>
extends TestRunResult<RR> {
+ private final TestState state;
protected final AndroidApp app;
private final TestRuntime runtime;
private final ProcessResult result;
private boolean executedSatisfyingRuntime = false;
- public SingleTestRunResult(AndroidApp app, TestRuntime runtime, ProcessResult result) {
+ public SingleTestRunResult(
+ AndroidApp app, TestRuntime runtime, ProcessResult result, TestState state) {
+ this.state = state;
this.app = app;
this.runtime = runtime;
this.result = result;
@@ -38,6 +43,10 @@
return false;
}
+ public TestState getState() {
+ return state;
+ }
+
public AndroidApp app() {
return app;
}
@@ -219,4 +228,13 @@
}
return self();
}
+
+ public <E extends Throwable> RR debugger(ThrowingConsumer<DebugTestConfig, E> consumer)
+ throws E, IOException {
+ Path out = state.getNewTempFolder().resolve("out.zip");
+ app.writeToZip(out, runtime.isCf() ? OutputMode.ClassFile : OutputMode.DexIndexed);
+ DebugTestConfig config = DebugTestConfig.create(runtime, out);
+ consumer.accept(config);
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 4f8181d..a575af1 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.profile.art.ArtProfileCollection;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 52d84fc..2faa8fd 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
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.utils.ListUtils;
import com.google.common.collect.ImmutableList;
@@ -99,8 +98,6 @@
return run(runtime, mainClass.getTypeName(), args);
}
- public abstract DebugTestConfig debugConfig();
-
public abstract T addProgramFiles(Collection<Path> files);
public abstract T addProgramClassFileData(Collection<byte[]> classes);
diff --git a/src/test/java/com/android/tools/r8/TestBuilderCollection.java b/src/test/java/com/android/tools/r8/TestBuilderCollection.java
index 0192832..9f59e8e 100644
--- a/src/test/java/com/android/tools/r8/TestBuilderCollection.java
+++ b/src/test/java/com/android/tools/r8/TestBuilderCollection.java
@@ -33,11 +33,6 @@
}
@Override
- public DebugTestConfig debugConfig() {
- throw new Unimplemented("Unsupported debug config as of now...");
- }
-
- @Override
public T addProgramFiles(Collection<Path> files) {
return forEach(b -> b.addProgramFiles(files));
}
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index b15e669..323cf72 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -19,9 +19,7 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.benchmarks.BenchmarkResults;
-import com.android.tools.r8.debug.CfDebugTestConfig;
import com.android.tools.r8.debug.DebugTestConfig;
-import com.android.tools.r8.debug.DexDebugTestConfig;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -605,20 +603,22 @@
return disassemble(System.out);
}
+ @Deprecated
public DebugTestConfig debugConfig() {
+ return debugConfig(
+ getBackend().isCf()
+ ? TestRuntime.getDefaultCfRuntime()
+ : new DexRuntime(ToolHelper.getDexVm()));
+ }
+
+ public DebugTestConfig debugConfig(TestRuntime runtime) {
+ assert runtime.getBackend() == getBackend();
// Rethrow exceptions since debug config is usually used in a delayed wrapper which
// does not declare exceptions.
try {
Path out = state.getNewTempFolder().resolve("out.zip");
app.writeToZip(out, getOutputMode());
- switch (getBackend()) {
- case CF:
- return new CfDebugTestConfig().addPaths(out);
- case DEX:
- return new DexDebugTestConfig().addPaths(out);
- default:
- throw new Unreachable();
- }
+ return DebugTestConfig.create(runtime, out);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -687,7 +687,8 @@
Path jarFile = tmp.resolve("out.jar");
Path oatFile = tmp.resolve("out.oat");
app.writeToZip(jarFile, OutputMode.DexIndexed);
- return new Dex2OatTestRunResult(app, runtime, ToolHelper.runDex2OatRaw(jarFile, oatFile, vm));
+ return new Dex2OatTestRunResult(
+ app, runtime, ToolHelper.runDex2OatRaw(jarFile, oatFile, vm), state);
}
public CR benchmarkCodeSize(BenchmarkResults results) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 141f879..165cd83 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -342,17 +342,6 @@
return compile().run(runtime, mainClass, args);
}
- @Override
- public DebugTestConfig debugConfig() {
- // Rethrow exceptions since debug config is usually used in a delayed wrapper which
- // does not declare exceptions.
- try {
- return compile().debugConfig();
- } catch (CompilationFailedException e) {
- throw new RuntimeException(e);
- }
- }
-
public T setMode(CompilationMode mode) {
builder.setMode(mode);
return self();
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
index c4fc8c4..dd84b92 100644
--- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -25,6 +25,8 @@
// Built via the methods found below. Defaults to no applicable parameters, i.e., the emtpy set.
private Predicate<TestParameters> filter = param -> false;
private boolean hasDexRuntimeFilter = false;
+ // TODO(b/245066448): Enable bot with testing new art master.
+ private boolean allowMaster = System.getProperty("com.android.tools.r8.artmaster") != null;
TestParametersBuilder() {}
@@ -40,7 +42,10 @@
private TestParametersBuilder withDexRuntimeFilter(Predicate<DexVm.Version> predicate) {
hasDexRuntimeFilter = true;
return withFilter(
- p -> p.isDexRuntime() && predicate.test(p.getRuntime().asDex().getVm().getVersion()));
+ p ->
+ p.isDexRuntime()
+ && (allowMaster || p.getDexRuntimeVersion().isOlderThan(DexVm.Version.MASTER))
+ && predicate.test(p.getDexRuntimeVersion()));
}
public TestParametersBuilder withNoneRuntime() {
@@ -130,12 +135,13 @@
/** Add all available DEX runtimes including master. */
public TestParametersBuilder withDexRuntimesIncludingMaster() {
- return withDexRuntimeFilter(vm -> true);
+ this.allowMaster = true;
+ return withDexRuntimes();
}
/** Add all available DEX runtimes except master. */
public TestParametersBuilder withDexRuntimes() {
- return withDexRuntimeFilter(vm -> vm != DexVm.Version.MASTER);
+ return withDexRuntimeFilter(vm -> true);
}
public TestParametersBuilder withDexRuntimesAndAllApiLevels() {
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index adb87d2..3524d63 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -19,6 +19,10 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
// Base class for the runtime structure in the test parameters.
public abstract class TestRuntime {
@@ -375,6 +379,26 @@
}
}
+ public <T> T match(Function<CfRuntime, T> onCf, BiFunction<DexRuntime, DexVm.Version, T> onDex) {
+ if (isCf()) {
+ return onCf.apply(asCf());
+ }
+ if (isDex()) {
+ return onDex.apply(asDex(), asDex().getVersion());
+ }
+ throw new Unreachable();
+ }
+
+ public void match(Consumer<CfRuntime> onCf, BiConsumer<DexRuntime, DexVm.Version> onDex) {
+ if (isCf()) {
+ onCf.accept(asCf());
+ } else if (isDex()) {
+ onDex.accept(asDex(), asDex().getVersion());
+ } else {
+ throw new Unreachable();
+ }
+ }
+
public boolean isDex() {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f31c1ae..5fb11f1 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.benchmarks.BenchmarkResults;
-import com.android.tools.r8.desugar.desugaredlibrary.jdk11.DesugaredLibraryJDK11Undesugarer;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.Unreachable;
@@ -194,11 +193,6 @@
: Paths.get(LIBS_DIR, "library_desugar_conversions.jar");
}
- public static Path getUndesugaredJdk11LibJarForTesting() {
- return DesugaredLibraryJDK11Undesugarer.undesugaredJarJDK11(
- Paths.get("build/libs"), DESUGARED_JDK_11_LIB_JAR);
- }
-
public static boolean isLocalDevelopment() {
return System.getProperty("local_development", "0").equals("1");
}
@@ -274,7 +268,7 @@
V6_0_1("6.0.1"),
V7_0_0("7.0.0"),
V8_1_0("8.1.0"),
- // TODO(b//204855476): Remove DEFAULT.
+ // TODO(b/204855476): Remove DEFAULT.
DEFAULT("default"),
V9_0_0("9.0.0"),
V10_0_0("10.0.0"),
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 7de78a2..29c9709 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -46,7 +46,7 @@
.resolve("new_api_database.ser");
// Update the API_LEVEL below to have the database generated for a new api level.
- private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.S;
+ private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.LATEST;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -103,9 +103,9 @@
}));
});
// These numbers will change when updating api-versions.xml
- assertEquals(5065, parsedApiClasses.size());
- assertEquals(26492, numberOfFields.get());
- assertEquals(40475, numberOfMethods.get());
+ assertEquals(5272, parsedApiClasses.size());
+ assertEquals(27868, numberOfFields.get());
+ assertEquals(42268, numberOfMethods.get());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
index 524147f..353c709 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelCovariantReturnTypeTest.java
@@ -42,6 +42,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
index 0a72589..c8c4fe2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
@@ -59,6 +59,7 @@
.setMinApi(parameters.getApiLevel())
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::disableOutlining)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), mockLevel))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
index 5798d5a..dea3e93 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
@@ -60,6 +60,7 @@
.setMinApi(parameters.getApiLevel())
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::disableOutlining)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), mockLevel))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java
index 5392a35..27ba7ed 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java
@@ -52,6 +52,7 @@
.apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
index ecbd92e..8ef728e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -53,6 +53,7 @@
.apply(setMockApiLevelForClass(Api.class, L_MR1))
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.inspect(
inspector ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
index f4465a4..8a5904a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticFieldTest.java
@@ -49,6 +49,7 @@
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.inspect(
inspector ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
index 9565973..6bd30af 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
@@ -47,6 +47,7 @@
.apply(setMockApiLevelForClass(ApiException.class, exceptionApiLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(ApiException.class, exceptionApiLevel))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.addHorizontallyMergedClassesInspector(
horizontallyMergedClassesInspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelObjectInitTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelObjectInitTest.java
index e5118ab..cffda67 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelObjectInitTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelObjectInitTest.java
@@ -63,6 +63,7 @@
.addLibraryClasses(LibraryClass.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters.getApiLevel())
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.L))
.apply(setMockApiLevelForMethod(declaredConstructor, AndroidApiLevel.L))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, AndroidApiLevel.N))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index 57eaa67..e826dd7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
@@ -60,7 +59,6 @@
.setMinApi(parameters.getApiLevel())
.addAndroidBuildVersion()
.apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
.apply(setMockApiLevelForMethod(addedOn23(), methodApiLevel))
// TODO(b/213552119): Remove when enabled by default.
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
@@ -162,7 +160,7 @@
// Only present from api level 19.
public static class LibraryClass {
- public void addedOn23() {
+ public static void addedOn23() {
System.out.println("LibraryClass::addedOn23");
}
}
@@ -171,12 +169,9 @@
@NeverInline
public static void test() {
- if (AndroidBuildVersion.VERSION >= 19) {
- LibraryClass libraryClass = new LibraryClass();
- if (AndroidBuildVersion.VERSION >= 23) {
- libraryClass.addedOn23();
- libraryClass.addedOn23();
- }
+ if (AndroidBuildVersion.VERSION >= 23) {
+ LibraryClass.addedOn23();
+ LibraryClass.addedOn23();
}
System.out.println("Hello World");
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index 9793b9b..978d068 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -35,7 +35,6 @@
public class ApiModelOutlineHorizontalMergingTest extends TestBase {
private final AndroidApiLevel libraryClassApiLevel = AndroidApiLevel.K;
- private final AndroidApiLevel otherLibraryClassApiLevel = AndroidApiLevel.K;
private final AndroidApiLevel firstMethodApiLevel = AndroidApiLevel.M;
private final AndroidApiLevel secondMethodApiLevel = AndroidApiLevel.O_MR1;
@@ -62,10 +61,10 @@
.apply(
setMockApiLevelForMethod(
LibraryClass.class.getMethod("addedOn27"), secondMethodApiLevel))
- .apply(setMockApiLevelForClass(OtherLibraryClass.class, otherLibraryClassApiLevel))
+ .apply(setMockApiLevelForClass(OtherLibraryClass.class, libraryClassApiLevel))
.apply(
setMockApiLevelForDefaultInstanceInitializer(
- OtherLibraryClass.class, otherLibraryClassApiLevel))
+ OtherLibraryClass.class, libraryClassApiLevel))
.apply(
setMockApiLevelForMethod(
OtherLibraryClass.class.getMethod("addedOn23"), firstMethodApiLevel))
@@ -101,7 +100,11 @@
.inspect(
inspector -> {
// TODO(b/187675788): Update when horizontal merging is enabled for D8 for debug mode.
- if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
+ if (parameters.getApiLevel().isLessThan(libraryClassApiLevel)) {
+ // We have generated 4 outlines two having api level 23 and two having api level 27
+ // and 2 outlines for each instance initializer.
+ assertEquals(11, inspector.allClasses().size());
+ } else if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
// We have generated 4 outlines two having api level 23 and two having api level 27.
assertEquals(7, inspector.allClasses().size());
} else if (parameters.getApiLevel().isLessThan(secondMethodApiLevel)) {
@@ -166,8 +169,12 @@
assertEquals(3, inspector.allClasses().size());
} else if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
// We have generated 4 outlines two having api level 23 and two having api level 27.
+ // If less than the library api level then we have synthesized two instance initializer
+ // outlines as well.
// Check that the levels are horizontally merged.
- assertEquals(5, inspector.allClasses().size());
+ assertEquals(
+ parameters.getApiLevel().isLessThan(libraryClassApiLevel) ? 6 : 5,
+ inspector.allClasses().size());
assertEquals(2, outlinedAddedOn23.size());
assertTrue(
outlinedAddedOn23.stream()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
new file mode 100644
index 0000000..a362bd3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
@@ -0,0 +1,165 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import 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 ApiModelOutlineInstanceInitializerSuperTest extends TestBase {
+
+ private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+
+ private static final String[] EXPECTED = new String[] {"Hello ", "World!"};
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class, ProgramExtendsLibraryClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(
+ setMockApiLevelForMethod(
+ LibraryClass.class.getDeclaredConstructor(String.class), classApiLevel))
+ .apply(setMockApiLevelForMethod(LibraryClass.class.getMethod("print"), classApiLevel))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(ProgramExtendsLibraryClass.class)
+ .addDontObfuscate()
+ .compile()
+ .inspect(inspector -> inspect(inspector, true))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
+ // Because each of the outline context also have a super call, R8 will inline back the outline
+ // because the super call has the same api level as the outlinee.
+ verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
+ .isOutlinedFromUntil(
+ ProgramExtendsLibraryClass.class.getMethod("print"),
+ isR8 ? AndroidApiLevel.B : classApiLevel);
+ verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(String.class))
+ .isOutlinedFromUntil(
+ ProgramExtendsLibraryClass.class.getDeclaredConstructor(String.class),
+ isR8 ? AndroidApiLevel.B : classApiLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel)) {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ runResult.assertSuccessWithOutputLines("Not calling API");
+ }
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ private final String arg;
+
+ public LibraryClass(String arg) {
+ this.arg = arg;
+ }
+
+ public void print() {
+ System.out.println(arg);
+ }
+ }
+
+ public static class ProgramExtendsLibraryClass extends LibraryClass {
+
+ private final LibraryClass otherArg;
+
+ public ProgramExtendsLibraryClass(String arg) {
+ super(arg); // <-- this cannot be outlined
+ otherArg = new LibraryClass("World!"); // <-- this should be outlined.
+ }
+
+ @Override
+ public void print() {
+ super.print();
+ otherArg.print();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ new ProgramExtendsLibraryClass("Hello ").print();
+ } else {
+ System.out.println("Not calling API");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
index ea08c59..b0a5fb7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
@@ -113,11 +113,11 @@
private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
Method mainMethod = Main.class.getMethod("main", String[].class);
verifyThat(inspector, parameters, Argument.class.getDeclaredConstructor(String.class))
- .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+ .isOutlinedFromUntil(mainMethod, classApiLevel);
verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(Argument.class))
- .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+ .isOutlinedFromUntil(mainMethod, classApiLevel);
verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
- .isOutlinedFromUntil(mainMethod, isR8 ? AndroidApiLevel.B : classApiLevel);
+ .isOutlinedFromUntil(mainMethod, classApiLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
index 0b2f270..3504527 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assume.assumeTrue;
@@ -55,8 +54,9 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableStubbingOfClasses)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ // We only model the class and not the default initializer, otherwise we outline the new
+ // instance call and remove the last reference in non-outlined code.
.apply(setMockApiLevelForClass(LibraryClass.class, libraryClassLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryClassLevel))
.apply(setMockApiLevelForMethod(apiMethod(), libraryMethodLevel));
}
@@ -123,7 +123,7 @@
public static class LibraryClass {
// Only present from api level 30
- public void foo() {
+ public static void foo() {
System.out.println("LibraryClass::foo");
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 21db603..22f79df 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
@@ -64,9 +63,6 @@
.setMinApi(parameters.getApiLevel())
.addAndroidBuildVersion()
.apply(setMockApiLevelForClass(LibraryClass.class, initialLibraryMockLevel))
- .apply(
- setMockApiLevelForDefaultInstanceInitializer(
- LibraryClass.class, initialLibraryMockLevel))
.apply(setMockApiLevelForMethod(addedOn23(), initialLibraryMockLevel))
.apply(setMockApiLevelForMethod(addedOn27(), finalLibraryMethodLevel))
// TODO(b/213552119): Remove when enabled by default.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 178c115..016a147 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithHolderAndName;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
@@ -28,6 +29,7 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
@@ -122,22 +124,23 @@
TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
compilerBuilder.addOptionsModification(
options -> {
+ options.apiModelingOptions().enableLibraryApiModeling = true;
options.apiModelingOptions().enableApiCallerIdentification = true;
});
}
- static void enableStubbingOfClasses(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ public static void enableStubbingOfClasses(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
compilerBuilder.addOptionsModification(
options -> {
- options.apiModelingOptions().enableApiCallerIdentification = true;
+ options.apiModelingOptions().enableLibraryApiModeling = true;
options.apiModelingOptions().enableStubbingOfClasses = true;
});
}
- static void enableOutliningOfMethods(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ public static void enableOutliningOfMethods(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
compilerBuilder.addOptionsModification(
options -> {
- options.apiModelingOptions().enableApiCallerIdentification = true;
+ options.apiModelingOptions().enableLibraryApiModeling = true;
options.apiModelingOptions().enableOutliningOfMethods = true;
});
}
@@ -167,6 +170,19 @@
options -> options.apiModelingOptions().enableOutliningOfMethods = false);
}
+ public static void disableApiCallerIdentification(
+ TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ compilerBuilder.addOptionsModification(
+ options -> options.apiModelingOptions().enableApiCallerIdentification = false);
+ }
+
+ public static void disableApiModeling(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ disableOutliningAndStubbing(compilerBuilder);
+ disableApiCallerIdentification(compilerBuilder);
+ compilerBuilder.addOptionsModification(
+ options -> options.apiModelingOptions().enableLibraryApiModeling = false);
+ }
+
static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
ThrowableConsumer<T> addTracedApiReferenceLevelCallBack(
BiConsumer<MethodReference, AndroidApiLevel> consumer) {
@@ -334,7 +350,7 @@
assertThat(target, not(CodeMatchers.invokesMethod(candidate)));
}
- void isOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
+ void isOutlinedFromUntil(Executable method, AndroidApiLevel apiLevel) {
if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
isOutlinedFrom(method);
} else {
@@ -342,7 +358,7 @@
}
}
- void isOutlinedFrom(Method method) {
+ void isOutlinedFrom(Executable method) {
// Check that the call is in a synthetic class.
List<FoundMethodSubject> outlinedMethod =
inspector.allClasses().stream()
@@ -350,18 +366,20 @@
.filter(
methodSubject ->
methodSubject.isSynthetic()
- && invokesMethodWithName(methodOfInterest.getMethodName())
+ && invokesMethodWithHolderAndName(
+ methodOfInterest.getHolderClass().getTypeName(),
+ methodOfInterest.getMethodName())
.matches(methodSubject))
.collect(Collectors.toList());
assertEquals(1, outlinedMethod.size());
// Assert that method invokes the outline
- MethodSubject caller = inspector.method(method);
+ MethodSubject caller = inspector.method(Reference.methodFromMethod(method));
assertThat(caller, isPresent());
assertThat(caller, invokesMethod(outlinedMethod.get(0)));
}
- void isNotOutlinedFrom(Method method) {
- MethodSubject caller = inspector.method(method);
+ void isNotOutlinedFrom(Executable method) {
+ MethodSubject caller = inspector.method(Reference.methodFromMethod(method));
assertThat(caller, isPresent());
assertThat(caller, invokesMethodWithName(methodOfInterest.getMethodName()));
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index d08bb2f..73f2712 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -294,7 +294,7 @@
// Cls1#foo and Cls2#bar should refer to Base#foo.
- MethodSubject barInCls2 = cls2Subject.method("void", "bar", "java.lang.String");
+ MethodSubject barInCls2 = baseSubject.method("void", "bar", "java.lang.String");
assertThat(barInCls2, isPresent());
// Cls1#foo has been moved to Base#foo as a result of bridge hoisting.
@@ -404,7 +404,7 @@
// DerivedString2#bar should refer to Base#foo.
- MethodSubject barInSub = subSubject.method("void", "bar", "java.lang.String");
+ MethodSubject barInSub = baseSubject.method("void", "bar", "java.lang.String");
assertThat(barInSub, isPresent());
if (parameters.isDexRuntime()) {
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodTest.java
index 10fc6da..449975b 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.google.common.collect.ImmutableList;
+import java.io.IOException;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,18 +36,7 @@
@Test
public void testRuntime() throws Exception {
testForRuntime(parameters)
- .addProgramClassFileData(
- transformer(Main.class)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
- .transform(),
- transformer(A.class).setClassDescriptor(TRANSFORMED_A_DESCRIPTOR).transform(),
- transformer(B.class)
- .setSuper(TRANSFORMED_A_DESCRIPTOR)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
- .setBridge(B.class.getDeclaredMethod("bridge"))
- .transform())
+ .addProgramClassFileData(getProgramClassFileData())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(EXPECTED_OUTPUT);
}
@@ -54,18 +44,7 @@
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClassFileData(
- transformer(Main.class)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
- .transform(),
- transformer(A.class).setClassDescriptor(TRANSFORMED_A_DESCRIPTOR).transform(),
- transformer(B.class)
- .setSuper(TRANSFORMED_A_DESCRIPTOR)
- .replaceClassDescriptorInMethodInstructions(
- descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
- .setBridge(B.class.getDeclaredMethod("bridge"))
- .transform())
+ .addProgramClassFileData(getProgramClassFileData())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
@@ -73,11 +52,22 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- // TODO(b/233866639): Should succeed with "A", "B".
- .applyIf(
- parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
- runResult -> runResult.assertSuccessWithOutputLines(EXPECTED_OUTPUT),
- runResult -> runResult.assertSuccessWithOutputLines("A", "A"));
+ .assertSuccessWithOutputLines(EXPECTED_OUTPUT);
+ }
+
+ private List<byte[]> getProgramClassFileData() throws IOException, NoSuchMethodException {
+ return ImmutableList.of(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
+ .transform(),
+ transformer(A.class).setClassDescriptor(TRANSFORMED_A_DESCRIPTOR).transform(),
+ transformer(B.class)
+ .setSuper(TRANSFORMED_A_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(A.class), TRANSFORMED_A_DESCRIPTOR)
+ .setBridge(B.class.getDeclaredMethod("bridge"))
+ .transform());
}
static class Main {
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodThatOverridesPublicMethodTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodThatOverridesPublicMethodTest.java
new file mode 100644
index 0000000..b273533
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeToPackagePrivateMethodThatOverridesPublicMethodTest.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.bridgeremoval.hoisting;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+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 BridgeToPackagePrivateMethodThatOverridesPublicMethodTest extends TestBase {
+
+ private static final String TRANSFORMED_B_DESCRIPTOR = "LB;";
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public boolean removeBridgeMethodFromA;
+
+ @Parameters(name = "{0}, remove A.bridge(): {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(Base.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Base.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ // Inspect Base.
+ ClassSubject baseClassSubject = inspector.clazz(Base.class);
+ assertThat(baseClassSubject, isPresent());
+
+ MethodSubject bridgeOnBaseMethodSubject =
+ baseClassSubject.uniqueMethodWithName("bridge");
+ assertThat(bridgeOnBaseMethodSubject, isPresent());
+
+ // Inspect A. This should have the bridge method unless it is removed by the
+ // transformation.
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject bridgeOnAMethodSubject = aClassSubject.uniqueMethodWithName("bridge");
+ assertThat(bridgeOnAMethodSubject, notIf(isPresent(), removeBridgeMethodFromA));
+
+ // Inspect B. This should not have a bridge method, as the bridge in C is not eligible
+ // for hoisting into B.
+ ClassSubject bClassSubject =
+ inspector.clazz(DescriptorUtils.descriptorToJavaType(TRANSFORMED_B_DESCRIPTOR));
+ assertThat(bClassSubject, isPresent());
+
+ MethodSubject bridgeMethodSubject = bClassSubject.uniqueMethodWithName("bridge");
+ assertThat(bridgeMethodSubject, isAbsent());
+
+ MethodSubject testMethodSubject = bClassSubject.uniqueMethodWithName("test");
+ assertThat(testMethodSubject, isPresent());
+
+ // Inspect C. The method C.bridge() is never eligible for hoisting.
+ ClassSubject cClassSubject = inspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+
+ MethodSubject bridgeOnCMethodSubject = cClassSubject.uniqueMethodWithName("bridge");
+ assertThat(bridgeOnCMethodSubject, isPresent());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutput());
+ }
+
+ private List<String> getExpectedOutput() {
+ if (removeBridgeMethodFromA) {
+ return ImmutableList.of("A.m()", "Base.bridge()", "C.m()", "C.m()");
+ }
+ return ImmutableList.of("A.m()", "A.bridge()", "C.m()", "C.m()");
+ }
+
+ private List<byte[]> getProgramClassFileData() throws IOException, NoSuchMethodException {
+ return ImmutableList.of(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(B.class), TRANSFORMED_B_DESCRIPTOR)
+ .transform(),
+ transformer(A.class)
+ .applyIf(
+ removeBridgeMethodFromA, transformer -> transformer.removeMethodsWithName("bridge"))
+ .setPublic(A.class.getDeclaredMethod("m"))
+ .transform(),
+ transformer(B.class).setClassDescriptor(TRANSFORMED_B_DESCRIPTOR).transform(),
+ transformer(C.class)
+ .setSuper(TRANSFORMED_B_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(B.class), TRANSFORMED_B_DESCRIPTOR)
+ .setBridge(C.class.getDeclaredMethod("bridge"))
+ .transform());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().callM();
+ B bAsB = System.currentTimeMillis() > 0 ? new B() : new C();
+ B cAsB = System.currentTimeMillis() > 0 ? new C() : new B();
+ Base cAsBase = System.currentTimeMillis() > 0 ? new C() : new Base();
+ bAsB.test(bAsB);
+ bAsB.test(cAsB);
+ cAsBase.bridge();
+ }
+ }
+
+ @NoVerticalClassMerging
+ public static class Base {
+
+ public void bridge() {
+ System.out.println("Base.bridge()");
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class A extends Base {
+
+ @NeverInline
+ /*public*/ void m() {
+ System.out.println("A.m()");
+ }
+
+ @Override
+ public void bridge() {
+ System.out.println("A.bridge()");
+ }
+
+ @NeverInline
+ public void callM() {
+ m();
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class /*otherpackage.*/ B extends A {
+
+ @NeverInline
+ public void test(B b) {
+ b.bridge();
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends B {
+
+ @NeverInline
+ void m() {
+ System.out.println("C.m()");
+ }
+
+ // Not eligible for bridge hoisting, as the bridge will then dispatch to A.m instead of B.m.
+ @NeverInline
+ @Override
+ public /*bridge*/ void bridge() {
+ this.m();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/SingleBridgeToPackagePrivateMethodThatOverridesPublicMethodTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/SingleBridgeToPackagePrivateMethodThatOverridesPublicMethodTest.java
new file mode 100644
index 0000000..91ddda8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/SingleBridgeToPackagePrivateMethodThatOverridesPublicMethodTest.java
@@ -0,0 +1,182 @@
+// 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.bridgeremoval.hoisting;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.util.ArrayList;
+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 SingleBridgeToPackagePrivateMethodThatOverridesPublicMethodTest extends TestBase {
+
+ private static final String TRANSFORMED_B_DESCRIPTOR = "LB;";
+
+ @Parameter(0)
+ public boolean enableBridgeHoistingFromB;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, enable bridge hoisting from B: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ assumeFalse(enableBridgeHoistingFromB);
+ testForRuntime(parameters)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ List<String> seenClassesInBridgeHoisting = new ArrayList<>();
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .applyIf(
+ !enableBridgeHoistingFromB,
+ testBuilder ->
+ testBuilder.addOptionsModification(
+ options ->
+ options.testing.isEligibleForBridgeHoisting =
+ clazz -> {
+ String classDescriptor = clazz.getType().toDescriptorString();
+ seenClassesInBridgeHoisting.add(classDescriptor);
+ if (classDescriptor.equals(TRANSFORMED_B_DESCRIPTOR)) {
+ return false;
+ }
+ return true;
+ }))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ // Inspect A.
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ MethodSubject bridgeOnAMethodSubject = aClassSubject.uniqueMethodWithName("bridge");
+ assertThat(bridgeOnAMethodSubject, onlyIf(enableBridgeHoistingFromB, isPresent()));
+
+ // Inspect B.
+ ClassSubject bClassSubject =
+ inspector.clazz(DescriptorUtils.descriptorToJavaType(TRANSFORMED_B_DESCRIPTOR));
+ assertThat(bClassSubject, isPresent());
+
+ MethodSubject bridgeOnBMethodSubject = bClassSubject.uniqueMethodWithName("bridge");
+ assertThat(bridgeOnBMethodSubject, notIf(isPresent(), enableBridgeHoistingFromB));
+
+ // Inspect C.
+ ClassSubject cClassSubject = inspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+
+ MethodSubject bridgeOnCMethodSubject = cClassSubject.uniqueMethodWithName("bridge");
+ assertThat(bridgeOnCMethodSubject, isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(getExpectedOutput());
+
+ // Verify that the there was the expected calls to isEligibleForBridgeHoisting().
+ if (!enableBridgeHoistingFromB) {
+ assertEquals(
+ Lists.newArrayList(descriptor(C.class), TRANSFORMED_B_DESCRIPTOR),
+ seenClassesInBridgeHoisting);
+ }
+ }
+
+ private List<String> getExpectedOutput() {
+ return ImmutableList.of("A.m()", "C.m()", "C.m()");
+ }
+
+ private List<byte[]> getProgramClassFileData() throws IOException, NoSuchMethodException {
+ return ImmutableList.of(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(B.class), TRANSFORMED_B_DESCRIPTOR)
+ .transform(),
+ transformer(A.class).setPublic(A.class.getDeclaredMethod("m")).transform(),
+ transformer(B.class).setClassDescriptor(TRANSFORMED_B_DESCRIPTOR).transform(),
+ transformer(C.class)
+ .setSuper(TRANSFORMED_B_DESCRIPTOR)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(B.class), TRANSFORMED_B_DESCRIPTOR)
+ .setBridge(C.class.getDeclaredMethod("bridge"))
+ .transform());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A().callM();
+ new C().callM();
+ new C().bridge();
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class A {
+
+ @NeverInline
+ /*public*/ void m() {
+ System.out.println("A.m()");
+ }
+
+ @NeverInline
+ public void callM() {
+ m();
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class /*otherpackage.*/ B extends A {}
+
+ @NeverClassInline
+ public static class C extends B {
+
+ @NeverInline
+ void m() {
+ System.out.println("C.m()");
+ }
+
+ @NeverInline
+ public /*bridge*/ void bridge() {
+ this.m();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java b/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java
index 7453e9b..10a6837 100644
--- a/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java
+++ b/src/test/java/com/android/tools/r8/cf/methodhandles/invokespecial/InvokeSpecialMethodHandleTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.errors.UnsupportedFeatureDiagnostic;
import com.android.tools.r8.transformers.ClassTransformer;
import com.android.tools.r8.utils.StringUtils;
@@ -75,6 +76,7 @@
.addProgramClasses(C.class, Main.class)
.addProgramClassFileData(getTransformedD())
.setMinApi(parameters.getApiLevel())
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
@@ -89,6 +91,7 @@
.addProgramClassFileData(getTransformedD())
.setMinApi(parameters.getApiLevel())
.allowDiagnosticMessages()
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.run(parameters.getRuntime(), Main.class)
.apply(this::checkResult);
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
new file mode 100644
index 0000000..ed2130d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InitClassToPackagePrivateFieldWithCrossPackageMergingTest.java
@@ -0,0 +1,111 @@
+// 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+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 InitClassToPackagePrivateFieldWithCrossPackageMergingTest extends TestBase {
+
+ private static final String NEW_B_DESCRIPTOR = "LB;";
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testRuntime() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(A.class, A2.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, A2.class)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertMergedInto(
+ Reference.classFromClass(A.class),
+ Reference.classFromDescriptor(NEW_B_DESCRIPTOR)))
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+
+ private List<byte[]> getProgramClassFileData() throws Exception {
+ return ImmutableList.of(
+ transformer(Main.class)
+ .replaceClassDescriptorInMethodInstructions(descriptor(B.class), NEW_B_DESCRIPTOR)
+ .transform(),
+ transformer(B.class).setClassDescriptor(NEW_B_DESCRIPTOR).transform());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ A.foo();
+ if (A2.f != 0) {
+ B.bar();
+ }
+ }
+ }
+
+ public static class A {
+
+ @NeverInline
+ public static void foo() {
+ // Will be optimized into reading A2.greeting after inlining, since the body of sayHello() is
+ // empty and A2.greeting will be used to trigger the class initializer of A2.
+ A2.sayHello();
+ }
+ }
+
+ @NoHorizontalClassMerging
+ public static class A2 {
+
+ // Will remain due to the use in Main.main.
+ static int f = (int) System.currentTimeMillis();
+
+ static {
+ System.out.print("Hello");
+ }
+
+ // Will be inlined.
+ static void sayHello() {}
+ }
+
+ public static class /* default package */ B {
+
+ @NeverInline
+ public static void bar() {
+ System.out.println(", world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index ae53238..0ecf5e3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -334,6 +334,7 @@
testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
.allowUnusedProguardConfigurationRules()
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::disableOutlining),
main,
programFiles,
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 092d2ad..73dbba2 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.compilerapi.androidplatformbuild.AndroidPlatformBuildApiTest;
+import com.android.tools.r8.compilerapi.artprofiles.ArtProfilesForRewritingApiTest;
import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
import com.android.tools.r8.compilerapi.classconflictresolver.ClassConflictResolverTest;
import com.android.tools.r8.compilerapi.desugardependencies.DesugarDependenciesTest;
@@ -53,7 +54,9 @@
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
ImmutableList.of(
- StartupProfileApiTest.ApiTest.class, ClassConflictResolverTest.ApiTest.class);
+ ArtProfilesForRewritingApiTest.ApiTest.class,
+ StartupProfileApiTest.ApiTest.class,
+ ClassConflictResolverTest.ApiTest.class);
private final TemporaryFolder temp;
diff --git a/src/test/java/com/android/tools/r8/compilerapi/artprofiles/ArtProfilesForRewritingApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/artprofiles/ArtProfilesForRewritingApiTest.java
new file mode 100644
index 0000000..cd7343c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/artprofiles/ArtProfilesForRewritingApiTest.java
@@ -0,0 +1,337 @@
+// 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.artprofiles;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TextInputStream;
+import com.android.tools.r8.TextOutputStream;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.compilerapi.artprofiles.ArtProfilesForRewritingApiTest.ApiTest.ArtProfileConsumerForTesting;
+import com.android.tools.r8.compilerapi.mockdata.MockClass;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileBuilder;
+import com.android.tools.r8.profile.art.ArtProfileClassRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
+import com.android.tools.r8.profile.art.ArtProfileRuleConsumer;
+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.ThrowingBiConsumer;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+
+public class ArtProfilesForRewritingApiTest extends CompilerApiTestRunner {
+
+ private static final int SOME_API_LEVEL = 24;
+
+ public ArtProfilesForRewritingApiTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(test::runD8);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(test::runR8);
+ }
+
+ private void runTest(
+ ThrowingBiConsumer<ArtProfileConsumerForTesting, ArtProfileConsumerForTesting, Exception>
+ testRunner)
+ throws Exception {
+ ArtProfileConsumerForTesting apiArtProfileConsumer = new ArtProfileConsumerForTesting();
+ ArtProfileConsumerForTesting textualArtProfileConsumer = new ArtProfileConsumerForTesting();
+ testRunner.accept(apiArtProfileConsumer, textualArtProfileConsumer);
+ for (ArtProfileConsumerForTesting artProfileConsumer :
+ new ArtProfileConsumerForTesting[] {apiArtProfileConsumer, textualArtProfileConsumer}) {
+ assertTrue(artProfileConsumer.isFinished());
+ assertTrue(artProfileConsumer.isOutputStreamClosed());
+ assertEquals(ApiTest.textualArtProfileLines, artProfileConsumer.getResidualArtProfileRules());
+ assertEquals(
+ ApiTest.textualArtProfileLines,
+ artProfileConsumer.getResidualArtProfileRulesFromOutputStream());
+ }
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ static ClassReference mockClassReference = Reference.classFromClass(MockClass.class);
+ static MethodReference mockInitMethodReference =
+ Reference.method(mockClassReference, "<init>", Collections.emptyList(), null);
+ static MethodReference mockMainMethodReference =
+ Reference.method(
+ mockClassReference,
+ "main",
+ Collections.singletonList(Reference.classFromClass(String[].class)),
+ null);
+ static List<String> textualArtProfileLines =
+ Arrays.asList(
+ "Lcom/android/tools/r8/compilerapi/mockdata/MockClass;",
+ "PLcom/android/tools/r8/compilerapi/mockdata/MockClass;-><init>()V",
+ "HSPLcom/android/tools/r8/compilerapi/mockdata/MockClass;->main([Ljava/lang/String;)V");
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ public void runD8(
+ ArtProfileConsumerForTesting apiArtProfileConsumer,
+ ArtProfileConsumerForTesting textualArtProfileConsumer)
+ throws Exception {
+ ApiArtProfileProviderForTesting apiArtProfileProvider = new ApiArtProfileProviderForTesting();
+ TextualArtProfileProviderForTesting textualArtProfileProvider =
+ new TextualArtProfileProviderForTesting();
+ D8Command.Builder commandBuilder =
+ D8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setMinApiLevel(SOME_API_LEVEL)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addArtProfileForRewriting(apiArtProfileProvider, apiArtProfileConsumer)
+ .addArtProfileForRewriting(textualArtProfileProvider, textualArtProfileConsumer);
+ D8.run(commandBuilder.build());
+ assertTrue(textualArtProfileProvider.inputStream.isClosed());
+ }
+
+ public void runR8(
+ ArtProfileConsumerForTesting apiArtProfileConsumer,
+ ArtProfileConsumerForTesting textualArtProfileConsumer)
+ throws Exception {
+ ApiArtProfileProviderForTesting apiArtProfileProvider = new ApiArtProfileProviderForTesting();
+ TextualArtProfileProviderForTesting textualArtProfileProvider =
+ new TextualArtProfileProviderForTesting();
+ R8Command.Builder commandBuilder =
+ R8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addProguardConfiguration(
+ Collections.singletonList("-keep class * { *; }"), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setMinApiLevel(SOME_API_LEVEL)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .addArtProfileForRewriting(apiArtProfileProvider, apiArtProfileConsumer)
+ .addArtProfileForRewriting(textualArtProfileProvider, textualArtProfileConsumer);
+ R8.run(commandBuilder.build());
+ assertTrue(textualArtProfileProvider.inputStream.isClosed());
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ runD8(null, null);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runR8(null, null);
+ }
+
+ static class ApiArtProfileProviderForTesting implements ArtProfileProvider {
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ profileBuilder
+ .addClassRule(
+ classRuleBuilder -> classRuleBuilder.setClassReference(mockClassReference))
+ .addMethodRule(
+ methodRuleBuilder ->
+ methodRuleBuilder
+ .setMethodReference(mockInitMethodReference)
+ .setMethodRuleInfo(
+ methodRuleInfoBuilder -> methodRuleInfoBuilder.setIsHot(true))
+ .setMethodRuleInfo(
+ methodRuleInfoBuilder -> methodRuleInfoBuilder.setIsPostStartup(true)))
+ .addMethodRule(
+ methodRuleBuilder ->
+ methodRuleBuilder
+ .setMethodReference(mockMainMethodReference)
+ .setMethodRuleInfo(
+ methodRuleInfoBuilder ->
+ methodRuleInfoBuilder
+ .setIsHot(true)
+ .setIsStartup(true)
+ .setIsPostStartup(true)));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ }
+
+ static class TextualArtProfileProviderForTesting implements ArtProfileProvider {
+
+ final ClosableByteArrayInputStream inputStream =
+ new ClosableByteArrayInputStream(
+ String.join("\n", textualArtProfileLines).concat("\n").getBytes());
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ profileBuilder.addHumanReadableArtProfile(
+ new TextInputStream() {
+ @Override
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+
+ @Override
+ public Charset getCharset() {
+ return StandardCharsets.UTF_8;
+ }
+ },
+ parserBuilderConsumer -> {});
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ }
+
+ static class ArtProfileConsumerForTesting implements ArtProfileConsumer {
+
+ private boolean finished;
+ private final List<String> residualArtProfileRules = new ArrayList<>();
+ private final ClosableByteArrayOutputStream outputStream =
+ new ClosableByteArrayOutputStream();
+
+ @Override
+ public TextOutputStream getHumanReadableArtProfileConsumer() {
+ return new TextOutputStream() {
+
+ @Override
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ @Override
+ public Charset getCharset() {
+ return StandardCharsets.UTF_8;
+ }
+ };
+ }
+
+ @Override
+ public ArtProfileRuleConsumer getRuleConsumer() {
+ return new ArtProfileRuleConsumer() {
+
+ @Override
+ public void acceptClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ residualArtProfileRules.add(classReference.getDescriptor());
+ }
+
+ @Override
+ public void acceptMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ StringBuilder builder = new StringBuilder();
+ if (methodRuleInfo.isHot()) {
+ builder.append('H');
+ }
+ if (methodRuleInfo.isStartup()) {
+ builder.append('S');
+ }
+ if (methodRuleInfo.isPostStartup()) {
+ builder.append('P');
+ }
+ residualArtProfileRules.add(
+ builder
+ .append(methodReference.getHolderClass().getDescriptor())
+ .append("->")
+ .append(methodReference.getMethodName())
+ .append(methodReference.getMethodDescriptor())
+ .toString());
+ }
+ };
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ finished = true;
+ }
+
+ List<String> getResidualArtProfileRulesFromOutputStream()
+ throws UnsupportedEncodingException {
+ return Arrays.asList(outputStream.toString(StandardCharsets.UTF_8.name()).split("\n"));
+ }
+
+ List<String> getResidualArtProfileRules() {
+ return residualArtProfileRules;
+ }
+
+ boolean isFinished() {
+ return finished;
+ }
+
+ boolean isOutputStreamClosed() {
+ return outputStream.isClosed();
+ }
+ }
+
+ private static class ClosableByteArrayInputStream extends ByteArrayInputStream {
+
+ private boolean closed;
+
+ public ClosableByteArrayInputStream(byte[] buf) {
+ super(buf);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ closed = true;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+ }
+
+ private static class ClosableByteArrayOutputStream extends ByteArrayOutputStream {
+
+ private boolean closed;
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ closed = true;
+ }
+
+ public boolean isClosed() {
+ return closed;
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java
index e8fa240..0a6acf6 100644
--- a/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/CfDebugTestConfig.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.file.Path;
@@ -15,6 +17,8 @@
public static final Path JDWP_JAR = ToolHelper.getJdwpTestsCfJarPath(AndroidApiLevel.N);
+ private final CfRuntime runtime;
+
public CfDebugTestConfig() {
this(Collections.emptyList());
}
@@ -23,13 +27,19 @@
this(Arrays.asList(paths));
}
+ @Deprecated
public CfDebugTestConfig(List<Path> paths) {
+ this(TestRuntime.getDefaultCfRuntime(), paths);
+ }
+
+ public CfDebugTestConfig(CfRuntime runtime, List<Path> paths) {
+ this.runtime = runtime;
addPaths(JDWP_JAR);
addPaths(paths);
}
@Override
- public final RuntimeKind getRuntimeKind() {
- return RuntimeKind.CF;
+ public final CfRuntime getRuntime() {
+ return runtime;
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/ClassInitializationTest.java b/src/test/java/com/android/tools/r8/debug/ClassInitializationTest.java
deleted file mode 100644
index 8931c9d..0000000
--- a/src/test/java/com/android/tools/r8/debug/ClassInitializationTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2017, 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.debug;
-
-import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class ClassInitializationTest extends DebugTestBase {
-
- private static DebugTestConfig config;
-
- @BeforeClass
- public static void setup() {
- config = new D8DebugTestResourcesConfig(temp);
- }
-
- @Test
- public void testStaticAssingmentInitialization() throws Throwable {
- final String SOURCE_FILE = "ClassInitializerAssignmentInitialization.java";
- final String CLASS = "ClassInitializerAssignmentInitialization";
-
- runDebugTest(
- config,
- CLASS,
- breakpoint(CLASS, "<clinit>"),
- run(),
- checkLine(SOURCE_FILE, 7),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(0)),
- checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
- checkStaticFieldClinitSafe(CLASS, "z", null, Value.createInt(0)),
- stepOver(),
- checkLine(SOURCE_FILE, 10),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(1)),
- checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
- checkStaticFieldClinitSafe(CLASS, "z", null, Value.createInt(0)),
- breakpoint(CLASS, "main"),
- run(),
- checkStaticField(CLASS, "x", null, Value.createInt(1)),
- checkStaticField(CLASS, "y", null, Value.createInt(0)),
- checkStaticField(CLASS, "z", null, Value.createInt(2)),
- run());
- }
-
- @Test
- public void testBreakpointInEmptyClassInitializer() throws Throwable {
- final String SOURCE_FILE = "ClassInitializerEmpty.java";
- final String CLASS = "ClassInitializerEmpty";
-
- runDebugTest(
- config,
- CLASS,
- breakpoint(CLASS, "<clinit>"),
- run(),
- checkLine(SOURCE_FILE, 8),
- run());
- }
-
- @Test
- public void testStaticBlockInitialization() throws Throwable {
- final String SOURCE_FILE = "ClassInitializerStaticBlockInitialization.java";
- final String CLASS = "ClassInitializerStaticBlockInitialization";
-
- runDebugTest(
- config,
- CLASS,
- breakpoint(CLASS, "<clinit>"),
- run(),
- checkLine(SOURCE_FILE, 12),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(0)),
- stepOver(),
- checkLine(SOURCE_FILE, 13),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(1)),
- stepOver(),
- checkLine(SOURCE_FILE, 14),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
- stepOver(),
- checkLine(SOURCE_FILE, 17),
- stepOver(),
- checkLine(SOURCE_FILE, 19),
- breakpoint(CLASS, "main"),
- run(),
- checkLine(SOURCE_FILE, 23),
- checkStaticField(CLASS, "x", null, Value.createInt(3)),
- run());
- }
-
- @Test
- public void testStaticMixedInitialization() throws Throwable {
- final String SOURCE_FILE = "ClassInitializerMixedInitialization.java";
- final String CLASS = "ClassInitializerMixedInitialization";
-
- runDebugTest(
- config,
- CLASS,
- breakpoint(CLASS, "<clinit>"),
- run(),
- checkLine(SOURCE_FILE, 8),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(0)),
- checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
- stepOver(),
- checkLine(SOURCE_FILE, 12),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(1)),
- checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
- stepOver(),
- checkLine(SOURCE_FILE, 13),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
- checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
- stepOver(),
- checkLine(SOURCE_FILE, 16),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
- checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
- stepOver(),
- checkLine(SOURCE_FILE, 18),
- checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
- checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(2)),
- breakpoint(CLASS, "main"),
- run(),
- checkLine(SOURCE_FILE, 22),
- checkStaticField(CLASS, "x", null, Value.createInt(3)),
- checkStaticField(CLASS, "y", null, Value.createInt(2)),
- run());
- }
-}
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
index c3e45b6..2a8e7c0 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousKotlinSteppingTest.java
@@ -36,14 +36,16 @@
@Test
public void testContinuousSingleStepKotlinApp() throws Throwable {
KotlinDebugD8Config d8Config =
- KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
+ KotlinDebugD8Config.build(
+ kotlinParameters, parameters.getApiLevel(), parameters.getRuntime().asDex());
runContinuousTest("KotlinApp", d8Config, MAIN_METHOD_NAME);
}
@Test
public void testContinuousSingleStepKotlinInline() throws Throwable {
KotlinDebugD8Config d8Config =
- KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
+ KotlinDebugD8Config.build(
+ kotlinParameters, parameters.getApiLevel(), parameters.getRuntime().asDex());
runContinuousTest("KotlinInline", d8Config, MAIN_METHOD_NAME);
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
index ef2205a..013d166 100644
--- a/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/D8DebugTestConfig.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -13,6 +14,7 @@
import com.android.tools.r8.utils.ListUtils;
import java.nio.file.Path;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.junit.rules.TemporaryFolder;
@@ -20,6 +22,13 @@
/** Test configuration with utilities for compiling with D8 and adding results to the classpath. */
public class D8DebugTestConfig extends DexDebugTestConfig {
+ @Deprecated
+ public D8DebugTestConfig() {}
+
+ public D8DebugTestConfig(DexRuntime runtime) {
+ super(runtime, Collections.emptyList());
+ }
+
// Use the option with api-level below.
@Deprecated()
public static AndroidApp d8Compile(List<Path> paths, Consumer<InternalOptions> optionsConsumer) {
diff --git a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
index 04477bd..426da42 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugStreamComparator.java
@@ -7,9 +7,9 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
-import com.android.tools.r8.debug.DebugTestConfig.RuntimeKind;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringUtils;
@@ -289,10 +289,10 @@
String sig = reference.getMethodSignature();
List<Variable> variables = reference.getVisibleVariables();
int frameDepth = reference.getFrameDepth();
- RuntimeKind referenceRuntime = reference.getConfig().getRuntimeKind();
+ TestRuntime referenceRuntime = reference.getConfig().getRuntime();
for (int i = 1; i < states.size(); i++) {
DebuggeeState state = states.get(i);
- RuntimeKind stateRuntime = state.getConfig().getRuntimeKind();
+ TestRuntime stateRuntime = state.getConfig().getRuntime();
if (verifyFiles) {
assertEquals("source file mismatch", file, state.getSourceFile());
}
@@ -328,12 +328,12 @@
}
}
- private static boolean shouldIgnoreVariable(Variable variable, RuntimeKind runtime) {
- return runtime == RuntimeKind.DEX && variable.getName().isEmpty();
+ private static boolean shouldIgnoreVariable(Variable variable, TestRuntime runtime) {
+ return runtime.isDex() && variable.getName().isEmpty();
}
private static void verifyVariablesEqual(
- RuntimeKind xRuntime, List<Variable> xs, RuntimeKind yRuntime, List<Variable> ys) {
+ TestRuntime xRuntime, List<Variable> xs, TestRuntime yRuntime, List<Variable> ys) {
Map<String, Variable> map = new HashMap<>(xs.size());
for (Variable x : xs) {
if (!shouldIgnoreVariable(x, xRuntime)) {
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 59d408f..c0f7ea3 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.debug;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
-import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command.NopCommand;
import com.android.tools.r8.errors.Unreachable;
@@ -189,20 +189,18 @@
protected DebugTestRunner getDebugTestRunner(
DebugTestConfig config, String debuggeeClass, List<JUnit3Wrapper.Command> commands)
throws Throwable {
- // TODO(b/199700280): Reenable on 12.0.0 when we have the libjdwp.so file include and the flags
- // fixed.
Assume.assumeTrue(
- "Skipping test " + testName.getMethodName() + " because debugging not enabled in 12.0.0",
- !ToolHelper.getDexVm().isEqualTo(DexVm.ART_12_0_0_HOST));
- // Skip test due to unsupported runtime.
- Assume.assumeTrue("Skipping test " + testName.getMethodName() + " because ART is not supported",
- ToolHelper.artSupported());
- Assume.assumeTrue("Skipping test " + testName.getMethodName()
+ "Skipping test "
+ + testName.getMethodName()
+ " because debug tests are not yet supported on Windows",
!ToolHelper.isWindows());
- Assume.assumeTrue("Skipping test " + testName.getMethodName()
- + " because debug tests are not yet supported on device",
- ToolHelper.getDexVm().getKind() == ToolHelper.DexVm.Kind.HOST);
+
+ // TODO(b/199700280): Reenable on 12.0.0 when we have the libjdwp.so file include and the flags
+ // fixed.
+ Assume.assumeTrue(
+ "Skipping test " + testName.getMethodName() + " because debugging not enabled in 12.0.0",
+ config.getRuntime().isCf()
+ || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V12_0_0));
ClassNameMapper classNameMapper =
config.getProguardMap() == null
@@ -545,14 +543,7 @@
protected final JUnit3Wrapper.Command checkStaticFieldClinitSafe(
String className, String fieldName, String fieldSignature, Value expectedValue) {
- return inspect(t -> {
- // TODO(65148874): The current Art from AOSP master hangs when requesting static fields
- // when breaking in <clinit>. Last known good version is 7.0.0.
- Assume.assumeTrue(
- "Skipping test " + testName.getMethodName() + " because ART version is not supported",
- t.isCfRuntime() || ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V7_0_0));
- checkStaticField(className, fieldName, fieldSignature, expectedValue);
- });
+ return inspect(t -> checkStaticField(className, fieldName, fieldSignature, expectedValue));
}
protected final JUnit3Wrapper.Command checkStaticField(
@@ -945,18 +936,27 @@
try {
command.perform(this);
} catch (TestErrorException e) {
- boolean ignoreException = false;
- if (config.isDexRuntime()
- && ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)) {
- // Dalvik has flaky synchronization issue on shutdown. The workaround is to ignore
- // the exception if and only if we know that it's the final resume command.
- if (debuggeeState == null && commandsQueue.isEmpty()) {
- // We should receive the VMDeath event and transition to the Exit state here.
- processEvents();
- assert state == State.Exit;
- ignoreException = true;
- }
- }
+ boolean ignoreException =
+ config
+ .getRuntime()
+ .match(
+ cfRuntime -> false,
+ (dexRuntime, version) -> {
+ if (version.isOlderThanOrEqual(Version.V4_4_4)) {
+ // Dalvik has flaky synchronization issue on shutdown. The workaround
+ // is to ignore
+ // the exception if and only if we know that it's the final resume
+ // command.
+ if (debuggeeState == null && commandsQueue.isEmpty()) {
+ // We should receive the VMDeath event and transition to the Exit
+ // state here.
+ processEvents();
+ assert state == State.Exit;
+ return true;
+ }
+ }
+ return false;
+ });
if (!ignoreException) {
throw e;
}
@@ -1072,20 +1072,34 @@
ArtTestOptions(String[] debuggeePath) {
// Set debuggee command-line.
- if (config.isDexRuntime()) {
- ArtCommandBuilder artCommandBuilder = new ArtCommandBuilder(ToolHelper.getDexVm());
- if (ToolHelper.getDexVm().getVersion().isNewerThan(DexVm.Version.V5_1_1)) {
- artCommandBuilder.appendArtOption("-Xcompiler-option");
- artCommandBuilder.appendArtOption("--debuggable");
- }
- if (ToolHelper.getDexVm().getVersion().isNewerThanOrEqual(DexVm.Version.V9_0_0)) {
- artCommandBuilder.appendArtOption("-XjdwpProvider:internal");
- }
- if (DEBUG_TESTS && ToolHelper.getDexVm().getVersion().isNewerThan(Version.V4_4_4)) {
- artCommandBuilder.appendArtOption("-verbose:jdwp");
- }
- setProperty("jpda.settings.debuggeeJavaPath", artCommandBuilder.build());
- }
+ config
+ .getRuntime()
+ .match(
+ cfRuntime -> {
+ setProperty(
+ "jpda.settings.debuggeeJavaPath", cfRuntime.getJavaExecutable().toString());
+ },
+ (dexRuntime, version) -> {
+ ArtCommandBuilder artCommandBuilder = new ArtCommandBuilder(dexRuntime.getVm());
+ if (version.isNewerThan(Version.V5_1_1)) {
+ artCommandBuilder.appendArtOption("-Xcompiler-option");
+ artCommandBuilder.appendArtOption("--debuggable");
+ }
+ if (version.isNewerThanOrEqual(Version.V13_0_0)) {
+ // TODO(b/199700280): These options may be the same for V12 once the libs are
+ // added.
+ artCommandBuilder.appendArtOption("-Xplugin:libopenjdkjvmti.so");
+ setProperty("jpda.settings.debuggeeAgentArgument", "-agentpath:");
+ setProperty("jpda.settings.debuggeeAgentName", "libjdwp.so");
+ } else if (version.isNewerThanOrEqual(Version.V9_0_0)) {
+ artCommandBuilder.appendArtOption("-XjdwpProvider:internal");
+ }
+ if (DEBUG_TESTS && version.isNewerThan(Version.V4_4_4)) {
+ artCommandBuilder.appendArtOption("-verbose:jdwp");
+ }
+ String build = artCommandBuilder.build();
+ setProperty("jpda.settings.debuggeeJavaPath", build);
+ });
// Set debuggee classpath
String debuggeeClassPath = String.join(File.pathSeparator, debuggeePath);
@@ -1442,6 +1456,10 @@
return config;
}
+ public TestRuntime getRuntime() {
+ return config.getRuntime();
+ }
+
public boolean isCfRuntime() {
return getConfig().isCfRuntime();
}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java
index 4ab22aa..83e7c3f 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java
@@ -5,6 +5,8 @@
import static com.android.tools.r8.naming.ClassNameMapper.MissingFileAction.MISSING_FILE_IS_ERROR;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.ClassNameMapper;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -20,6 +22,16 @@
DEX
}
+ public static DebugTestConfig create(TestRuntime runtime, Path... paths) {
+ if (runtime.isCf()) {
+ return new CfDebugTestConfig(runtime.asCf(), Arrays.asList(paths));
+ }
+ if (runtime.isDex()) {
+ return new DexDebugTestConfig(runtime.asDex(), Arrays.asList(paths));
+ }
+ throw new Unreachable();
+ }
+
private boolean mustProcessAllCommands = true;
private List<Path> paths = new ArrayList<>();
@@ -27,15 +39,15 @@
private ClassNameMapper.MissingFileAction missingProguardMapAction;
private boolean usePcForMissingLineNumberTable = false;
- /** The expected runtime kind for the debuggee. */
- public abstract RuntimeKind getRuntimeKind();
+ /** The runtime to use for the debuggee. */
+ public abstract TestRuntime getRuntime();
public boolean isCfRuntime() {
- return getRuntimeKind() == RuntimeKind.CF;
+ return getRuntime().isCf();
}
public boolean isDexRuntime() {
- return getRuntimeKind() == RuntimeKind.DEX;
+ return getRuntime().isDex();
}
public void allowUsingPcForMissingLineNumberTable() {
@@ -95,7 +107,7 @@
new StringBuilder()
.append("DebugTestConfig{")
.append("runtime:")
- .append(getRuntimeKind())
+ .append(getRuntime())
.append(", classpath:[")
.append(
String.join(", ", paths.stream().map(Path::toString).collect(Collectors.toList())))
diff --git a/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java
index babf170..c74c84a 100644
--- a/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java
+++ b/src/test/java/com/android/tools/r8/debug/DexDebugTestConfig.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.ToolHelper;
import java.nio.file.Path;
import java.util.Arrays;
@@ -12,24 +14,31 @@
/** Base test configuration with DEX version of JDWP. */
public class DexDebugTestConfig extends DebugTestConfig {
- public static final Path JDWP_DEX_JAR =
- ToolHelper.getJdwpTestsDexJarPath(ToolHelper.getMinApiLevelForDexVm());
+ private final DexRuntime runtime;
+ @Deprecated
public DexDebugTestConfig() {
this(Collections.emptyList());
}
+ @Deprecated
public DexDebugTestConfig(Path... paths) {
this(Arrays.asList(paths));
}
+ @Deprecated
public DexDebugTestConfig(List<Path> paths) {
- addPaths(JDWP_DEX_JAR);
+ this(new DexRuntime(ToolHelper.getDexVm()), paths);
+ }
+
+ public DexDebugTestConfig(TestRuntime.DexRuntime runtime, List<Path> paths) {
+ this.runtime = runtime;
+ addPaths(ToolHelper.getJdwpTestsDexJarPath(runtime.maxSupportedApiLevel()));
addPaths(paths);
}
@Override
- public final RuntimeKind getRuntimeKind() {
- return RuntimeKind.DEX;
+ public TestRuntime getRuntime() {
+ return runtime;
}
}
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index 927b5ba..e603d08 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -7,7 +7,9 @@
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix;
import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getDefaultMethodPrefix;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
import java.nio.file.Path;
@@ -42,6 +44,10 @@
@Test
public void testDefaultMethod() throws Throwable {
+ // TODO(b/244683447): This test fails on ART 13 when checking current method in doSomething.
+ assumeTrue(
+ config.getRuntime().isCf()
+ || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V13_0_0));
String debuggeeClass = "DebugInterfaceMethod";
String parameterName = "msg";
String localVariableName = "name";
diff --git a/src/test/java/com/android/tools/r8/debug/JvmSyntheticTestRunner.java b/src/test/java/com/android/tools/r8/debug/JvmSyntheticTestRunner.java
index e3f0660..0600584 100644
--- a/src/test/java/com/android/tools/r8/debug/JvmSyntheticTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/JvmSyntheticTestRunner.java
@@ -3,13 +3,10 @@
import static com.android.tools.r8.references.Reference.methodFromMethod;
import static org.hamcrest.CoreMatchers.containsString;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.debug.JvmSyntheticTest.A;
import com.android.tools.r8.debug.JvmSyntheticTest.Runner;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -32,17 +29,17 @@
}
@Test
- public void testStacktrace() throws ExecutionException, CompilationFailedException, IOException {
+ public void testStacktrace() throws Throwable {
testForJvm()
.addProgramClasses(A.class, CLASS)
.run(parameters.getRuntime(), CLASS)
.assertSuccessWithOutputThatMatches(
- containsString("at com.android.tools.r8.debug.JvmSyntheticTest$A.access$000"));
+ containsString("at com.android.tools.r8.debug.JvmSyntheticTest$A.access$000"))
+ .debugger(this::testDebug)
+ .debugger(this::testDebugIntelliJ);
}
- @Test
- public void testDebug() throws Throwable {
- DebugTestConfig debugTestConfig = testForJvm().addProgramClasses(A.class, CLASS).debugConfig();
+ public void testDebug(DebugTestConfig debugTestConfig) throws Throwable {
runDebugTest(
debugTestConfig,
CLASS,
@@ -54,9 +51,7 @@
run());
}
- @Test
- public void testDebugIntelliJ() throws Throwable {
- DebugTestConfig debugTestConfig = testForJvm().addProgramClasses(A.class, CLASS).debugConfig();
+ public void testDebugIntelliJ(DebugTestConfig debugTestConfig) throws Throwable {
runDebugTest(
debugTestConfig,
CLASS,
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinDebugD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinDebugD8Config.java
index 348d43a..4166711 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinDebugD8Config.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinDebugD8Config.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestRuntime.DexRuntime;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.IOException;
import java.nio.file.Path;
@@ -22,6 +23,10 @@
/** Shared test configuration for D8 compiled resources from the "kotlinR8TestResources/debug". */
class KotlinDebugD8Config extends D8DebugTestConfig {
+ public KotlinDebugD8Config(DexRuntime runtime) {
+ super(runtime);
+ }
+
static final KotlinCompileMemoizer compiledKotlinJars =
getCompileMemoizer(KotlinTestBase.getKotlinFilesInResource("debug"))
.configure(KotlinCompilerTool::includeRuntime);
@@ -40,9 +45,9 @@
}
public static KotlinDebugD8Config build(
- KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel) {
+ KotlinTestParameters kotlinTestParameters, AndroidApiLevel apiLevel, DexRuntime runtime) {
try {
- KotlinDebugD8Config kotlinDebugD8Config = new KotlinDebugD8Config();
+ KotlinDebugD8Config kotlinDebugD8Config = new KotlinDebugD8Config(runtime);
kotlinDebugD8Config.addPaths(compiledResourcesMemoized.apply(kotlinTestParameters, apiLevel));
return kotlinDebugD8Config;
} catch (Throwable e) {
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index 419a379..901e412 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -5,10 +5,12 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -41,7 +43,8 @@
}
protected KotlinDebugD8Config getD8Config() {
- return KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
+ return KotlinDebugD8Config.build(
+ kotlinParameters, parameters.getApiLevel(), parameters.getRuntime().asDex());
}
@Test
@@ -254,6 +257,9 @@
@Test
public void testNestedInlining() throws Throwable {
+ assumeTrue(
+ "b/244704042: Incorrect step-into StringBuilder.",
+ parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0));
// Count the number of lines in the source file. This is needed to check that inlined code
// refers to non-existing line numbers.
Path sourceFilePath =
@@ -336,7 +342,7 @@
checkLocals(left_mangledLvName, right_mangledLvName),
// Enter "foo"
stepInto(),
- // TODO(b/207743106): Remove when resolved.
+ // See b/207743106 for incorrect debug info on Kotlin 1.6.
applyIf(
kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0,
this::stepInto),
@@ -395,7 +401,7 @@
checkNoLocal(inlinee2_lambda2_inlineScope),
// Enter the call to "foo"
stepInto(),
- // TODO(b/207743106): Remove when resolved.
+ // See b/207743106 for incorrect debug info on Kotlin 1.6.
applyIf(
kotlinParameters.getCompilerVersion() == KotlinCompilerVersion.KOTLINC_1_6_0,
this::stepInto),
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index 4c9f8c0..a7ae2b0 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -33,7 +33,8 @@
}
protected KotlinDebugD8Config getD8Config() {
- return KotlinDebugD8Config.build(kotlinParameters, parameters.getApiLevel());
+ return KotlinDebugD8Config.build(
+ kotlinParameters, parameters.getApiLevel(), parameters.getRuntime().asDex());
}
// TODO(shertz) simplify test
diff --git a/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
index 0e77b85..1635252 100644
--- a/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LambdaOuterContextTestRunner.java
@@ -4,46 +4,67 @@
package com.android.tools.r8.debug;
import static com.android.tools.r8.references.Reference.methodFromMethod;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.D8TestCompileResult;
-import com.android.tools.r8.JvmTestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.debug.LambdaOuterContextTest.Converter;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
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 LambdaOuterContextTestRunner extends DebugTestBase {
public static final Class<?> CLASS = LambdaOuterContextTest.class;
public static final String EXPECTED = StringUtils.lines("84");
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ private final TestParameters parameters;
+
+ public LambdaOuterContextTestRunner(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void testJvm() throws Throwable {
- JvmTestBuilder jvmTestBuilder = testForJvm().addTestClasspath();
- jvmTestBuilder.run(CLASS).assertSuccessWithOutput(EXPECTED);
- runDebugger(jvmTestBuilder.debugConfig());
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClassesAndInnerClasses(CLASS)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(this::runDebugger);
}
@Test
public void testD8() throws Throwable {
- D8TestCompileResult compileResult =
- testForD8().addProgramClassesAndInnerClasses(CLASS).compile();
- compileResult.run(CLASS).assertSuccessWithOutput(EXPECTED);
- runDebugger(compileResult.debugConfig());
+ testForD8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(CLASS)
+ .setMinApi(AndroidApiLevel.B)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(this::runDebugger);
}
@Test
- public void testR8Cf() throws Throwable {
- R8TestCompileResult compileResult =
- testForR8(Backend.CF)
- .addProgramClassesAndInnerClasses(CLASS)
- .debug()
- .addDontObfuscate()
- .noTreeShaking()
- .compile();
- compileResult.run(CLASS).assertSuccessWithOutput(EXPECTED);
- runDebugger(compileResult.debugConfig());
+ public void testR8() throws Throwable {
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(CLASS)
+ .debug()
+ .addDontObfuscate()
+ .noTreeShaking()
+ .setMinApi(AndroidApiLevel.B)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(this::runDebugger);
}
private void runDebugger(DebugTestConfig config) throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java b/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
index e382d39..9cd5079 100644
--- a/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LoadInvokeLoadOptimizationTestRunner.java
@@ -3,11 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
-import com.android.tools.r8.ToolHelper;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.FrameInspector;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.DescriptorUtils;
-import java.util.List;
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -19,44 +20,56 @@
static final Class CLASS = LoadInvokeLoadOptimizationTest.class;
static final String NAME = CLASS.getCanonicalName();
- static final String DESC = DescriptorUtils.javaTypeToDescriptor(NAME);
static final String FILE = CLASS.getSimpleName() + ".java";
static final AndroidApiLevel minApi = AndroidApiLevel.B;
+ static final String EXPECTED = "";
- private final String name;
- private final DebugTestConfig config;
+ private final TestParameters parameters;
@Parameters(name = "{0}")
- public static List<Object[]> setup() {
- DebugTestParameters parameters =
- parameters()
- .add("CF", temp -> testForJvm(temp).addTestClasspath().debugConfig())
- .add(
- "D8",
- temp -> testForD8(temp).setMinApi(minApi).addProgramClasses(CLASS).debugConfig());
- for (Backend backend : ToolHelper.getBackends()) {
- parameters.add(
- "R8/" + backend,
- temp ->
- testForR8(temp, backend)
- .noTreeShaking()
- .addDontObfuscate()
- .addKeepRules("-keepattributes SourceFile,LineNumberTable")
- .addProgramClasses(CLASS)
- .setMinApi(minApi)
- .debug()
- .debugConfig());
- }
- return parameters.build();
+ public static TestParametersCollection setup() {
+ return TestParameters.builder().withAllRuntimes().build();
}
- public LoadInvokeLoadOptimizationTestRunner(String name, DelayedDebugTestConfig config) {
- this.name = name;
- this.config = config.getConfig(temp);
+ public LoadInvokeLoadOptimizationTestRunner(TestParameters parameters) {
+ this.parameters = parameters;
}
@Test
- public void test() throws Throwable {
+ public void testReference() throws Throwable {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm(temp)
+ .addProgramClasses(CLASS)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(this::runDebugger);
+ }
+
+ @Test
+ public void testD8() throws Throwable {
+ testForD8(parameters.getBackend())
+ .setMinApi(minApi)
+ .addProgramClasses(CLASS)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(this::runDebugger);
+ }
+
+ @Test
+ public void testR8() throws Throwable {
+ testForR8(parameters.getBackend())
+ .noTreeShaking()
+ .addDontObfuscate()
+ .addKeepRules("-keepattributes SourceFile,LineNumberTable")
+ .addProgramClasses(CLASS)
+ .setMinApi(minApi)
+ .debug()
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(this::runDebugger);
+ }
+
+ public void runDebugger(DebugTestConfig config) throws Throwable {
Value int42 = Value.createInt(42);
Value int7 = Value.createInt(7);
// The test ensures that when breaking inside a function and changing a local in the parent
diff --git a/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestRunner.java b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestRunner.java
index aa4542a..460bdd7 100644
--- a/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/StepIntoMethodWithTypeParameterArgumentsTestRunner.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
@@ -60,9 +61,10 @@
checkLocal("strings"),
checkNoLocal("objects"),
stepOver(),
- // Step will skip line 14 and hit 15 on JVM but will (correctly?) hit 14 on Art.
+ // Step will skip line 14 and hit 15 on JVM but will (correctly?) hit 14 on Art pre-13.
subcommands(
- config instanceof CfDebugTestConfig
+ (config.getRuntime().isCf()
+ || config.getRuntime().asDex().getVersion().isNewerThanOrEqual(Version.V13_0_0))
? ImmutableList.of()
: ImmutableList.of(checkLine(FILE, 14), stepOver())),
checkLine(FILE, 15),
diff --git a/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializationTest.java b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializationTest.java
new file mode 100644
index 0000000..1abe558
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializationTest.java
@@ -0,0 +1,172 @@
+// 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.debug.classinit;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.debug.DebugTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// Appears to have been a regression for b/65148874, but reproduction was not possible recently.
+@RunWith(Parameterized.class)
+public class ClassInitializationTest extends DebugTestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withAllRuntimes()
+ .withApiLevel(AndroidApiLevel.B)
+ .enableApiLevelsForCf()
+ .build();
+ }
+
+ private final TestParameters parameters;
+
+ public ClassInitializationTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private String fileName(Class<?> clazz) {
+ return clazz.getSimpleName() + ".java";
+ }
+
+ @Test
+ public void testStaticAssignmentInitialization() throws Throwable {
+ Class<?> clazz = ClassInitializerAssignmentInitialization.class;
+ final String SOURCE_FILE = fileName(clazz);
+ final String CLASS = typeName(clazz);
+ testForD8(parameters.getBackend())
+ .addProgramClasses(clazz)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), clazz)
+ .debugger(
+ config ->
+ runDebugTest(
+ config,
+ clazz,
+ breakpoint(CLASS, "<clinit>"),
+ run(),
+ checkLine(SOURCE_FILE, 8),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(0)),
+ checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
+ checkStaticFieldClinitSafe(CLASS, "z", null, Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 11),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(1)),
+ checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
+ checkStaticFieldClinitSafe(CLASS, "z", null, Value.createInt(0)),
+ breakpoint(CLASS, "main"),
+ run(),
+ checkStaticField(CLASS, "x", null, Value.createInt(1)),
+ checkStaticField(CLASS, "y", null, Value.createInt(0)),
+ checkStaticField(CLASS, "z", null, Value.createInt(2)),
+ run()));
+ }
+
+ @Test
+ public void testBreakpointInEmptyClassInitializer() throws Throwable {
+ Class<?> clazz = ClassInitializerEmpty.class;
+ final String SOURCE_FILE = fileName(clazz);
+ final String CLASS = typeName(clazz);
+ testForD8(parameters.getBackend())
+ .addProgramClasses(clazz)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), clazz)
+ .debugger(
+ config ->
+ runDebugTest(
+ config,
+ clazz,
+ breakpoint(CLASS, "<clinit>"),
+ run(),
+ checkLine(SOURCE_FILE, 9),
+ run()));
+ }
+
+ @Test
+ public void testStaticBlockInitialization() throws Throwable {
+ Class<?> clazz = ClassInitializerStaticBlockInitialization.class;
+ final String SOURCE_FILE = fileName(clazz);
+ final String CLASS = typeName(clazz);
+
+ testForD8(parameters.getBackend())
+ .addProgramClasses(clazz)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), clazz)
+ .debugger(
+ config ->
+ runDebugTest(
+ config,
+ CLASS,
+ breakpoint(CLASS, "<clinit>"),
+ run(),
+ checkLine(SOURCE_FILE, 13),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 14),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(1)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 15),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 18),
+ stepOver(),
+ checkLine(SOURCE_FILE, 20),
+ breakpoint(CLASS, "main"),
+ run(),
+ checkLine(SOURCE_FILE, 24),
+ checkStaticField(CLASS, "x", null, Value.createInt(3)),
+ run()));
+ }
+
+ @Test
+ public void testStaticMixedInitialization() throws Throwable {
+ Class<?> clazz = ClassInitializerMixedInitialization.class;
+ final String SOURCE_FILE = fileName(clazz);
+ final String CLASS = typeName(clazz);
+
+ testForD8(parameters.getBackend())
+ .addProgramClasses(clazz)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), clazz)
+ .debugger(
+ config ->
+ runDebugTest(
+ config,
+ CLASS,
+ breakpoint(CLASS, "<clinit>"),
+ run(),
+ checkLine(SOURCE_FILE, 9),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(0)),
+ checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 13),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(1)),
+ checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 14),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
+ checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 17),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
+ checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(0)),
+ stepOver(),
+ checkLine(SOURCE_FILE, 19),
+ checkStaticFieldClinitSafe(CLASS, "x", null, Value.createInt(2)),
+ checkStaticFieldClinitSafe(CLASS, "y", null, Value.createInt(2)),
+ breakpoint(CLASS, "main"),
+ run(),
+ checkLine(SOURCE_FILE, 23),
+ checkStaticField(CLASS, "x", null, Value.createInt(3)),
+ checkStaticField(CLASS, "y", null, Value.createInt(2)),
+ run()));
+ }
+}
diff --git a/src/test/debugTestResources/ClassInitializerAssignmentInitialization.java b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerAssignmentInitialization.java
similarity index 77%
rename from src/test/debugTestResources/ClassInitializerAssignmentInitialization.java
rename to src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerAssignmentInitialization.java
index fa6078a..2044905 100644
--- a/src/test/debugTestResources/ClassInitializerAssignmentInitialization.java
+++ b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerAssignmentInitialization.java
@@ -1,6 +1,7 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// 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.debug.classinit;
public class ClassInitializerAssignmentInitialization {
diff --git a/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerEmpty.java b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerEmpty.java
new file mode 100644
index 0000000..62cacf3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerEmpty.java
@@ -0,0 +1,12 @@
+// 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.debug.classinit;
+
+public class ClassInitializerEmpty {
+
+ static {
+ }
+
+ public static void main(String[] args) {}
+}
diff --git a/src/test/debugTestResources/ClassInitializerMixedInitialization.java b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerMixedInitialization.java
similarity index 80%
rename from src/test/debugTestResources/ClassInitializerMixedInitialization.java
rename to src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerMixedInitialization.java
index 504db6c..9d11f9d 100644
--- a/src/test/debugTestResources/ClassInitializerMixedInitialization.java
+++ b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerMixedInitialization.java
@@ -1,6 +1,7 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// 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.debug.classinit;
public class ClassInitializerMixedInitialization {
diff --git a/src/test/debugTestResources/ClassInitializerStaticBlockInitialization.java b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerStaticBlockInitialization.java
similarity index 81%
rename from src/test/debugTestResources/ClassInitializerStaticBlockInitialization.java
rename to src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerStaticBlockInitialization.java
index 78fe9a0..d79ff7e 100644
--- a/src/test/debugTestResources/ClassInitializerStaticBlockInitialization.java
+++ b/src/test/java/com/android/tools/r8/debug/classinit/ClassInitializerStaticBlockInitialization.java
@@ -1,6 +1,7 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// 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.debug.classinit;
public class ClassInitializerStaticBlockInitialization {
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
index 499527f..4dff885 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithSelfReferenceTestRunner.java
@@ -11,8 +11,6 @@
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.Disassemble;
import com.android.tools.r8.Disassemble.DisassembleCommand;
-import com.android.tools.r8.JvmTestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
@@ -92,28 +90,28 @@
@Test
public void testJvm() throws Throwable {
assumeTrue(parameters.isCfRuntime());
- JvmTestBuilder builder = testForJvm().addTestClasspath();
- builder.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED);
- runDebugger(builder.debugConfig(), false);
+ testForJvm()
+ .addProgramClassesAndInnerClasses(CLASS)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(config -> runDebugger(config, false));
}
@Test
public void testR8() throws Throwable {
- R8TestCompileResult compileResult =
- testForR8(parameters.getBackend())
- .addProgramClassesAndInnerClasses(CLASS)
- .setMinApi(parameters.getApiLevel())
- .addDontObfuscate()
- .noTreeShaking()
- .addKeepAllAttributes()
- .debug()
- .compile()
- .assertNoMessages();
- compileResult
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(CLASS)
+ .setMinApi(parameters.getApiLevel())
+ .addDontObfuscate()
+ .noTreeShaking()
+ .addKeepAllAttributes()
+ .debug()
+ .compile()
+ .assertNoMessages()
.run(parameters.getRuntime(), CLASS)
.assertSuccessWithOutput(EXPECTED)
- .inspect(inspector -> assertThat(inspector.clazz(CLASS), isPresent()));
- runDebugger(compileResult.debugConfig(), true);
+ .inspect(inspector -> assertThat(inspector.clazz(CLASS), isPresent()))
+ .debugger(config -> runDebugger(config, true));
}
@Test
@@ -168,9 +166,8 @@
.assertNoMessages()
.writeToZip(out2)
.run(parameters.getRuntime(), CLASS)
- .assertSuccessWithOutput(EXPECTED);
-
- runDebugger(compiledResult.debugConfig(), false);
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(config -> runDebugger(config, false));
Path dissasemble1 = temp.newFolder().toPath().resolve("disassemble1.txt");
Path dissasemble2 = temp.newFolder().toPath().resolve("disassemble2.txt");
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
index 263a87e..ce29093 100644
--- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
+++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
@@ -6,10 +6,7 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.D8TestCompileResult;
-import com.android.tools.r8.JvmTestBuilder;
import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.debug.DebugTestBase;
@@ -88,24 +85,24 @@
@Test
public void testJvm() throws Throwable {
assumeTrue(parameters.isCfRuntime());
- JvmTestBuilder builder = testForJvm().addTestClasspath();
- builder.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED);
- runDebugger(builder.debugConfig(), false);
+ testForJvm()
+ .addProgramClassesAndInnerClasses(CLASS)
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(config -> runDebugger(config, false));
}
@Test
public void testD8() throws Throwable {
assumeTrue(parameters.isDexRuntime());
- D8TestCompileResult compileResult =
- testForD8()
- .addProgramClassesAndInnerClasses(CLASS)
- .setMinApiThreshold(AndroidApiLevel.K)
- .compile()
- .assertNoMessages();
- compileResult
+ testForD8()
+ .addProgramClassesAndInnerClasses(CLASS)
+ .setMinApiThreshold(AndroidApiLevel.K)
+ .compile()
+ .assertNoMessages()
.run(parameters.getRuntime(), CLASS)
- .assertSuccessWithOutput(EXPECTED);
- runDebugger(compileResult.debugConfig(), true);
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(config -> runDebugger(config, true));
}
@Test
@@ -125,8 +122,9 @@
if (parameters.isDexRuntime()) {
r8FullTestBuilder.setMinApiThreshold(AndroidApiLevel.K);
}
- R8TestCompileResult compileResult = r8FullTestBuilder.compile();
- compileResult.run(parameters.getRuntime(), CLASS).assertSuccessWithOutput(EXPECTED);
- runDebugger(compileResult.debugConfig(), true);
+ r8FullTestBuilder
+ .run(parameters.getRuntime(), CLASS)
+ .assertSuccessWithOutput(EXPECTED)
+ .debugger(config -> runDebugger(config, true));
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java
index fd59fc9..aae96ea 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryInvalidTest.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.DesugaredLibraryTestBuilder;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
@@ -47,7 +46,7 @@
LibraryDesugaringSpecification jdk11InvalidLib =
new LibraryDesugaringSpecification(
"JDK11_INVALID_LIB",
- ToolHelper.getUndesugaredJdk11LibJarForTesting(),
+ LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar(),
"jdk11/desugar_jdk_libs.json",
AndroidApiLevel.L,
LibraryDesugaringSpecification.JDK11_DESCRIPTOR,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
index 71026ba..9f50994 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
@@ -5,10 +5,8 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
-import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import com.android.tools.r8.CompilationFailedException;
@@ -18,7 +16,6 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
-import com.android.tools.r8.errors.DesugaredLibraryMismatchDiagnostic;
import com.android.tools.r8.origin.Origin;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
@@ -40,8 +37,7 @@
return buildParameters(
getTestParameters()
.withDexRuntime(Version.first())
- .withDexRuntime(Version.V7_0_0)
- .withDexRuntime(Version.V8_1_0)
+ .withDefaultDexRuntime()
.withDexRuntime(Version.last())
.withOnlyDexRuntimeApiLevel()
.build(),
@@ -82,8 +78,6 @@
containsString(
"The compilation is slowed down due to a mix of class file and dex"
+ " file inputs in the context of desugared library.")));
- diagnostics.assertErrorsMatch(
- diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
} else {
diagnostics.assertNoMessages();
}
@@ -149,23 +143,11 @@
.writeToZip();
// Combine CF input with library desugaring with dexing without library desugaring.
- try {
- testForD8()
- .addProgramFiles(desugaredLibrary)
- .addProgramClasses(TestRunner.class)
- .setMinApi(parameters.getApiLevel())
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- if (libraryDesugaringSpecification.hasAnyDesugaring(parameters)) {
- diagnostics.assertOnlyErrors();
- diagnostics.assertErrorsMatch(
- diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
- } else {
- diagnostics.assertNoMessages();
- }
- });
- } catch (CompilationFailedException e) {
- }
+ testForD8()
+ .addProgramFiles(desugaredLibrary)
+ .addProgramClasses(TestRunner.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
}
@Test
@@ -186,23 +168,11 @@
.compile()
.writeToZip();
- try {
- testForD8()
- .addProgramFiles(libraryDex)
- .addProgramFiles(programDex)
- .setMinApi(parameters.getApiLevel())
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- if (libraryDesugaringSpecification.hasAnyDesugaring(parameters)) {
- diagnostics.assertOnlyErrors();
- diagnostics.assertErrorsMatch(
- diagnosticType(DesugaredLibraryMismatchDiagnostic.class));
- } else {
- diagnostics.assertNoMessages();
- }
- });
- } catch (CompilationFailedException e) {
- }
+ testForD8()
+ .addProgramFiles(libraryDex)
+ .addProgramFiles(programDex)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
}
@Test
@@ -247,21 +217,11 @@
.compile()
.writeToZip();
- try {
- testForD8()
- .addProgramFiles(libraryDex)
- .addProgramFiles(programDex)
- .setMinApi(parameters.getApiLevel())
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertOnlyErrors();
- diagnostics.assertErrorsMatch(
- allOf(
- diagnosticType(DesugaredLibraryMismatchDiagnostic.class),
- diagnosticMessage(containsString("my_group:my_artifact:1.0.9"))));
- });
- } catch (CompilationFailedException e) {
- }
+ testForD8()
+ .addProgramFiles(libraryDex)
+ .addProgramFiles(programDex)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
}
static class Library {}
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 c572169..bb51f2f 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
@@ -100,9 +100,12 @@
"java.util.Locale$FilteringMode",
"java.util.SplittableRandom");
- // TODO(b/238179854): Investigate how to fix these.
- private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION =
+ private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION = ImmutableSet.of();
+
+ // Missing conversions in JDK8 and JDK11_LEGACY desugared library that are fixed in JDK11.
+ private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_8 =
ImmutableSet.of(
+ "java.util.Set java.util.stream.Collector.characteristics()",
"java.util.stream.Stream java.util.stream.Stream.flatMap(java.util.function.Function)",
"java.util.stream.DoubleStream"
+ " java.util.stream.DoubleStream.flatMap(java.util.function.DoubleFunction)",
@@ -115,12 +118,7 @@
"java.util.stream.LongStream"
+ " java.util.stream.Stream.flatMapToLong(java.util.function.Function)",
"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()",
+ + " java.util.stream.LongStream.flatMap(java.util.function.LongFunction)",
"java.lang.Object java.lang.StackWalker.walk(java.util.function.Function)");
// TODO(b/238179854): Investigate how to fix these.
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index 341ab5a..17ed737 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -157,7 +157,7 @@
Path jdkLibJar =
libraryDesugaringSpecification == JDK8
? ToolHelper.DESUGARED_JDK_8_LIB_JAR
- : ToolHelper.getUndesugaredJdk11LibJarForTesting();
+ : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar();
GenerateLintFiles.main(
new String[] {
libraryDesugaringSpecification.getSpecification().toString(),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
index be8001a..c9b6885 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MergingWithDesugaredLibraryTest.java
@@ -14,18 +14,15 @@
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.not;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8TestCompileResult;
-import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.ExtractMarker;
import com.android.tools.r8.LibraryDesugaringTestConfiguration;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
@@ -63,35 +60,37 @@
@Test
public void testMergeDesugaredAndNonDesugared() throws Exception {
- D8TestCompileResult compileResult;
- try {
- compileResult =
- testForD8()
- .addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
- .addProgramFiles(buildPart1DesugaredLibrary(), buildPart2NoDesugaredLibrary())
- .setMinApi(parameters.getApiLevel())
- .applyIf(
- someLibraryDesugaringRequired(),
- b ->
- b.enableCoreLibraryDesugaring(
- LibraryDesugaringTestConfiguration.forSpecification(
- libraryDesugaringSpecification.getSpecification())))
- .compileWithExpectedDiagnostics(this::assertError)
- .addRunClasspathFiles(
- getNonShrunkDesugaredLib(parameters, libraryDesugaringSpecification));
- assertFalse(expectError());
- } catch (CompilationFailedException e) {
- assertTrue(expectError());
- return;
- }
- assert !expectError();
- assert compileResult != null;
+ D8TestCompileResult compileResult =
+ testForD8()
+ .addLibraryFiles(libraryDesugaringSpecification.getLibraryFiles())
+ .addProgramFiles(buildPart1DesugaredLibrary(), buildPart2NoDesugaredLibrary())
+ .setMinApi(parameters.getApiLevel())
+ .applyIf(
+ someLibraryDesugaringRequired(),
+ b ->
+ b.enableCoreLibraryDesugaring(
+ LibraryDesugaringTestConfiguration.forSpecification(
+ libraryDesugaringSpecification.getSpecification())))
+ .compile()
+ .addRunClasspathFiles(
+ getNonShrunkDesugaredLib(parameters, libraryDesugaringSpecification));
compileResult
.run(parameters.getRuntime(), Part1.class)
- .assertSuccessWithOutputLines(JAVA_RESULT);
+ .assertSuccessWithOutputLines(getExpected());
+ boolean expectNoSuchMethodError =
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isOlderThan(Version.V7_0_0);
compileResult
.run(parameters.getRuntime(), Part2.class)
- .assertSuccessWithOutputLines(JAVA_RESULT);
+ .assertFailureWithErrorThatThrowsIf(expectNoSuchMethodError, NoSuchMethodError.class)
+ .assertSuccessWithOutputLinesIf(!expectNoSuchMethodError, JAVA_RESULT);
+ }
+
+ private String getExpected() {
+ if (parameters.getApiLevel().getLevel() >= AndroidApiLevel.N.getLevel()) {
+ return JAVA_RESULT;
+ } else {
+ return J$_RESULT;
+ }
}
@Test
@@ -169,9 +168,17 @@
.writeToZip();
// D8 dex file output marker has the same marker as the D8 class file output.
- // TODO(b/166617364): They should not be the same after backend is recorded and neither has
- // library desugaring info.
- assertMarkersMatch(ExtractMarker.extractMarkerFromJarFile(desugaredLibDex), markerMatcher);
+ Matcher<Marker> dexMarkerMatcher =
+ allOf(
+ markerTool(Tool.D8),
+ markerCompilationMode(CompilationMode.DEBUG),
+ markerBackend(Backend.DEX),
+ markerIsDesugared(),
+ markerMinApi(parameters.getApiLevel()),
+ not(markerHasDesugaredLibraryIdentifier()));
+ List<Matcher<Marker>> markerMatcherAfterDex = ImmutableList.of(markerMatcher, dexMarkerMatcher);
+ assertMarkersMatch(
+ ExtractMarker.extractMarkerFromJarFile(desugaredLibDex), markerMatcherAfterDex);
// Build an app using library desugaring merging with library not using library desugaring.
Path app;
@@ -183,36 +190,20 @@
.writeToZip();
// When there is no class-file resources we are adding the marker for the last compilation.
- assertMarkersMatch(
- ExtractMarker.extractMarkerFromDexFile(app),
- ImmutableList.of(
- markerMatcher,
- allOf(
- markerTool(Tool.D8),
- markerCompilationMode(CompilationMode.DEBUG),
- markerBackend(Backend.DEX),
- markerIsDesugared(),
- markerMinApi(parameters.getApiLevel()),
- someLibraryDesugaringRequired()
- ? markerHasDesugaredLibraryIdentifier()
- : not(markerHasDesugaredLibraryIdentifier()))));
- }
-
- private void assertError(TestDiagnosticMessages m) {
- List<Diagnostic> errors = m.getErrors();
- if (expectError()) {
- assertEquals(1, errors.size());
- assertTrue(
- errors.stream()
- .anyMatch(
- w ->
- w.getDiagnosticMessage()
- .contains(
- "The compilation is merging inputs with different"
- + " desugared library desugaring")));
- } else {
- assertEquals(0, errors.size());
+ List<Matcher<Marker>> expectedMarkers = new ArrayList<>();
+ expectedMarkers.add(markerMatcher);
+ expectedMarkers.add(dexMarkerMatcher);
+ if (someLibraryDesugaringRequired()) {
+ expectedMarkers.add(
+ allOf(
+ markerTool(Tool.D8),
+ markerCompilationMode(CompilationMode.DEBUG),
+ markerBackend(Backend.DEX),
+ markerIsDesugared(),
+ markerMinApi(parameters.getApiLevel()),
+ markerHasDesugaredLibraryIdentifier()));
}
+ assertMarkersMatch(ExtractMarker.extractMarkerFromDexFile(app), expectedMarkers);
}
private boolean expectError() {
@@ -241,21 +232,12 @@
.addRunClasspathFiles(
getNonShrunkDesugaredLib(parameters, libraryDesugaringSpecification))
.inspectDiagnosticMessages(this::assertWarningPresent);
- if (parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) {
- compileResult
- .run(parameters.getRuntime(), Part1.class)
- .assertSuccessWithOutputLines(J$_RESULT);
- compileResult
- .run(parameters.getRuntime(), Part2.class)
- .assertSuccessWithOutputLines(J$_RESULT);
- } else {
- compileResult
- .run(parameters.getRuntime(), Part1.class)
- .assertSuccessWithOutputLines(JAVA_RESULT);
- compileResult
- .run(parameters.getRuntime(), Part2.class)
- .assertSuccessWithOutputLines(JAVA_RESULT);
- }
+ compileResult
+ .run(parameters.getRuntime(), Part1.class)
+ .assertSuccessWithOutputLines(getExpected());
+ compileResult
+ .run(parameters.getRuntime(), Part2.class)
+ .assertSuccessWithOutputLines(getExpected());
}
private void assertWarningPresent(TestDiagnosticMessages testDiagnosticMessages) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index 9c300c7..2306edf 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -83,7 +82,7 @@
LibraryDesugaringSpecification jdk11MaxCompileSdk =
new LibraryDesugaringSpecification(
"JDK11_MAX",
- ToolHelper.getUndesugaredJdk11LibJarForTesting(),
+ LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar(),
"jdk11/desugar_jdk_libs.json",
AndroidApiLevel.LATEST,
LibraryDesugaringSpecification.JDK11_DESCRIPTOR,
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 5a05e80..51222b2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -64,7 +64,7 @@
new LibraryDesugaringSpecification(
"JDK11_CL",
ImmutableSet.of(
- ToolHelper.getUndesugaredJdk11LibJarForTesting(),
+ LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar(),
ToolHelper.getDesugarLibConversions(LATEST),
ToolHelper.getCoreLambdaStubs()),
JDK11.getSpecification(),
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FlatMapConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FlatMapConversionTest.java
new file mode 100644
index 0000000..62561e1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/FlatMapConversionTest.java
@@ -0,0 +1,155 @@
+// 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.conversiontests;
+
+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_LEGACY;
+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 static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.TestParameters;
+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.CustomLibrarySpecification;
+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.util.Arrays;
+import java.util.List;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+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 FlatMapConversionTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "[1, 2, 3]",
+ "[1, 2, 3]",
+ "[1.0, 2.0, 3.0]",
+ "[1, 2, 3]",
+ "[1, 2, 3]",
+ "[1.0, 2.0, 3.0]",
+ "[1, 2, 3]");
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getConversionParametersUpToExcluding(MIN_SUPPORTED),
+ ImmutableList.of(JDK8, JDK11_LEGACY, JDK11_MINIMAL, JDK11, JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public FlatMapConversionTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void testReference() throws Throwable {
+ Assume.assumeTrue(
+ "Run only once",
+ libraryDesugaringSpecification == JDK11 && compilationSpecification == D8_L8DEBUG);
+ testForD8()
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(Executor.class, CustomLibClass.class)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testConvert() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramClasses(Executor.class)
+ .setCustomLibrarySpecification(
+ new CustomLibrarySpecification(CustomLibClass.class, MIN_SUPPORTED))
+ .addKeepMainRule(Executor.class)
+ .run(parameters.getRuntime(), Executor.class)
+ .applyIf(
+ libraryDesugaringSpecification == JDK8
+ || libraryDesugaringSpecification == JDK11_LEGACY,
+ r ->
+ r.assertFailureWithErrorThatMatches(containsString("java.lang.ClassCastException")),
+ r -> r.assertSuccessWithOutput(EXPECTED_RESULT));
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ System.out.println(
+ Arrays.toString(CustomLibClass.getIntStreamAsStream().flatMap(Stream::of).toArray()));
+
+ System.out.println(
+ Arrays.toString(
+ CustomLibClass.getIntStreamAsStream().flatMapToInt(IntStream::of).toArray()));
+ System.out.println(
+ Arrays.toString(
+ CustomLibClass.getDoubleStreamAsStream()
+ .flatMapToDouble(DoubleStream::of)
+ .toArray()));
+ System.out.println(
+ Arrays.toString(
+ CustomLibClass.getLongStreamAsStream().flatMapToLong(LongStream::of).toArray()));
+
+ System.out.println(
+ Arrays.toString(CustomLibClass.getIntStream().flatMap(IntStream::of).toArray()));
+ System.out.println(
+ Arrays.toString(CustomLibClass.getDoubleStream().flatMap(DoubleStream::of).toArray()));
+ System.out.println(
+ Arrays.toString(CustomLibClass.getLongStream().flatMap(LongStream::of).toArray()));
+ }
+ }
+
+ // This class will be put at compilation time as library and on the runtime class path.
+ // This class is convenient for easy testing. Each method plays the role of methods in the
+ // platform APIs for which argument/return values need conversion.
+ static class CustomLibClass {
+
+ public static Stream<Integer> getIntStreamAsStream() {
+ return Arrays.stream(new Integer[] {1, 2, 3});
+ }
+
+ public static Stream<Double> getDoubleStreamAsStream() {
+ return Arrays.stream(new Double[] {1.0, 2.0, 3.0});
+ }
+
+ public static Stream<Long> getLongStreamAsStream() {
+ return Arrays.stream(new Long[] {1L, 2L, 3L});
+ }
+
+ public static IntStream getIntStream() {
+ return Arrays.stream(new int[] {1, 2, 3});
+ }
+
+ public static DoubleStream getDoubleStream() {
+ return Arrays.stream(new double[] {1.0, 2.0, 3.0});
+ }
+
+ public static LongStream getLongStream() {
+ return Arrays.stream(new long[] {1L, 2L, 3L});
+ }
+ }
+}
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 3309c38..da24386 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
@@ -79,6 +79,11 @@
if (!entry.getName().endsWith(".class")) {
return;
}
+ // TODO(b/244273080): Remove from the jar.
+ if (entry.getName().equals("sun/nio/fs/DefaultFileSystemProvider.class")
+ || entry.getName().equals("sun/nio/fs/DefaultFileTypeDetector.class")) {
+ return;
+ }
final byte[] bytes = StreamUtils.streamToByteArrayClose(input);
final byte[] rewrittenBytes =
transformInvoke(entry.getName().substring(0, entry.getName().length() - 6), bytes);
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 4b41588..e68c8bc 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
@@ -24,6 +24,9 @@
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase.KeepRuleConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileForRewriting;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.FileUtils;
@@ -34,6 +37,7 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import org.junit.Assume;
@@ -45,6 +49,7 @@
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
private final CompilationSpecification compilationSpecification;
private final TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> builder;
+ private List<ArtProfileForRewriting> l8ArtProfilesForRewriting = new ArrayList<>();
private String l8ExtraKeepRules = "";
private Consumer<InternalOptions> l8OptionModifier = ConsumerUtils.emptyConsumer();
private boolean l8FinalPrefixVerification = true;
@@ -417,6 +422,11 @@
compilationSpecification.isL8Shrink() && !backend.isCf() && !l8ExtraKeepRules.isEmpty(),
b -> b.addKeepRules(l8ExtraKeepRules))
.addOptionsModifier(l8OptionModifier);
+ for (ArtProfileForRewriting artProfileForRewriting : l8ArtProfilesForRewriting) {
+ l8Builder.addArtProfileForRewriting(
+ artProfileForRewriting.getArtProfileProvider(),
+ artProfileForRewriting.getResidualArtProfileConsumer());
+ }
}
public String collectKeepRulesWithTraceReferences(
@@ -482,4 +492,11 @@
builder.disableDesugaring();
return this;
}
+
+ public DesugaredLibraryTestBuilder<?> addL8ArtProfileForRewriting(
+ ArtProfileProvider artProfileProvider, ArtProfileConsumer residualArtProfileConsumer) {
+ l8ArtProfilesForRewriting.add(
+ new ArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer));
+ return this;
+ }
}
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 3ec019a..0222cb7 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
@@ -6,7 +6,6 @@
import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_11_LIB_JAR;
import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_8_LIB_JAR;
import static com.android.tools.r8.ToolHelper.DESUGARED_LIB_RELEASES_DIR;
-import static com.android.tools.r8.ToolHelper.getUndesugaredJdk11LibJarForTesting;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LATEST;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LEGACY;
@@ -14,6 +13,7 @@
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.jdk11.DesugaredLibraryJDK11Undesugarer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -29,6 +29,7 @@
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
+import org.junit.rules.TemporaryFolder;
public class LibraryDesugaringSpecification {
@@ -89,6 +90,24 @@
LATEST
}
+ private static final Path tempLibraryJDK11Undesugar = createUndesugaredJdk11LibJarForTesting();
+
+ private static Path createUndesugaredJdk11LibJarForTesting() {
+ try {
+ TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest();
+ staticTemp.create();
+ Path jdklib_desugaring = staticTemp.newFolder("jdklib_desugaring").toPath();
+ return DesugaredLibraryJDK11Undesugarer.undesugaredJarJDK11(
+ jdklib_desugaring, DESUGARED_JDK_11_LIB_JAR);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static Path getTempLibraryJDK11Undesugar() {
+ return tempLibraryJDK11Undesugar;
+ }
+
// Main head specifications.
public static LibraryDesugaringSpecification JDK8 =
new LibraryDesugaringSpecification(
@@ -101,7 +120,7 @@
public static LibraryDesugaringSpecification JDK11 =
new LibraryDesugaringSpecification(
"JDK11",
- getUndesugaredJdk11LibJarForTesting(),
+ tempLibraryJDK11Undesugar,
"jdk11/desugar_jdk_libs.json",
AndroidApiLevel.R,
JDK11_DESCRIPTOR,
@@ -109,7 +128,7 @@
public static LibraryDesugaringSpecification JDK11_MINIMAL =
new LibraryDesugaringSpecification(
"JDK11_MINIMAL",
- getUndesugaredJdk11LibJarForTesting(),
+ tempLibraryJDK11Undesugar,
"jdk11/desugar_jdk_libs_minimal.json",
AndroidApiLevel.R,
EMPTY_DESCRIPTOR_24,
@@ -117,7 +136,7 @@
public static LibraryDesugaringSpecification JDK11_PATH =
new LibraryDesugaringSpecification(
"JDK11_PATH",
- getUndesugaredJdk11LibJarForTesting(),
+ tempLibraryJDK11Undesugar,
"jdk11/desugar_jdk_libs_nio.json",
AndroidApiLevel.R,
JDK11_PATH_DESCRIPTOR,
@@ -127,7 +146,7 @@
public static LibraryDesugaringSpecification JDK11_PATH_ALTERNATIVE_3 =
new LibraryDesugaringSpecification(
"JDK11_PATH_ALTERNATIVE_3",
- getUndesugaredJdk11LibJarForTesting(),
+ tempLibraryJDK11Undesugar,
"jdk11/desugar_jdk_libs_nio_alternative_3.json",
AndroidApiLevel.R,
JDK11_PATH_DESCRIPTOR,
@@ -135,7 +154,7 @@
public static LibraryDesugaringSpecification JDK11_CHM_ONLY =
new LibraryDesugaringSpecification(
"JDK11_CHM_ONLY",
- getUndesugaredJdk11LibJarForTesting(),
+ tempLibraryJDK11Undesugar,
"jdk11/chm_only_desugar_jdk_libs.json",
AndroidApiLevel.R,
EMPTY_DESCRIPTOR_24,
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/RegressionB244970402.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/RegressionB244970402.java
new file mode 100644
index 0000000..58142a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/RegressionB244970402.java
@@ -0,0 +1,53 @@
+// 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.staticinterfacemethod;
+
+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;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RegressionB244970402 extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultDexRuntime().withApiLevel(AndroidApiLevel.B).build();
+ }
+
+ private final TestParameters parameters;
+
+ public RegressionB244970402(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(I.class, TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .addMainDexRules("-keep class * { *; }")
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("null");
+ }
+
+ interface I {
+
+ static I getInstance() {
+ return null;
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(I.getInstance());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index f774b57..fae6781 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -10,8 +10,10 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.examples.JavaExampleClassProxy;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -94,6 +96,7 @@
public void testD8() throws Exception {
testForD8(parameters.getBackend())
.addProgramFiles(getProgramInputs())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN.typeName(), getZipFile())
.assertSuccessWithOutput(EXPECTED)
@@ -106,7 +109,7 @@
// TODO(b/168568827): Once we support a nested addSuppressed this will increase.
int expectedSynthetics =
parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
- ? 2
+ ? 3
: 0;
assertEquals(INPUT_CLASSES + expectedSynthetics, inspector.allClasses().size());
});
@@ -117,12 +120,9 @@
assumeTrue(parameters.isDexRuntime());
testForR8(parameters.getBackend())
.addProgramFiles(getProgramInputs())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.addKeepMainRule(MAIN.typeName())
.addKeepClassAndMembersRules(FOO.typeName(), BAR.typeName())
- // TODO(b/214250388): Don't warn about synthetic code.
- .applyIf(
- parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport()),
- builder -> builder.addDontWarn("java.lang.AutoCloseable"))
.setMinApi(parameters.getApiLevel())
.addDontObfuscate()
.run(parameters.getRuntime(), MAIN.typeName(), getZipFile())
@@ -141,11 +141,7 @@
if (parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())) {
Set<String> classOutputWithSynthetics = new HashSet<>(nonSyntheticClassOutput);
classOutputWithSynthetics.add(
- SyntheticItemsTestUtils.syntheticBackportClass(BAR.getClassReference(), 0)
- .getTypeName());
- classOutputWithSynthetics.add(
- SyntheticItemsTestUtils.syntheticTwrCloseResourceClass(
- BAR.getClassReference(), 1)
+ SyntheticItemsTestUtils.syntheticApiOutlineClass(BAR.getClassReference(), 0)
.getTypeName());
assertEquals(classOutputWithSynthetics, foundClasses);
} else {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/PrivateEnumWithPrivateInvokeSpecialTest.java b/src/test/java/com/android/tools/r8/enumunboxing/PrivateEnumWithPrivateInvokeSpecialTest.java
new file mode 100644
index 0000000..3943993
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/PrivateEnumWithPrivateInvokeSpecialTest.java
@@ -0,0 +1,64 @@
+// 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.enumunboxing;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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;
+
+/** This is a reproduction of b/245096779 */
+@RunWith(Parameterized.class)
+public class PrivateEnumWithPrivateInvokeSpecialTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("FOO");
+ }
+
+ private static class Main {
+
+ private enum MyEnum {
+ FOO,
+ BAR;
+
+ private boolean isFoo() {
+ return this == FOO;
+ }
+
+ public MyEnum compute() {
+ if (isFoo()) {
+ return BAR;
+ }
+ return FOO;
+ }
+ }
+
+ public static void main(String[] args) {
+ MyEnum myEnum = args.length > 0 ? MyEnum.FOO : MyEnum.BAR;
+ if (myEnum.compute().isFoo()) {
+ System.out.println("FOO");
+ } else {
+ System.out.println("BAR");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToEnumUnboxedMethodTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToEnumUnboxedMethodTest.java
index c40df02..b9ee0f5 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToEnumUnboxedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToEnumUnboxedMethodTest.java
@@ -6,10 +6,7 @@
import static com.android.tools.r8.graph.invokespecial.InvokeSpecialToEnumUnboxedMethodTest.MyEnum.TEST_1;
import static com.android.tools.r8.graph.invokespecial.InvokeSpecialToEnumUnboxedMethodTest.MyEnum.TEST_2;
-import static org.junit.Assert.assertThrows;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.DiagnosticsMatcher;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -32,19 +29,13 @@
@Test
public void testR8() throws Exception {
- assertThrows(
- CompilationFailedException.class,
- () ->
- testForR8Compat(parameters.getBackend())
- .addInnerClasses(getClass())
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
- .enableInliningAnnotations()
- // TODO(b/235817866): Should not have invalid assert.
- .compileWithExpectedDiagnostics(
- diagnostics ->
- diagnostics.assertErrorThatMatches(
- DiagnosticsMatcher.diagnosticException(AssertionError.class))));
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Foo");
}
public enum MyEnum {
@@ -63,6 +54,8 @@
}
}
+ // This is similar to PrivateEnumWithPrivateInvokeSpecialTest by invoking a private method on an
+ // enum that is unboxed.
public static class Main {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/internal/Chrome200430Test.java b/src/test/java/com/android/tools/r8/internal/Chrome200430Test.java
index 6314424..4e7c6e3 100644
--- a/src/test/java/com/android/tools/r8/internal/Chrome200430Test.java
+++ b/src/test/java/com/android/tools/r8/internal/Chrome200430Test.java
@@ -34,6 +34,10 @@
.addProgramFiles(getProgramFiles())
.addLibraryFiles(getLibraryFiles())
.addKeepRuleFiles(getKeepRuleFiles())
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .allowUnnecessaryDontWarnWildcards()
+ .allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules()
.setMinApi(AndroidApiLevel.N)
.compile();
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
index 9ea0db9..262f164 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2BuilderShrinkingTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -24,9 +25,9 @@
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;
-// TODO(b/112437944): Strengthen test to ensure that builder inlining succeeds even without single-
-// and double-caller inlining.
@RunWith(Parameterized.class)
public class Proto2BuilderShrinkingTest extends ProtoShrinkingTestBase {
@@ -36,10 +37,13 @@
private static List<Path> PROGRAM_FILES =
ImmutableList.of(PROTO2_EXAMPLES_JAR, PROTO2_PROTO_JAR, PROTOBUF_LITE_JAR);
- private final List<String> mains;
- private final TestParameters parameters;
+ @Parameter(0)
+ public List<String> mains;
- @Parameterized.Parameters(name = "{1}, {0}")
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, {0}")
public static List<Object[]> data() {
return buildParameters(
ImmutableList.of(
@@ -59,11 +63,6 @@
getTestParameters().withDefaultDexRuntime().withAllApiLevels().build());
}
- public Proto2BuilderShrinkingTest(List<String> mains, TestParameters parameters) {
- this.mains = mains;
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
R8TestCompileResult result =
@@ -187,18 +186,46 @@
}
private void verifyMethodToInvokeValuesAreAbsent(CodeInspector outputInspector) {
+ ClassSubject generatedMessageLiteClassSubject =
+ outputInspector.clazz("com.google.protobuf.GeneratedMessageLite");
+ assertThat(generatedMessageLiteClassSubject, isPresent());
+
+ MethodSubject isInitializedMethodSubject =
+ generatedMessageLiteClassSubject.uniqueMethodWithName("isInitialized");
+
DexType methodToInvokeType =
- outputInspector.clazz(METHOD_TO_INVOKE_ENUM).getDexProgramClass().type;
+ outputInspector.clazz(METHOD_TO_INVOKE_ENUM).getDexProgramClass().getType();
for (String main : mains) {
MethodSubject mainMethodSubject = outputInspector.clazz(main).mainMethod();
assertThat(mainMethodSubject, isPresent());
+
+ // Verify that the calls to GeneratedMessageLite.createBuilder() have been inlined.
+ assertTrue(
+ mainMethodSubject
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(InstructionSubject::getMethod)
+ .allMatch(
+ method ->
+ method.getHolderType()
+ != generatedMessageLiteClassSubject.getDexProgramClass().getType()
+ || (isInitializedMethodSubject.isPresent()
+ && method
+ == isInitializedMethodSubject
+ .getProgramMethod()
+ .getReference())));
+
+ // Verify that there are no accesses to MethodToInvoke after inlining createBuilder() -- and
+ // specifically no accesses to MethodToInvoke.NEW_BUILDER.
assertTrue(
main,
mainMethodSubject
.streamInstructions()
.filter(InstructionSubject::isStaticGet)
- .map(instruction -> instruction.getField().type)
+ .map(instruction -> instruction.getField().getType())
.noneMatch(methodToInvokeType::equals));
+
+ // Verify that there is no switches on the ordinal of a MethodToInvoke instance.
assertTrue(mainMethodSubject.streamInstructions().noneMatch(InstructionSubject::isSwitch));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/AssertionErrorMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/AssertionErrorMethods.java
new file mode 100644
index 0000000..7b8c096
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/AssertionErrorMethods.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.ir.desugar.backports;
+
+import java.lang.reflect.Constructor;
+
+public final class AssertionErrorMethods {
+
+ public static AssertionError createAssertionError(String message, Throwable cause) {
+ try {
+ Constructor<AssertionError> twoArgsConstructor =
+ AssertionError.class.getDeclaredConstructor(String.class, Throwable.class);
+ return twoArgsConstructor.newInstance(message, cause);
+ } catch (Exception e) {
+ return new AssertionError(message);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
index fb6a937..2059e86 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
@@ -39,11 +39,11 @@
Method method = resource.getClass().getMethod("close");
method.invoke(resource);
} catch (NoSuchMethodException | SecurityException e) {
- throw new AssertionError(resource.getClass() + " does not have a close() method.", e);
+ throw new RuntimeException(resource.getClass() + " does not have a close() method.", e);
} catch (IllegalAccessException
| IllegalArgumentException
| ExceptionInInitializerError e) {
- throw new AssertionError("Fail to call close() on " + resource.getClass(), e);
+ throw new RuntimeException("Fail to call close() on " + resource.getClass(), e);
} catch (InvocationTargetException e) {
throw e.getCause();
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 81f92c3..429f2be 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -34,6 +34,7 @@
factory.createType("Lcom/android/tools/r8/ir/desugar/backports/BackportedMethods;");
private final List<Class<?>> METHOD_TEMPLATE_CLASSES =
ImmutableList.of(
+ AssertionErrorMethods.class,
AtomicReferenceArrayMethods.class,
AtomicReferenceFieldUpdaterMethods.class,
AtomicReferenceMethods.class,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
index 26bbf63..9292441 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -45,6 +46,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(ConstructorWithNonTrivialControlFlowTest.class)
.addKeepMainRule(TestClass.class)
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.addOptionsModification(options -> options.enableClassInlining = enableClassInlining)
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
index b21bcab..e4b2d91 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
@@ -54,6 +54,9 @@
}
private void inspect(CodeInspector inspector) {
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
ClassSubject bClassSubject = inspector.clazz(B.class);
assertThat(bClassSubject, isPresent());
@@ -63,7 +66,8 @@
assertTrue(
negativeTestSubject.streamInstructions().noneMatch(InstructionSubject::isInvokeVirtual));
- MethodSubject positiveTestSubject = bClassSubject.uniqueMethodWithName("positiveTest");
+ // B.positiveTest() is moved to A as a result of bridge hoisting.
+ MethodSubject positiveTestSubject = aClassSubject.uniqueMethodWithName("positiveTest");
assertThat(positiveTestSubject, isPresent());
assertTrue(positiveTestSubject.streamInstructions().noneMatch(this::isInvokeSuper));
assertTrue(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 6037e95..a1cf0e5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -11,13 +11,18 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestRunResult;
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.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.UnverifiableCfCodeDiagnostic;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.Streams;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,36 +66,62 @@
}
@Test
+ public void testNoInlineNonExistingCatchPreLWithApiModeling() throws Exception {
+ setupR8TestBuilder(
+ b ->
+ b.apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification),
+ inspector -> {
+ ClassSubject classSubject = inspector.clazz(TestClassCallingMethodWithNonExisting.class);
+ boolean hasCatchHandler =
+ Streams.stream(classSubject.mainMethod().iterateTryCatches()).count() > 0;
+ // The catch handler does not exist in ClassWithCatchNonExisting.methodWithCatch thus we
+ // assign UNKNOWN api level. As a result we do not inline.
+ assertFalse(hasCatchHandler);
+ });
+ }
+
+ @Test
public void testNoInlineNonExistingCatchPreL() throws Exception {
- R8TestRunResult result =
- testForR8(parameters.getBackend())
- .addProgramClasses(
- TestClassCallingMethodWithNonExisting.class,
- ClassWithCatchNonExisting.class,
- ExistingException.class)
- .addKeepMainRule(TestClassCallingMethodWithNonExisting.class)
- .addDontWarn(NonExistingException.class)
- .allowDiagnosticWarningMessages()
- .setMinApi(parameters.getApiLevel())
- .compileWithExpectedDiagnostics(
- diagnostics ->
- diagnostics.assertWarningsMatch(
- allOf(
- diagnosticType(UnverifiableCfCodeDiagnostic.class),
- diagnosticMessage(
- containsString(
- "Unverifiable code in `void "
- + ClassWithCatchNonExisting.class.getTypeName()
- + ".methodWithCatch()`")))))
- .run(parameters.getRuntime(), TestClassCallingMethodWithNonExisting.class)
- .assertSuccess();
- ClassSubject classSubject =
- result.inspector().clazz(TestClassCallingMethodWithNonExisting.class);
- boolean hasCatchHandler =
- Streams.stream(classSubject.mainMethod().iterateTryCatches()).count() > 0;
- // The catch handler does not exist in ClassWithCatchNonExisting.methodWithCatch thus we assign
- // UNKNOWN api level. As a result we do not inline.
- assertFalse(hasCatchHandler);
+ setupR8TestBuilder(
+ b -> b.apply(ApiModelingTestHelper::disableApiModeling),
+ inspector -> {
+ ClassSubject classSubject = inspector.clazz(TestClassCallingMethodWithNonExisting.class);
+ boolean hasCatchHandler =
+ Streams.stream(classSubject.mainMethod().iterateTryCatches()).count() > 0;
+ int runtimeLevel = parameters.getApiLevel().getLevel();
+ assertEquals(runtimeLevel >= AndroidApiLevel.L.getLevel(), hasCatchHandler);
+ });
+ }
+
+ private void setupR8TestBuilder(
+ ThrowableConsumer<R8FullTestBuilder> r8TestBuilderConsumer,
+ ThrowingConsumer<CodeInspector, ? extends Exception> inspect)
+ throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ TestClassCallingMethodWithNonExisting.class,
+ ClassWithCatchNonExisting.class,
+ ExistingException.class)
+ .addKeepMainRule(TestClassCallingMethodWithNonExisting.class)
+ .addDontWarn(NonExistingException.class)
+ .allowDiagnosticWarningMessages()
+ .setMinApi(parameters.getApiLevel())
+ .apply(r8TestBuilderConsumer)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertWarningsMatch(
+ allOf(
+ diagnosticType(UnverifiableCfCodeDiagnostic.class),
+ diagnosticMessage(
+ containsString(
+ "Unverifiable code in `void "
+ + ClassWithCatchNonExisting.class.getTypeName()
+ + ".methodWithCatch()`")))))
+ .run(parameters.getRuntime(), TestClassCallingMethodWithNonExisting.class)
+ .assertSuccess()
+ .inspect(inspect);
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index 1f51c2f..e0a1d16 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -46,10 +46,9 @@
.addInnerClasses(SingleTargetAfterInliningTest.class)
.addKeepMainRule(TestClass.class)
.addOptionsModification(
- options -> {
- options.inlinerOptions().applyInliningToInlinee = true;
- options.inlinerOptions().applyInliningToInlineeMaxDepth = maxInliningDepth;
- })
+ options ->
+ options.inlinerOptions().applyInliningToInlineePredicateForTesting =
+ (appView, inlinee, inliningDepth) -> true)
.enableAlwaysInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
index 011cc58..9fdb8ca 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -62,6 +63,7 @@
.allowDiagnosticWarningMessages()
.setMinApi(parameters.getApiLevel())
.addDontObfuscate()
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
.run(parameters.getRuntime(), MAIN, "foobar")
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 1273bfd..c8e9e8a 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRBasicCallbackTest.java
@@ -41,7 +41,11 @@
method,
v -> {
throw new Unreachable();
- })
+ },
+ b -> {
+ throw new Unreachable();
+ },
+ factory)
.setMetadata(IRMetadata.unknown())
.addConstNull()
.addConstInt(42)
diff --git a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
index a169bfc..1a8d281 100644
--- a/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LIRRoundtripTest.java
@@ -5,6 +5,7 @@
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -18,7 +19,13 @@
static class TestClass {
public static void main(String[] args) {
- System.out.println("Hello, world!");
+ String message = "Hello, world!";
+ try {
+ System.out.println(42 / (args.length == 0 ? 0 : 1));
+ message = "Oh no!";
+ } catch (ArithmeticException ignored) {
+ }
+ System.out.println(message);
}
}
@@ -57,7 +64,8 @@
.assertSuccessWithOutputLines("Hello, world!");
}
- @Test
+ // TODO(b/225838009): Support debug local info.
+ @Test(expected = CompilationFailedException.class)
public void testRoundtripDebug() throws Exception {
testForD8(parameters.getBackend())
.debug()
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/StaticFieldClassInitMemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/StaticFieldClassInitMemberRebindingTest.java
index c31c93d..7ab14c7 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/StaticFieldClassInitMemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/StaticFieldClassInitMemberRebindingTest.java
@@ -47,7 +47,6 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
- .addOptionsModification(options -> options.enableVisibilityBridgeRemoval = false)
.run(parameters.getRuntime(), Main.class)
// TODO(b/220668540): R8 should not change class loading semantics.
.assertSuccessWithOutputLines(R8_EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
index 87a2fb2..9501ae9 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.debug.DebugTestBase;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.debug.DexDebugTestConfig;
+import com.android.tools.r8.debug.classinit.ClassInitializationTest;
+import com.android.tools.r8.debug.classinit.ClassInitializerEmpty;
import com.android.tools.r8.shaking.ProguardKeepRule;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
@@ -48,6 +50,7 @@
ImmutableList.of("SourceFile", "LineNumberTable"));
})
.addProgramFiles(DEBUGGEE_JAR)
+ .addProgramFiles(ToolHelper.getClassFileForTestClass(ClassInitializerEmpty.class))
.setMode(CompilationMode.DEBUG)
.setProguardMapOutputPath(proguardMapPath);
DebugTestConfig config;
@@ -82,19 +85,16 @@
this.backend = backend;
}
- /**
- * replica of {@link
- * com.android.tools.r8.debug.ClassInitializationTest#testBreakpointInEmptyClassInitializer}
- */
+ /** replica of {@link ClassInitializationTest#testBreakpointInEmptyClassInitializer} */
@Test
public void testBreakpointInEmptyClassInitializer() throws Throwable {
- final String CLASS = "ClassInitializerEmpty";
+ final String CLASS = typeName(ClassInitializerEmpty.class);
runDebugTest(
configs.get(backend),
CLASS,
breakpoint(CLASS, "<clinit>"),
run(),
- checkLine(TEST_FILE, 8),
+ checkLine(TEST_FILE, 9),
run());
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/ArtProfileCollisionAfterClassMergingRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/ArtProfileCollisionAfterClassMergingRewritingTest.java
new file mode 100644
index 0000000..15ed09c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/ArtProfileCollisionAfterClassMergingRewritingTest.java
@@ -0,0 +1,147 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.Lists;
+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 ArtProfileCollisionAfterClassMergingRewritingTest extends TestBase {
+
+ private static final ClassReference mainClassReference = Reference.classFromClass(Main.class);
+ private static final MethodReference mainMethodReference =
+ MethodReferenceUtils.mainMethod(Main.class);
+
+ private static final ClassReference fooClassReference = Reference.classFromClass(Foo.class);
+ private static final MethodReference helloMethodReference =
+ MethodReferenceUtils.methodFromMethod(Foo.class, "hello");
+
+ private static final ClassReference barClassReference = Reference.classFromClass(Bar.class);
+ private static final MethodReference worldMethodReference =
+ MethodReferenceUtils.methodFromMethod(Bar.class, "world");
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ MyArtProfileProvider artProfileProvider = new MyArtProfileProvider();
+ ArtProfileConsumerForTesting residualArtProfileConsumer = new ArtProfileConsumerForTesting();
+ testForR8(Backend.DEX)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertMergedInto(Foo.class, Bar.class).assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compile()
+ .inspect(inspector -> inspect(inspector, residualArtProfileConsumer));
+ }
+
+ private void inspect(
+ CodeInspector inspector, ArtProfileConsumerForTesting residualArtProfileConsumer) {
+ ClassSubject barClassSubject = inspector.clazz(Bar.class);
+ assertThat(barClassSubject, isPresentAndRenamed());
+
+ MethodSubject helloMethodSubject = barClassSubject.uniqueMethodWithName("hello");
+ assertThat(helloMethodSubject, isPresentAndRenamed());
+
+ MethodSubject worldMethodSubject = barClassSubject.uniqueMethodWithName("world");
+ assertThat(worldMethodSubject, isPresentAndRenamed());
+
+ assertTrue(residualArtProfileConsumer.finished);
+ assertEquals(
+ Lists.newArrayList(
+ mainClassReference,
+ mainMethodReference,
+ barClassSubject.getFinalReference(),
+ helloMethodSubject.getFinalReference(),
+ worldMethodSubject.getFinalReference()),
+ residualArtProfileConsumer.references);
+ assertEquals(
+ Lists.newArrayList(
+ ArtProfileClassRuleInfoImpl.empty(),
+ ArtProfileMethodRuleInfoImpl.empty(),
+ ArtProfileClassRuleInfoImpl.empty(),
+ ArtProfileMethodRuleInfoImpl.empty(),
+ ArtProfileMethodRuleInfoImpl.empty()),
+ residualArtProfileConsumer.infos);
+ }
+
+ static class MyArtProfileProvider implements ArtProfileProvider {
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ profileBuilder
+ .addClassRule(classRuleBuilder -> classRuleBuilder.setClassReference(mainClassReference))
+ .addMethodRule(
+ methodRuleBuilder -> methodRuleBuilder.setMethodReference(mainMethodReference))
+ .addClassRule(classRuleBuilder -> classRuleBuilder.setClassReference(fooClassReference))
+ .addMethodRule(
+ methodRuleBuilder -> methodRuleBuilder.setMethodReference(helloMethodReference))
+ .addClassRule(classRuleBuilder -> classRuleBuilder.setClassReference(barClassReference))
+ .addMethodRule(
+ methodRuleBuilder -> methodRuleBuilder.setMethodReference(worldMethodReference));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Foo.hello();
+ Bar.world();
+ }
+ }
+
+ static class Foo {
+
+ @NeverInline
+ static void hello() {
+ System.out.print("Hello");
+ }
+ }
+
+ static class Bar {
+
+ @NeverInline
+ static void world() {
+ System.out.println(", world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/ArtProfileConsumerForTesting.java b/src/test/java/com/android/tools/r8/profile/art/ArtProfileConsumerForTesting.java
new file mode 100644
index 0000000..2a0a041
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/ArtProfileConsumerForTesting.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.profile.art;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ArtProfileConsumerForTesting implements ArtProfileConsumer {
+
+ boolean finished;
+ List<Object> references = new ArrayList<>();
+ List<Object> infos = new ArrayList<>();
+
+ @Override
+ public ArtProfileRuleConsumer getRuleConsumer() {
+ return new ArtProfileRuleConsumer() {
+
+ @Override
+ public void acceptClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ references.add(classReference);
+ infos.add(classRuleInfo);
+ }
+
+ @Override
+ public void acceptMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ references.add(methodReference);
+ infos.add(methodRuleInfo);
+ }
+ };
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ finished = true;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/ArtProfilePassthroughTest.java b/src/test/java/com/android/tools/r8/profile/art/ArtProfilePassthroughTest.java
new file mode 100644
index 0000000..b45ba38
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/ArtProfilePassthroughTest.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+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 ArtProfilePassthroughTest extends TestBase {
+
+ enum ProviderStatus {
+ PENDING,
+ ACTIVE,
+ DONE
+ }
+
+ private static final ClassReference mainClassReference = Reference.classFromClass(Main.class);
+ private static final MethodReference mainMethodReference =
+ MethodReferenceUtils.mainMethod(Main.class);
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ ArtProfileProviderForTesting artProfileProvider = new ArtProfileProviderForTesting();
+ ArtProfileConsumerForTesting residualArtProfileConsumer =
+ new ArtProfileConsumerForTesting(artProfileProvider);
+ testForD8(Backend.DEX)
+ .addProgramClasses(Main.class)
+ // Add a profile provider and consumer that verifies that the profile is being provided at
+ // the same time it is being consumed.
+ .addArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer)
+ .release()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compile();
+
+ // Verify that the profile input was processed.
+ assertEquals(ProviderStatus.DONE, artProfileProvider.providerStatus);
+ assertTrue(residualArtProfileConsumer.finished);
+ assertEquals(
+ Lists.newArrayList(mainClassReference, mainMethodReference),
+ residualArtProfileConsumer.references);
+ }
+
+ static class ArtProfileProviderForTesting implements ArtProfileProvider {
+
+ ProviderStatus providerStatus = ProviderStatus.PENDING;
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ providerStatus = ProviderStatus.ACTIVE;
+ profileBuilder
+ .addClassRule(classRuleBuilder -> classRuleBuilder.setClassReference(mainClassReference))
+ .addMethodRule(
+ methodRuleBuilder -> methodRuleBuilder.setMethodReference(mainMethodReference));
+ providerStatus = ProviderStatus.DONE;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ }
+
+ static class ArtProfileConsumerForTesting implements ArtProfileConsumer {
+
+ private final ArtProfileProviderForTesting artProfileProvider;
+
+ List<Object> references = new ArrayList<>();
+ boolean finished;
+
+ ArtProfileConsumerForTesting(ArtProfileProviderForTesting artProfileProvider) {
+ this.artProfileProvider = artProfileProvider;
+ }
+
+ @Override
+ public ArtProfileRuleConsumer getRuleConsumer() {
+ // The compiler should not request to get the profile from the provider before getting the
+ // consumer.
+ assertTrue(artProfileProvider.providerStatus == ProviderStatus.PENDING);
+ return new ArtProfileRuleConsumer() {
+ @Override
+ public void acceptClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ // The consumer should only be receiving callbacks while the profile is being
+ // provided.
+ assertTrue(artProfileProvider.providerStatus == ProviderStatus.ACTIVE);
+ references.add(classReference);
+ }
+
+ @Override
+ public void acceptMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ // The consumer should only be receiving callbacks while the profile is being
+ // provided.
+ assertTrue(artProfileProvider.providerStatus == ProviderStatus.ACTIVE);
+ references.add(methodReference);
+ }
+ };
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ // The profile should be fully provided when all data is given to the consumer.
+ assertTrue(artProfileProvider.providerStatus == ProviderStatus.DONE);
+ finished = true;
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/ArtProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/ArtProfileRewritingTest.java
new file mode 100644
index 0000000..80bb288
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/ArtProfileRewritingTest.java
@@ -0,0 +1,134 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.Lists;
+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 ArtProfileRewritingTest extends TestBase {
+
+ private static final ClassReference mainClassReference = Reference.classFromClass(Main.class);
+ private static final MethodReference mainMethodReference =
+ MethodReferenceUtils.mainMethod(Main.class);
+
+ private static final ClassReference greeterClassReference =
+ Reference.classFromClass(Greeter.class);
+ private static final MethodReference greetMethodReference =
+ MethodReferenceUtils.methodFromMethod(Greeter.class, "greet");
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ ArtProfileProviderForTesting artProfileProvider = new ArtProfileProviderForTesting();
+ ArtProfileConsumerForTesting residualArtProfileConsumer = new ArtProfileConsumerForTesting();
+ testForR8(Backend.DEX)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer)
+ .enableInliningAnnotations()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compile()
+ .inspect(inspector -> inspect(inspector, residualArtProfileConsumer));
+ }
+
+ private void inspect(
+ CodeInspector inspector, ArtProfileConsumerForTesting residualArtProfileConsumer) {
+ ClassSubject greeterClassSubject = inspector.clazz(Greeter.class);
+ assertThat(greeterClassSubject, isPresentAndRenamed());
+
+ MethodSubject greetMethodSubject = greeterClassSubject.uniqueMethodWithName("greet");
+ assertThat(greetMethodSubject, isPresentAndRenamed());
+
+ assertTrue(residualArtProfileConsumer.finished);
+ assertEquals(
+ Lists.newArrayList(
+ mainClassReference,
+ mainMethodReference,
+ greeterClassSubject.getFinalReference(),
+ greetMethodSubject.getFinalReference()),
+ residualArtProfileConsumer.references);
+ assertEquals(
+ Lists.newArrayList(
+ ArtProfileClassRuleInfoImpl.empty(),
+ ArtProfileMethodRuleInfoImpl.builder().setIsStartup().build(),
+ ArtProfileClassRuleInfoImpl.empty(),
+ ArtProfileMethodRuleInfoImpl.builder().setIsHot().setIsPostStartup().build()),
+ residualArtProfileConsumer.infos);
+ }
+
+ static class ArtProfileProviderForTesting implements ArtProfileProvider {
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ profileBuilder
+ .addClassRule(classRuleBuilder -> classRuleBuilder.setClassReference(mainClassReference))
+ .addMethodRule(
+ methodRuleBuilder ->
+ methodRuleBuilder
+ .setMethodReference(mainMethodReference)
+ .setMethodRuleInfo(
+ methodRuleInfoBuilder -> methodRuleInfoBuilder.setIsStartup(true)))
+ .addClassRule(
+ classRuleBuilder -> classRuleBuilder.setClassReference(greeterClassReference))
+ .addMethodRule(
+ methodRuleBuilder ->
+ methodRuleBuilder
+ .setMethodReference(greetMethodReference)
+ .setMethodRuleInfo(
+ methodRuleInfoBuilder ->
+ methodRuleInfoBuilder.setIsHot(true).setIsPostStartup(true)));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Greeter.greet();
+ }
+ }
+
+ static class Greeter {
+
+ @NeverInline
+ static void greet() {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/DesugaredLibraryArtProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/DesugaredLibraryArtProfileRewritingTest.java
new file mode 100644
index 0000000..da88bc6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/DesugaredLibraryArtProfileRewritingTest.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.profile.art;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+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.origin.Origin;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Assume;
+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 DesugaredLibraryArtProfileRewritingTest extends DesugaredLibraryTestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public CompilationSpecification compilationSpecification;
+
+ @Parameter(2)
+ public LibraryDesugaringSpecification libraryDesugaringSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ DEFAULT_SPECIFICATIONS,
+ getJdk8Jdk11());
+ }
+
+ @Test
+ public void test() throws Throwable {
+ Assume.assumeTrue(libraryDesugaringSpecification.hasEmulatedInterfaceDesugaring(parameters));
+ ArtProfileProviderForTesting artProfileProvider = new ArtProfileProviderForTesting();
+ ArtProfileConsumerForTesting residualArtProfileConsumer = new ArtProfileConsumerForTesting();
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addL8ArtProfileForRewriting(artProfileProvider, residualArtProfileConsumer)
+ .compile()
+ .inspectL8(
+ inspector -> {
+ ClassSubject consumerClassSubject =
+ inspector.clazz(
+ libraryDesugaringSpecification.functionPrefix(parameters)
+ + ".util.function.Consumer");
+ assertThat(consumerClassSubject, isPresent());
+
+ ClassSubject streamClassSubject = inspector.clazz("j$.util.stream.Stream");
+ assertThat(streamClassSubject, isPresentAndNotRenamed());
+
+ MethodSubject forEachMethodSubject =
+ streamClassSubject.uniqueMethodWithName("forEach");
+ assertThat(
+ forEachMethodSubject,
+ isPresentAndRenamed(
+ compilationSpecification.isL8Shrink()
+ && libraryDesugaringSpecification
+ == LibraryDesugaringSpecification.JDK8));
+ assertEquals(
+ consumerClassSubject.asTypeSubject(), forEachMethodSubject.getParameter(0));
+
+ assertEquals(
+ Lists.newArrayList(forEachMethodSubject.getFinalReference()),
+ residualArtProfileConsumer.references);
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0");
+ }
+
+ class ArtProfileProviderForTesting implements ArtProfileProvider {
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder profileBuilder) {
+ MethodReference forEachMethodReference =
+ Reference.method(
+ Reference.classFromTypeName("j$.util.stream.Stream"),
+ "forEach",
+ ImmutableList.of(
+ Reference.classFromTypeName(
+ libraryDesugaringSpecification.functionPrefix(parameters)
+ + ".util.function.Consumer")),
+ null);
+ profileBuilder.addMethodRule(
+ methodRuleBuilder -> methodRuleBuilder.setMethodReference(forEachMethodReference));
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new ArrayList<>().stream().collect(Collectors.toList()).size());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticFromArtProfileTest.java b/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticFromArtProfileTest.java
new file mode 100644
index 0000000..ba3a623
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticFromArtProfileTest.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.profile.art.diagnostic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.ArtProfileBuilder;
+import com.android.tools.r8.profile.art.ArtProfileClassRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileConsumer;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfo;
+import com.android.tools.r8.profile.art.ArtProfileProvider;
+import com.android.tools.r8.profile.art.ArtProfileRuleConsumer;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.UTF8TextInputStream;
+import java.io.ByteArrayInputStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class HumanReadableArtProfileParserErrorDiagnosticFromArtProfileTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testD8() throws Exception {
+ testForD8()
+ .addProgramClasses(Main.class)
+ .addArtProfileForRewriting(createArtProfileProvider(), createArtProfileConsumer())
+ .release()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testR8() throws Exception {
+ testForR8(Backend.DEX)
+ .addProgramClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(createArtProfileProvider(), createArtProfileConsumer())
+ .release()
+ .setMinApi(AndroidApiLevel.LATEST)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics);
+ }
+
+ private ArtProfileProvider createArtProfileProvider() {
+ return new ArtProfileProvider() {
+
+ @Override
+ public void getArtProfile(ArtProfileBuilder artProfileBuilder) {
+ artProfileBuilder.addHumanReadableArtProfile(
+ new UTF8TextInputStream(new ByteArrayInputStream("INVALID1\nINVALID2".getBytes())),
+ ConsumerUtils.emptyConsumer());
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+ };
+ }
+
+ private ArtProfileConsumer createArtProfileConsumer() {
+ return new ArtProfileConsumer() {
+
+ @Override
+ public ArtProfileRuleConsumer getRuleConsumer() {
+ return new ArtProfileRuleConsumer() {
+
+ @Override
+ public void acceptClassRule(
+ ClassReference classReference, ArtProfileClassRuleInfo classRuleInfo) {
+ // Ignore.
+ }
+
+ @Override
+ public void acceptMethodRule(
+ MethodReference methodReference, ArtProfileMethodRuleInfo methodRuleInfo) {
+ // Ignore.
+ }
+ };
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ // Ignore.
+ }
+ };
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
+ diagnostics.assertErrorsMatch(
+ allOf(
+ diagnosticType(HumanReadableArtProfileParserErrorDiagnostic.class),
+ diagnosticMessage(
+ equalTo("Unable to parse rule at line 1 from ART profile: INVALID1"))),
+ allOf(
+ diagnosticType(HumanReadableArtProfileParserErrorDiagnostic.class),
+ diagnosticMessage(
+ equalTo("Unable to parse rule at line 2 from ART profile: INVALID2"))));
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticTest.java b/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticFromStartupProfileTest.java
similarity index 96%
rename from src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticTest.java
rename to src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticFromStartupProfileTest.java
index aee021b..8ee4777 100644
--- a/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/diagnostic/HumanReadableArtProfileParserErrorDiagnosticFromStartupProfileTest.java
@@ -28,7 +28,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class HumanReadableArtProfileParserErrorDiagnosticTest extends TestBase {
+public class HumanReadableArtProfileParserErrorDiagnosticFromStartupProfileTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteApi16Test.java b/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteApi16Test.java
index 4fbb7c3..e8ca0e3 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteApi16Test.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteApi16Test.java
@@ -44,6 +44,6 @@
.enableInliningAnnotations()
.setMinApi(AndroidApiLevel.J)
.run(parameters.getRuntime(), Main.class, String.valueOf(false))
- .assertSuccessWithOutputLines("OK", "OK");
+ .assertSuccessWithOutputLines("message", "java.lang.RuntimeException: cause message");
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteTest.java
index 2318414..f22208c 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertionerror/AssertionErrorRewriteTest.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.rewrite.assertionerror;
-import static com.android.tools.r8.ToolHelper.getDefaultAndroidJar;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverInline;
@@ -38,46 +37,33 @@
@Test public void d8() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForD8()
- .addLibraryFiles(getDefaultAndroidJar())
.addProgramClasses(Main.class)
.setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), Main.class, String.valueOf(expectCause))
- .assertSuccessWithOutputLines("OK", "OK");
+ .run(parameters.getRuntime(), Main.class)
+ // None of the VMs we have for testing is missing the two args constructor.
+ .assertSuccessWithOutputLines("message", "java.lang.RuntimeException: cause message");
}
@Test public void r8() throws Exception {
testForR8(parameters.getBackend())
- .addLibraryFiles(getDefaultAndroidJar())
.addProgramClasses(Main.class)
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class, String.valueOf(expectCause))
- .assertSuccessWithOutputLines("OK", "OK");
+ // None of the VMs we have for testing is missing the two args constructor.
+ .assertSuccessWithOutputLines("message", "java.lang.RuntimeException: cause message");
}
public static final class Main {
public static void main(String[] args) {
- boolean expectCause = Boolean.parseBoolean(args[0]);
-
Throwable expectedCause = new RuntimeException("cause message");
try {
throwAssertionError(expectedCause);
System.out.println("unreachable");
} catch (AssertionError e) {
- String message = e.getMessage();
- if (!message.equals("message")) {
- throw new RuntimeException("Incorrect AssertionError message: " + message);
- } else {
- System.out.println("OK");
- }
-
- Throwable cause = e.getCause();
- if (expectCause && cause != expectedCause) {
- throw new RuntimeException("Incorrect AssertionError cause", cause);
- } else {
- System.out.println("OK");
- }
+ System.out.println(e.getMessage());
+ System.out.println(e.getCause());
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
index 0cf3e9e..7fc0289 100644
--- a/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/enums/EnumOptimizationTest.java
@@ -51,7 +51,6 @@
options.enableEnumValueOptimization = enableOptimization;
options.enableEnumSwitchMapRemoval = enableOptimization;
options.enableEnumUnboxing = false;
- options.apiModelingOptions().enableApiCallerIdentification = true;
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexTest.java
new file mode 100644
index 0000000..6ff7000
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexTest.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.KeepConstantArguments;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+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 com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+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 ForwardingConstructorShakingOnDexTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.testing.enableRedundantConstructorBridgeRemoval = true)
+ .enableConstantArgumentAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "A(String)", "B", "B(String)", "C", "C(String)");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ boolean canHaveNonReboundConstructorInvoke =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertEquals(2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ assertEquals(
+ canHaveNonReboundConstructorInvoke ? 0 : 2,
+ bClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+
+ ClassSubject cClassSubject = inspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+ assertEquals(
+ canHaveNonReboundConstructorInvoke ? 0 : 2,
+ cClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new A();
+ new A("A(String)");
+ new B();
+ new B("B(String)");
+ new C();
+ new C("C(String)");
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ static class A {
+
+ A() {
+ if (this instanceof C) {
+ System.out.println("C");
+ } else if (this instanceof B) {
+ System.out.println("B");
+ } else {
+ System.out.println("A");
+ }
+ }
+
+ A(String string) {
+ System.out.println(string);
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ static class B extends A {
+
+ // These constructors simply forward the arguments to the parent constructor.
+ // They can be removed when compiling for dex and the API is above Dalvik.
+ B() {}
+
+ B(String string) {
+ super(string);
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ static class C extends B {
+
+ // Ditto.
+ C() {}
+
+ @KeepConstantArguments
+ C(String string) {
+ super(string);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexWithClassMergingTest.java b/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexWithClassMergingTest.java
new file mode 100644
index 0000000..c7f9a92
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ForwardingConstructorShakingOnDexWithClassMergingTest.java
@@ -0,0 +1,144 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+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 com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.Iterables;
+import java.util.Objects;
+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 ForwardingConstructorShakingOnDexWithClassMergingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> {
+ options.testing.enableRedundantConstructorBridgeRemoval = true;
+ options.testing.horizontalClassMergingTarget =
+ (appView, candidates, target) ->
+ Iterables.find(
+ candidates,
+ candidate -> candidate.getTypeName().equals(B.class.getTypeName()));
+ })
+ .addHorizontallyMergedClassesInspector(
+ inspector -> inspector.assertMergedInto(A.class, B.class).assertNoOtherClassesMerged())
+ .enableInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private boolean canHaveNonReboundConstructorInvoke() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject aSubClassSubject = inspector.clazz(ASub.class);
+ assertThat(aSubClassSubject, isPresent());
+ assertEquals(
+ canHaveNonReboundConstructorInvoke() ? 0 : 1, aSubClassSubject.allMethods().size());
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.print(new A(obfuscate("Hello")).getMessageFromA());
+ System.out.print(new ASub(obfuscate(", ")).getMessageFromA());
+ System.out.println(new B(obfuscate("world!")).getMessageFromB());
+ }
+
+ @NeverInline
+ private static String obfuscate(String str) {
+ return System.currentTimeMillis() > 0 ? str : "dead";
+ }
+
+ @NeverInline
+ public static void requireNonNull(Object o) {
+ Objects.requireNonNull(o);
+ }
+ }
+
+ @NoVerticalClassMerging
+ public static class A {
+
+ private final String msg;
+
+ // Will be merged with B.<init>(String). Since B is chosen as the merge target, a new
+ // B.<init>(String, int) method will be generated, and mappings will be created from
+ // A.<init>(String) -> B.init$A(String) and B.<init>(String) -> B.init$B(String).
+ public A(String msg) {
+ // So that A.<init>(String) and B.<init>(String) will not be considered identical during class
+ // merging. The indirection is to ensure that the API level of class A is 1.
+ Main.requireNonNull(msg);
+ this.msg = msg;
+ }
+
+ // Will be moved to B.<init>(String, String) by horizontal class merging, and then be optimized
+ // to B.<init>(String) by unused argument removal.
+ public A(String unused, String msg) {
+ this.msg = msg;
+ }
+
+ @NeverInline
+ public String getMessageFromA() {
+ return msg;
+ }
+ }
+
+ public static class ASub extends A {
+
+ // After horizontal class merging and unused argument removal, this is rewritten to target
+ // B.<init>(String). This constructor can therefore be eliminated by the redundant bridge
+ // removal phase.
+ public ASub(String msg) {
+ super("<unused>", msg);
+ }
+ }
+
+ public static class B {
+
+ private final String msg;
+
+ public B(String msg) {
+ this.msg = msg;
+ }
+
+ @NeverInline
+ public String getMessageFromB() {
+ return msg;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
new file mode 100644
index 0000000..f914a87
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+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 com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+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 RetainIndirectlyReferencedConstructorShakingOnDexTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.testing.enableRedundantConstructorBridgeRemoval = true)
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ boolean canHaveNonReboundConstructorInvoke =
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+
+ // A.<init> should be retained despite the fact that there is no invoke-direct in the program
+ // that directly targets A.<init> when B.<init> is removed.
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+ assertEquals(1, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ assertEquals(
+ canHaveNonReboundConstructorInvoke ? 0 : 1,
+ bClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ // This uses an invoke-direct instruction that targets B.<init>(). If we remove B.<init>()
+ // when compiling for dex, and keep the invoke-direct instruction targeting B.<init>(), it is
+ // important that tree shaking traces A.<init>(), by resolving the invoke in the Enqueuer,
+ // instead of trying to lookup <init>() directly on B. Otherwise, the program will fail with
+ // NSME.
+ System.out.println(new B());
+ }
+ }
+
+ @NoVerticalClassMerging
+ abstract static class A {
+
+ A() {
+ System.out.println("A");
+ }
+ }
+
+ @NoVerticalClassMerging
+ static class B extends A {
+
+ // This constructor simply forward the arguments to the parent constructor.
+ // It can be removed when compiling for dex and the API is above Dalvik.
+ B() {}
+
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/convertchecknotnull/ConvertCheckNotNullTest.java b/src/test/java/com/android/tools/r8/shaking/convertchecknotnull/ConvertCheckNotNullTest.java
index 5aad9aa..27f7735 100644
--- a/src/test/java/com/android/tools/r8/shaking/convertchecknotnull/ConvertCheckNotNullTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/convertchecknotnull/ConvertCheckNotNullTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeMatchers;
@@ -64,7 +65,10 @@
MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
assertThat(mainMethodSubject, isPresent());
assertEquals(
- 6,
+ parameters.isDexRuntime()
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.K)
+ ? 4
+ : 6,
mainMethodSubject
.streamInstructions()
.filter(
@@ -92,8 +96,16 @@
}
} else {
if (parameters.getDexRuntimeVersion().isEqualToOneOf(Version.V8_1_0, Version.DEFAULT)) {
- message4 =
- message5 = message6 = "Attempt to invoke a virtual method on a null object reference";
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.K)) {
+ message4 = message5 = "Attempt to invoke a virtual method on a null object reference";
+ message6 =
+ "Attempt to invoke virtual method 'java.lang.Class"
+ + " java.lang.Object.getClass()' on a null object reference";
+
+ } else {
+ message4 =
+ message5 = message6 = "Attempt to invoke a virtual method on a null object reference";
+ }
} else if (parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V5_1_1)) {
message4 =
message5 =
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index da3ef50..9b534e2 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -46,6 +46,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Assert;
@@ -1384,7 +1385,14 @@
opts.outline.maxSize = 3;
// Do not allow dead code elimination of the new-instance instructions.
- opts.apiModelingOptions().enableApiCallerIdentification = false;
+ opts.apiModelingOptions().disableApiModeling();
+ // Do not allow dead code elimination of the new-instance instructions. This can be
+ // achieved by not assuming that StringBuilder is present.
+ DexItemFactory dexItemFactory = opts.itemFactory;
+ opts.itemFactory.libraryTypesAssumedToBePresent =
+ new HashSet<>(dexItemFactory.libraryTypesAssumedToBePresent);
+ dexItemFactory.libraryTypesAssumedToBePresent.remove(
+ dexItemFactory.stringBuilderType);
});
AndroidApp originalApplication = buildApplicationWithAndroidJar(builder);
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 691a33d..b6e08bf 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
@@ -19,7 +19,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.profile.art.AlwaysTrueArtProfileRulePredicate;
import com.android.tools.r8.profile.art.ArtProfileBuilder;
import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
@@ -86,12 +85,12 @@
public StartupProfileBuilder addHumanReadableArtProfile(
TextInputStream textInputStream,
Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
+ // The ART profile parser never calls addHumanReadableArtProfile().
throw new Unreachable();
}
};
return ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
startupProfileBuilder,
- new AlwaysTrueArtProfileRulePredicate(),
syntheticToSyntheticContextGeneralization);
}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index e3ad2c8..c5bb96f 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -66,6 +66,10 @@
return syntheticClass(clazz, naming.API_MODEL_OUTLINE, id);
}
+ public static ClassReference syntheticApiOutlineClass(ClassReference classReference, int id) {
+ return syntheticClass(classReference, naming.API_MODEL_OUTLINE, id);
+ }
+
public static ClassReference syntheticBackportClass(Class<?> clazz, int id) {
return syntheticClass(clazz, naming.BACKPORT, id);
}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index b4c6e12..322c1d4 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1007,6 +1007,26 @@
}
@Override
+ public void visitFrame(
+ int type, int numLocal, Object[] local, int numStack, Object[] stack) {
+ for (int i = 0; i < numLocal; i++) {
+ Object object = local[i];
+ if (object instanceof String) {
+ local[i] = rewriteASMInternalTypeName((String) object);
+ }
+ i++;
+ }
+ for (int i = 0; i < numStack; i++) {
+ Object object = stack[i];
+ if (object instanceof String) {
+ stack[i] = rewriteASMInternalTypeName((String) object);
+ }
+ i++;
+ }
+ super.visitFrame(type, numLocal, local, numStack, stack);
+ }
+
+ @Override
public void visitLdcInsn(Object value) {
if (value instanceof Type) {
Type type = (Type) value;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index e3855b0..aeeb8f4 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.references.MethodReference;
import java.util.List;
public class AbsentMethodSubject extends MethodSubject {
@@ -82,6 +83,11 @@
}
@Override
+ public MethodReference getFinalReference() {
+ throw new Unreachable("Cannot get the final reference for an absent method");
+ }
+
+ @Override
public TypeSubject getParameter(int index) {
throw new Unreachable("Cannot get the parameter for an absent method");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 533fa55..3e28a56 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -134,6 +134,11 @@
}
@Override
+ public MethodReference getFinalReference() {
+ return dexMethod.getReference().asMethodReference();
+ }
+
+ @Override
public TypeSubject getParameter(int index) {
return new TypeSubject(codeInspector, getMethod().getParameter(index));
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index 69b8232..91a9a80 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -87,6 +87,11 @@
}
public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
+ return assertMergedInto(Reference.classFromClass(from), Reference.classFromClass(target));
+ }
+
+ public HorizontallyMergedClassesInspector assertMergedInto(
+ ClassReference from, ClassReference target) {
assertEquals(
horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from)), toDexType(target));
seen.add(toDexType(from).asClassReference());
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index a083636..ccc63b0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.references.MethodReference;
import com.google.common.collect.Streams;
import java.util.Iterator;
import java.util.List;
@@ -54,6 +55,8 @@
public abstract DexEncodedMethod getMethod();
+ public abstract MethodReference getFinalReference();
+
public abstract TypeSubject getParameter(int index);
public abstract List<TypeSubject> getParameters();
diff --git a/third_party/api_database/api_database.tar.gz.sha1 b/third_party/api_database/api_database.tar.gz.sha1
index 3a106aa..275471b 100644
--- a/third_party/api_database/api_database.tar.gz.sha1
+++ b/third_party/api_database/api_database.tar.gz.sha1
@@ -1 +1 @@
-da5a2b797563b19461f62db48bf1e45e2f86752f
\ No newline at end of file
+f081c538df68649432fa8e45ec511d43d5548396
\ No newline at end of file
diff --git a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1 b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
index 6d54f29..524abea 100644
--- a/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
+++ b/third_party/openjdk/desugar_jdk_libs_11.tar.gz.sha1
@@ -1 +1 @@
-cbd86c3d046d227e71e8157742b3a1f2e4abcd8e
\ No newline at end of file
+ae103f657ce219b839706397baac799745d46049
\ No newline at end of file
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py
index 004450b..63d02c6 100755
--- a/tools/archive_desugar_jdk_libs.py
+++ b/tools/archive_desugar_jdk_libs.py
@@ -75,6 +75,9 @@
'jdk11_nio': BASE_LIBRARY_NAME + '_jdk11_nio.zip'
}
+DESUGAR_JDK_LIBS_HASH_FILE = os.path.join(
+ defines.THIRD_PARTY, 'openjdk', 'desugar_jdk_libs_11', 'desugar_jdk_libs_hash')
+
def ParseOptions(argv):
result = optparse.OptionParser()
@@ -131,11 +134,11 @@
print('File available at: %s' %
destination.replace('gs://', 'https://storage.googleapis.com/', 1))
-def CloneDesugaredLibrary(github_account, checkout_dir):
+def CloneDesugaredLibrary(github_account, checkout_dir, desugar_jdk_libs_hash):
git_utils.GitClone(
'https://github.com/'
+ github_account + '/' + GITHUB_REPRO, checkout_dir)
- git_utils.GitCheckout('3a970cd008e944845a7b3d29a3b5a13123df11fe', checkout_dir)
+ git_utils.GitCheckout(desugar_jdk_libs_hash, checkout_dir)
def GetJavaEnv():
java_env = dict(os.environ, JAVA_HOME = jdk.GetJdk11Home())
@@ -251,10 +254,13 @@
raise Exception(path + ' does not exist or is not a directory')
def BuildAndUpload(options, variant):
+ desugar_jdk_libs_hash = ''
+ with open(DESUGAR_JDK_LIBS_HASH_FILE, 'r') as input_hash:
+ desugar_jdk_libs_hash = input_hash.readline()
if options.build_only:
with utils.TempDir() as checkout_dir:
- CloneDesugaredLibrary(options.github_account, checkout_dir)
- (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir, variant)
+ CloneDesugaredLibrary(options.github_account, checkout_dir, desugar_jdk_libs_hash)
+ (library_jar, maven_zip) = BuildDesugaredLibrary(checkout_dir, variant, desugar_jdk_libs_hash)
shutil.copyfile(
library_jar,
os.path.join(options.build_only, os.path.basename(library_jar)))
@@ -267,7 +273,7 @@
is_main = False
with utils.TempDir() as checkout_dir:
- CloneDesugaredLibrary(options.github_account, checkout_dir)
+ CloneDesugaredLibrary(options.github_account, checkout_dir, desugar_jdk_libs_hash)
version = GetVersion(os.path.join(checkout_dir, VERSION_MAP[variant]))
destination = archive.GetVersionDestination(
@@ -318,6 +324,7 @@
utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE)
+ utils.DownloadFromGoogleCloudStorage(utils.DESUGAR_JDK_LIBS_11_SHA_FILE)
for v in options.variant:
BuildAndUpload(options, v)
diff --git a/tools/cherry-pick.py b/tools/cherry-pick.py
index 08fdac3..a51ed00 100755
--- a/tools/cherry-pick.py
+++ b/tools/cherry-pick.py
@@ -9,15 +9,18 @@
import sys
import utils
+VERSION_FILE = 'src/main/java/com/android/tools/r8/Version.java'
+VERSION_PREFIX = 'String LABEL = "'
+
def parse_options():
parser = argparse.ArgumentParser(description='Release r8')
parser.add_argument('--branch',
metavar=('<branch>'),
help='Branch to cherry-pick to')
- parser.add_argument('--clean-checkout', '--clean_checkout',
+ parser.add_argument('--current-checkout', '--current_checkout',
default=False,
action='store_true',
- help='Perform cherry picks in a clean checkout')
+ help='Perform cherry picks into the current checkout')
parser.add_argument('--no-upload', '--no_upload',
default=False,
action='store_true',
@@ -32,12 +35,14 @@
# Checkout the branch.
subprocess.check_output(['git', 'checkout', args.branch])
- if (not args.clean_checkout):
+ if (args.current_checkout):
for i in range(len(args.hashes) + 1):
branch = 'cherry-%d' % (i + 1)
print('Deleting branch %s' % branch)
subprocess.run(['git', 'branch', branch, '-D'])
+ bugs = set()
+
count = 1
for hash in args.hashes:
branch = 'cherry-%d' % count
@@ -49,39 +54,67 @@
subprocess.run(['git', 'cherry-pick', hash])
- question = ('Ready to continue (cwd %s, will not upload to Gerrit)' % os.getcwd()
- if args.no_upload else
- 'Ready to upload %s (cwd %s)' % (branch, os.getcwd()))
- answer = input(question + ' [y/N]?')
- if answer != 'y':
- print('Aborting new branch for %s' % branch_version)
- sys.exit(1)
-
- if (not args.no_upload):
- subprocess.run(['git', 'cl', 'upload'])
+ commit_message = subprocess.check_output(['git', 'log', '--format=%B', '-n', '1', 'HEAD'])
+ commit_lines = [l.strip() for l in commit_message.decode('UTF-8').split('\n')]
+ for line in commit_lines:
+ if line.startswith('Bug: '):
+ normalized = line.replace('Bug: ', '').replace('b/', '')
+ if len(normalized) > 0:
+ bugs.add(normalized)
+ confirm_and_upload(branch, args)
count = count + 1
branch = 'cherry-%d' % count
subprocess.run(['git', 'new-branch', branch, '--upstream-current'])
- editor = os.environ.get('VISUAL')
- if not editor:
- editor = os.environ.get('EDITOR')
- if not editor:
- editor = 'vi'
+
+ old_version = 'unknown'
+ for line in open(VERSION_FILE, 'r'):
+ index = line.find(VERSION_PREFIX)
+ if index > 0:
+ index += len(VERSION_PREFIX)
+ subline = line[index:]
+ old_version = subline[:subline.index('"')]
+ break
+
+ new_version = 'unknown'
+ if old_version.find('.') > 0:
+ split_version = old_version.split('.')
+ new_version = '.'.join(split_version[:-1] + [str(int(split_version[-1]) + 1)])
+ subprocess.run(['sed', '-i', 's/%s/%s/' % (old_version, new_version), VERSION_FILE])
else:
- print("Opening src/main/java/com/android/tools/r8/Version.java" +
- " for version update with %" % editor)
- subprocess.run([editor, 'src/main/java/com/android/tools/r8/Version.java'])
- subprocess.run(['git', 'commit', '-a'])
- if (not args.no_upload):
- subprocess.run(['git', 'cl', 'upload'])
- if (args.clean_checkout):
+ editor = os.environ.get('VISUAL')
+ if not editor:
+ editor = os.environ.get('EDITOR')
+ if not editor:
+ editor = 'vi'
+ else:
+ print("Opening %s for version update with %s" % (VERSION_FILE, editor))
+ subprocess.run([editor, VERSION_FILE])
+
+ message = ("Version %s\n\n" % new_version)
+ for bug in sorted(bugs):
+ message += 'Bug: b/%s\n' % bug
+
+ subprocess.run(['git', 'commit', '-a', '-m', message])
+ confirm_and_upload(branch, args)
+ if (not args.current_checkout):
answer = input('Done, press enter to delete checkout in %s' % os.getcwd())
+def confirm_and_upload(branch, args):
+ question = ('Ready to continue (cwd %s, will not upload to Gerrit)' % os.getcwd()
+ if args.no_upload else
+ 'Ready to upload %s (cwd %s)' % (branch, os.getcwd()))
+ answer = input(question + ' [y/N]?')
+ if answer != 'y':
+ print('Aborting new branch for %s' % branch_version)
+ sys.exit(1)
+ if (not args.no_upload):
+ subprocess.run(['git', 'cl', 'upload', '--bypass-hooks'])
+
def main():
args = parse_options()
- if (args.clean_checkout):
+ if (not args.current_checkout):
with utils.TempDir() as temp:
print("Performing cherry-picking in %s" % temp)
subprocess.check_call(['git', 'clone', utils.REPO_SOURCE, temp])
diff --git a/tools/desugar_jdk_libs_repository.py b/tools/desugar_jdk_libs_repository.py
index 7cb7a19..08cb6c1 100755
--- a/tools/desugar_jdk_libs_repository.py
+++ b/tools/desugar_jdk_libs_repository.py
@@ -10,6 +10,7 @@
import shutil
import subprocess
import sys
+import urllib.request
import gradle
import utils
@@ -42,6 +43,13 @@
default=None,
metavar=('<path>'),
help='Use existing checkout of github.com/google/desugar_jdk_libs.')
+ parser.add_argument('--desugar-jdk-libs-revision', '--desugar_jdk_libs_revision',
+ default=None,
+ metavar=('<revision>'),
+ help='Revision of github.com/google/desugar_jdk_libs to use.')
+ parser.add_argument('--release-version', '--release_version',
+ metavar=('<version>'),
+ help='The desugared library release version to use. This will pull from the archived releases')
args = parser.parse_args()
return args
@@ -69,7 +77,8 @@
implementation = None
version_file = None
implementation_build_target = None
- implementation_build_output = None
+ implementation_maven_zip = None
+ release_archive_location = None
match args.variant:
case Variant.jdk8:
artifact = 'desugar_jdk_libs'
@@ -79,7 +88,8 @@
implementation = utils.DESUGAR_IMPLEMENTATION
version_file = 'VERSION.txt'
implementation_build_target = ':maven_release'
- implementation_build_output = join('bazel-bin', 'desugar_jdk_libs.zip')
+ implementation_maven_zip = 'desugar_jdk_libs.zip'
+ release_archive_location = 'desugar_jdk_libs'
case Variant.jdk11_legacy:
artifact = 'desugar_jdk_libs'
configuration_artifact = 'desugar_jdk_libs_configuration'
@@ -88,7 +98,8 @@
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')
+ implementation_maven_zip = 'desugar_jdk_libs_jdk11_legacy.zip'
+ release_archive_location = 'desugar_jdk_libs'
case Variant.jdk11_minimal:
artifact = 'desugar_jdk_libs_minimal'
configuration_artifact = 'desugar_jdk_libs_configuration_minimal'
@@ -97,7 +108,8 @@
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')
+ implementation_maven_zip = 'desugar_jdk_libs_jdk11_minimal.zip'
+ release_archive_location = 'desugar_jdk_libs_minimal'
case Variant.jdk11:
artifact = 'desugar_jdk_libs'
configuration_artifact = 'desugar_jdk_libs_configuration'
@@ -106,7 +118,8 @@
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')
+ implementation_maven_zip = 'desugar_jdk_libs_jdk11.zip'
+ release_archive_location = 'desugar_jdk_libs'
case Variant.jdk11_nio:
artifact = 'desugar_jdk_libs_nio'
configuration_artifact = 'desugar_jdk_libs_configuration_nio'
@@ -115,30 +128,43 @@
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')
+ implementation_maven_zip = 'desugar_jdk_libs_jdk11_nio.zip'
+ release_archive_location = 'desugar_jdk_libs_nio'
+ implementation_build_output = join('bazel-bin', implementation_maven_zip)
gradle.RunGradle([utils.R8])
- with utils.TempDir(delete=False) as tmp_dir:
- (name, version) = utils.desugar_configuration_name_and_version(configuration, False)
+ with utils.TempDir() as tmp_dir:
+ (name, configuration_version) = utils.desugar_configuration_name_and_version(configuration, False)
+ if (args.release_version != None and args.release_version != configuration_version):
+ raise Exception(
+ 'Configuration version %s is different for specified version %s'
+ % (configuration_version, version))
+ version = configuration_version
+ print("Name: %s" % name)
+ print("Version: %s" % version)
# 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):
- 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()
- for line in version_file_lines:
- if not line.startswith('#'):
- desugar_jdk_libs_version = line.strip()
- if (version != desugar_jdk_libs_version):
- raise Exception(
- "Version mismatch. Configuration has version '"
- + version
- + "', and desugar_jdk_libs has version '"
- + desugar_jdk_libs_version
- + "'")
+ if (not args.release_version and not use_existing_checkout):
+ subprocess.check_call(
+ ['git', 'clone', 'https://github.com/google/desugar_jdk_libs.git', checkout_dir])
+ if (args.desugar_jdk_libs_revision):
+ subprocess.check_call(
+ ['git', '-C', checkout_dir, 'checkout', args.desugar_jdk_libs_revision])
+ with utils.ChangedWorkingDirectory(checkout_dir):
+ with open(version_file) as version_file:
+ version_file_lines = version_file.readlines()
+ for line in version_file_lines:
+ if not line.startswith('#'):
+ desugar_jdk_libs_version = line.strip()
+ if (version != desugar_jdk_libs_version):
+ raise Exception(
+ "Version mismatch. Configuration has version '"
+ + version
+ + "', and desugar_jdk_libs has version '"
+ + desugar_jdk_libs_version
+ + "'")
# Build desugared library configuration.
print("Building desugared library configuration " + version)
@@ -160,27 +186,36 @@
'-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):
- subprocess.check_call([
- 'bazel',
- '--bazelrc=/dev/null',
- 'build',
- '--spawn_strategy=local',
- '--verbose_failures',
- implementation_build_target])
+ undesugared_if_needed = None
+ if not args.release_version:
+ # Build desugared library.
+ print("Building desugared library " + version)
+ with utils.ChangedWorkingDirectory(checkout_dir):
+ subprocess.check_call([
+ 'bazel',
+ '--bazelrc=/dev/null',
+ 'build',
+ '--spawn_strategy=local',
+ '--verbose_failures',
+ implementation_build_target])
- # Undesugar desugared library if needed.
- undesugared_if_needed = join(checkout_dir, implementation_build_output)
- if (args.variant == Variant.jdk11_minimal
- or args.variant == Variant.jdk11
- or args.variant == Variant.jdk11_nio):
- undesugared_if_needed = join(tmp_dir, 'undesugared.zip')
- archive_desugar_jdk_libs.Undesugar(
- str(args.variant),
- join(checkout_dir, implementation_build_output),
- version,
+ # Undesugar desugared library if needed.
+ undesugared_if_needed = join(checkout_dir, implementation_build_output)
+ if (args.variant == Variant.jdk11_minimal
+ or args.variant == Variant.jdk11
+ or args.variant == Variant.jdk11_nio):
+ undesugared_if_needed = join(tmp_dir, 'undesugared.zip')
+ archive_desugar_jdk_libs.Undesugar(
+ str(args.variant),
+ join(checkout_dir, implementation_build_output),
+ version,
+ undesugared_if_needed)
+ else:
+ # Download the already built and undesugared library from release archive.
+ undesugared_if_needed = join(tmp_dir, implementation_maven_zip)
+ urllib.request.urlretrieve(
+ ('https://storage.googleapis.com/r8-releases/raw/%s/%s/%s'
+ % (release_archive_location, version, implementation_maven_zip)),
undesugared_if_needed)
unzip_dir = join(tmp_dir, 'desugar_jdk_libs_unzipped')
@@ -227,6 +262,15 @@
def main():
args = parse_options()
+ if args.desugar_jdk_libs_checkout and args.release_version:
+ raise Exception(
+ 'Options --desugar-jdk-libs-checkout and --release-version are mutually exclusive')
+ if args.desugar_jdk_libs_revision and args.release_version:
+ raise Exception(
+ 'Options --desugar-jdk-libs-revision and --release-version are mutually exclusive')
+ if args.desugar_jdk_libs_checkout and args.desugar_jdk_libs_revision:
+ raise Exception(
+ 'Options --desugar-jdk-libs-checkout and --desugar-jdk-libs-revision are mutually exclusive')
if args.clear_repo:
shutil.rmtree(args.repo_root, ignore_errors=True)
utils.makedirs_if_needed(args.repo_root)
diff --git a/tools/desugar_jdk_libs_update.py b/tools/desugar_jdk_libs_update.py
new file mode 100755
index 0000000..2e751e9
--- /dev/null
+++ b/tools/desugar_jdk_libs_update.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import argparse
+import os
+from os.path import join
+import shutil
+import subprocess
+import sys
+
+import utils
+
+def GetGitHash(checkout_dir):
+ return subprocess.check_output(
+ ['git', '-C', checkout_dir, 'rev-parse', 'HEAD']).decode('utf-8').strip()
+
+def run(args):
+ with utils.TempDir() as tmp_dir:
+ 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):
+ subprocess.check_call(
+ ['git', 'clone', 'https://github.com/google/desugar_jdk_libs.git', checkout_dir])
+ if (args.desugar_jdk_libs_revision):
+ subprocess.check_call(
+ ['git', '-C', checkout_dir, 'checkout', args.desugar_jdk_libs_revision])
+ print("Building desugared library")
+ bazel = os.path.join(utils.BAZEL_TOOL, 'lib', 'bazel', 'bin', 'bazel')
+ with utils.ChangedWorkingDirectory(checkout_dir):
+ for target in [':desugar_jdk_libs_jdk11', '//jdk11/src:java_base_chm_only']:
+ subprocess.check_call([
+ bazel,
+ '--bazelrc=/dev/null',
+ 'build',
+ '--spawn_strategy=local',
+ '--verbose_failures',
+ target])
+
+ openjdk_dir = join('third_party', 'openjdk')
+ openjdk_subdir = 'desugar_jdk_libs_11'
+ dest_dir = join(openjdk_dir, openjdk_subdir)
+ src_dir = join(checkout_dir, 'bazel-bin', 'jdk11', 'src')
+
+ metadata_files = ('LICENSE', 'README.google')
+ for f in metadata_files:
+ shutil.copyfile(join(dest_dir, f), join(tmp_dir, f))
+ shutil.rmtree(dest_dir)
+ os.remove(join(openjdk_dir, openjdk_subdir + '.tar.gz'))
+ os.remove(join(openjdk_dir, openjdk_subdir + '.tar.gz.sha1'))
+ os.mkdir(dest_dir)
+ for s in [
+ (join(src_dir, 'd8_java_base_selected_with_addon.jar'),
+ join(dest_dir, 'desugar_jdk_libs.jar')),
+ (join(src_dir, 'java_base_chm_only.jar'),
+ join(dest_dir, 'desugar_jdk_libs_chm_only.jar'))]:
+ shutil.copyfile(s[0], s[1])
+ for f in metadata_files:
+ shutil.copyfile(join(tmp_dir, f), join(dest_dir, f))
+ desugar_jdk_libs_hash = os.path.join(dest_dir, 'desugar_jdk_libs_hash')
+ with open(desugar_jdk_libs_hash, 'w') as desugar_jdk_libs_hash_writer:
+ desugar_jdk_libs_hash_writer.write(GetGitHash(checkout_dir))
+
+ print('Now run')
+ print(' (cd %s; upload_to_google_storage.py -a --bucket r8-deps %s)'
+ % (openjdk_dir, openjdk_subdir))
+
+
+
+def main():
+ args = parse_options()
+ run(args)
+
+def parse_options():
+ parser = argparse.ArgumentParser(
+ description='Script for updating third_party/openjdk/desugar_jdk_libs*')
+ parser.add_argument('--desugar-jdk-libs-checkout', '--desugar_jdk_libs_checkout',
+ default=None,
+ metavar=('<path>'),
+ help='Use existing checkout of github.com/google/desugar_jdk_libs.')
+ parser.add_argument('--desugar-jdk-libs-revision', '--desugar_jdk_libs_revision',
+ default=None,
+ metavar=('<revision>'),
+ help='Revision of github.com/google/desugar_jdk_libs to use.')
+ args = parser.parse_args()
+ return args
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/test.py b/tools/test.py
index 5586802..a4e7b6e 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -255,7 +255,7 @@
os.makedirs(desugar_jdk_libs_dir)
print('Building desugared library.')
with utils.TempDir() as checkout_dir:
- archive_desugar_jdk_libs.CloneDesugaredLibrary('google', checkout_dir)
+ archive_desugar_jdk_libs.CloneDesugaredLibrary('google', checkout_dir, 'HEAD')
# Make sure bazel is extracted in third_party.
utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
diff --git a/tools/utils.py b/tools/utils.py
index 1830f53..c5fb4ce 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -109,6 +109,7 @@
BAZEL_TOOL = os.path.join(THIRD_PARTY, 'bazel')
JAVA8_SHA_FILE = os.path.join(THIRD_PARTY, 'openjdk', 'jdk8', 'linux-x86.tar.gz.sha1')
JAVA11_SHA_FILE = os.path.join(THIRD_PARTY, 'openjdk', 'jdk-11', 'linux.tar.gz.sha1')
+DESUGAR_JDK_LIBS_11_SHA_FILE = os.path.join(THIRD_PARTY, 'openjdk', 'desugar_jdk_libs_11.tar.gz.sha1')
IGNORE_WARNINGS_RULES = os.path.join(REPO_ROOT, 'src', 'test', 'ignorewarnings.rules')
ANDROID_HOME_ENVIROMENT_NAME = "ANDROID_HOME"