Merge commit '2c35b3a531fc115c044e04fa036855835ad33a9a' into dev-release
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 609f920..5adf5d1 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -1,5 +1,5 @@
{
- "identifier": "com.tools.android:desugar_jdk_libs_configuration:2.0.0",
+ "identifier": "com.tools.android:desugar_jdk_libs_configuration:2.0.2",
"configuration_format_version": 100,
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
@@ -38,7 +38,26 @@
"api_level_below_or_equal": 29,
"rewrite_prefix": {
"java.util.concurrent.Flow": "j$.util.concurrent.Flow"
- }
+ },
+ "wrapper_conversion": [
+ "java.util.concurrent.Flow$Publisher",
+ "java.util.concurrent.Flow$Subscriber",
+ "java.util.concurrent.Flow$Subscription"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 32,
+ "api_level_greater_or_equal": 24,
+ "emulate_interface": {
+ "java.util.Collection": "j$.util.Collection"
+ },
+ "dont_rewrite": [
+ "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
+ "java.util.Spliterator java.util.Collection#spliterator()",
+ "java.util.stream.Stream java.util.Collection#parallelStream()",
+ "java.util.stream.Stream java.util.Collection#stream()",
+ "void java.util.Collection#forEach(java.util.function.Consumer)"
+ ]
},
{
"api_level_below_or_equal": 23,
@@ -142,6 +161,12 @@
],
"program_flags": [
{
+ "api_level_below_or_equal": 33,
+ "amend_library_method": [
+ "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
+ ]
+ },
+ {
"api_level_below_or_equal": 32,
"api_level_greater_or_equal": 26,
"covariant_retarget_method": {
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
index 7d17a1d..c29c213 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_legacy.json
@@ -2,7 +2,7 @@
"configuration_format_version": 5,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.2.2",
+ "version": "1.2.3",
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
"support_all_callbacks_from_library": true,
@@ -37,7 +37,12 @@
"api_level_below_or_equal": 29,
"rewrite_prefix": {
"java.util.concurrent.Flow": "j$.util.concurrent.Flow"
- }
+ },
+ "wrapper_conversion": [
+ "java.util.concurrent.Flow$Publisher",
+ "java.util.concurrent.Flow$Subscriber",
+ "java.util.concurrent.Flow$Subscription"
+ ]
},
{
"api_level_below_or_equal": 23,
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
index d13b947..15939bc 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
@@ -1,5 +1,5 @@
{
- "identifier": "com.tools.android:desugar_jdk_libs_configuration_minimal:2.0.0",
+ "identifier": "com.tools.android:desugar_jdk_libs_configuration_minimal:2.0.2",
"configuration_format_version": 100,
"required_compilation_api_level": 24,
"synthesized_library_classes_package_prefix": "j$.",
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index e2f74e8..8d31143 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -1,5 +1,5 @@
{
- "identifier": "com.tools.android:desugar_jdk_libs_configuration_nio:2.0.0",
+ "identifier": "com.tools.android:desugar_jdk_libs_configuration_nio:2.0.2",
"configuration_format_version": 100,
"required_compilation_api_level": 30,
"synthesized_library_classes_package_prefix": "j$.",
@@ -52,7 +52,26 @@
"api_level_below_or_equal": 29,
"rewrite_prefix": {
"java.util.concurrent.Flow": "j$.util.concurrent.Flow"
- }
+ },
+ "wrapper_conversion": [
+ "java.util.concurrent.Flow$Publisher",
+ "java.util.concurrent.Flow$Subscriber",
+ "java.util.concurrent.Flow$Subscription"
+ ]
+ },
+ {
+ "api_level_below_or_equal": 32,
+ "api_level_greater_or_equal": 24,
+ "emulate_interface": {
+ "java.util.Collection": "j$.util.Collection"
+ },
+ "dont_rewrite": [
+ "boolean java.util.Collection#removeIf(java.util.function.Predicate)",
+ "java.util.Spliterator java.util.Collection#spliterator()",
+ "java.util.stream.Stream java.util.Collection#parallelStream()",
+ "java.util.stream.Stream java.util.Collection#stream()",
+ "void java.util.Collection#forEach(java.util.function.Consumer)"
+ ]
},
{
"api_level_below_or_equal": 25,
@@ -301,6 +320,12 @@
],
"program_flags": [
{
+ "api_level_below_or_equal": 33,
+ "amend_library_method": [
+ "public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
+ ]
+ },
+ {
"api_level_below_or_equal": 32,
"api_level_greater_or_equal": 26,
"covariant_retarget_method": {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d57d450..6ef6c416 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -370,6 +370,10 @@
assert appView.rootSet().verifyKeptItemsAreKept(appView);
appView.rootSet().checkAllRulesAreUsed(options);
+ if (options.apiModelingOptions().reportUnknownApiReferences) {
+ appView.apiLevelCompute().reportUnknownApiReferences();
+ }
+
if (options.proguardSeedsConsumer != null) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bytes);
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiDiagnostic.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDiagnostic.java
new file mode 100644
index 0000000..98471f2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiDiagnostic.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.Diagnostic;
+
+/** Base class for api related diagnostics. */
+public abstract class AndroidApiDiagnostic implements Diagnostic {}
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 1c61851..5a0bd81 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.androidapi;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.androidapi.ComputedApiLevel.KnownApiLevel;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
@@ -44,6 +45,10 @@
public abstract boolean isEnabled();
+ public void reportUnknownApiReferences() {
+ // Do nothing here.
+ }
+
public ComputedApiLevel computeApiLevelForDefinition(
DexMember<?, ?> reference, DexItemFactory factory, ComputedApiLevel unknownValue) {
return computeApiLevelForDefinition(reference.getReferencedBaseTypes(factory), unknownValue);
@@ -105,10 +110,12 @@
private final AndroidApiReferenceLevelCache cache;
private final ComputedApiLevel minApiLevel;
+ private final DiagnosticsHandler diagnosticsHandler;
public DefaultAndroidApiLevelCompute(AppView<?> appView) {
this.cache = AndroidApiReferenceLevelCache.create(appView, this);
this.minApiLevel = of(appView.options().getMinApiLevel());
+ this.diagnosticsHandler = appView.reporter();
}
@Override
@@ -131,5 +138,14 @@
DexReference reference, ComputedApiLevel unknownValue) {
return cache.lookup(reference, unknownValue);
}
+
+ @Override
+ public void reportUnknownApiReferences() {
+ cache
+ .getUnknownReferencesToReport()
+ .forEach(
+ reference ->
+ diagnosticsHandler.warning(new AndroidApiUnknownReferenceDiagnostic(reference)));
+ }
}
}
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 c4d2d5a..0e267e5 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -12,7 +12,9 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ConsumerUtils;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import java.util.List;
+import java.util.Set;
import java.util.function.BiConsumer;
public class AndroidApiReferenceLevelCache {
@@ -22,6 +24,10 @@
private final AppView<?> appView;
private final DexItemFactory factory;
+ // Collection of unknown references attempteed to be looked up.
+ private final Set<DexReference> unknownReferencesToReport = Sets.newConcurrentHashSet();
+ private final boolean reportUnknownReferences;
+
private AndroidApiReferenceLevelCache(
AppView<?> appView,
AndroidApiLevelCompute apiLevelCompute,
@@ -32,6 +38,7 @@
androidApiLevelDatabase =
new AndroidApiLevelHashingDatabaseImpl(
predefinedApiTypeLookupForHashing, appView.options(), appView.reporter());
+ reportUnknownReferences = appView.options().apiModelingOptions().reportUnknownApiReferences;
}
public static AndroidApiReferenceLevelCache create(
@@ -49,6 +56,10 @@
return new AndroidApiReferenceLevelCache(appView, apiLevelCompute, builder.build());
}
+ public Set<DexReference> getUnknownReferencesToReport() {
+ return unknownReferencesToReport;
+ }
+
public ComputedApiLevel lookupMax(
DexReference reference, ComputedApiLevel minApiLevel, ComputedApiLevel unknownValue) {
assert !minApiLevel.isNotSetApiLevel();
@@ -98,8 +109,12 @@
androidApiLevelDatabase::getTypeApiLevel,
androidApiLevelDatabase::getFieldApiLevel,
androidApiLevelDatabase::getMethodApiLevel);
- return (foundApiLevel == null)
- ? unknownValue
- : apiLevelCompute.of(foundApiLevel.max(appView.options().getMinApiLevel()));
+ if (foundApiLevel == null) {
+ if (reportUnknownReferences) {
+ unknownReferencesToReport.add(reference);
+ }
+ return unknownValue;
+ }
+ return apiLevelCompute.of(foundApiLevel.max(appView.options().getMinApiLevel()));
}
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnostic.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnostic.java
new file mode 100644
index 0000000..fba87df
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnostic.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.androidapi;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+
+@Keep
+public class AndroidApiUnknownReferenceDiagnostic extends AndroidApiDiagnostic {
+
+ private final DexReference reference;
+
+ AndroidApiUnknownReferenceDiagnostic(DexReference reference) {
+ this.reference = reference;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return reference.toSourceString() + " cannot be found in the api database.";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/errors/ProguardKeepRuleDiagnostic.java b/src/main/java/com/android/tools/r8/errors/ProguardKeepRuleDiagnostic.java
new file mode 100644
index 0000000..dcb6bb9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/ProguardKeepRuleDiagnostic.java
@@ -0,0 +1,9 @@
+// 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+
+/** Base class for diagnostics related to proguard keep rules. */
+public abstract class ProguardKeepRuleDiagnostic implements Diagnostic {}
diff --git a/src/main/java/com/android/tools/r8/errors/UnusedProguardKeepRuleDiagnostic.java b/src/main/java/com/android/tools/r8/errors/UnusedProguardKeepRuleDiagnostic.java
new file mode 100644
index 0000000..396f88b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/UnusedProguardKeepRuleDiagnostic.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.errors;
+
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+
+@Keep
+public class UnusedProguardKeepRuleDiagnostic extends ProguardKeepRuleDiagnostic {
+
+ private final ProguardConfigurationRule rule;
+
+ public UnusedProguardKeepRuleDiagnostic(ProguardConfigurationRule rule) {
+ this.rule = rule;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return rule.getOrigin();
+ }
+
+ @Override
+ public Position getPosition() {
+ return rule.getPosition();
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ return "Proguard configuration rule does not match anything: `" + rule + "`";
+ }
+}
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 ab044e3..751b635 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
@@ -359,10 +359,7 @@
workaroundAbstractMethodOnNonAbstractClassVerificationBug(executor);
DexApplication application = appView.appInfo().app();
D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executor);
- InterfaceProcessor interfaceProcessor =
- appView.options().isInterfaceMethodDesugaringEnabled()
- ? new InterfaceProcessor(appView)
- : null;
+ InterfaceProcessor interfaceProcessor = InterfaceProcessor.create(appView);
timing.begin("IR conversion");
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index aa8ca78..a42d6e5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -93,17 +93,15 @@
if (appView.options().enableTryWithResourcesDesugaring()) {
desugarings.add(new TwrInstructionDesugaring(appView));
}
- if (appView.options().isInterfaceMethodDesugaringEnabled()) {
- interfaceMethodRewriter =
- new InterfaceMethodRewriter(
- appView,
- SetUtils.newImmutableSetExcludingNullItems(
- alwaysThrowingInstructionDesugaring,
- backportedMethodRewriter,
- desugaredLibraryRetargeter));
+ interfaceMethodRewriter =
+ InterfaceMethodRewriter.create(
+ appView,
+ SetUtils.newImmutableSetExcludingNullItems(
+ alwaysThrowingInstructionDesugaring,
+ backportedMethodRewriter,
+ desugaredLibraryRetargeter));
+ if (interfaceMethodRewriter != null) {
desugarings.add(interfaceMethodRewriter);
- } else {
- interfaceMethodRewriter = null;
}
desugaredLibraryAPIConverter =
appView.typeRewriter.isRewriting()
@@ -121,7 +119,9 @@
desugarings.add(new LambdaInstructionDesugaring(appView));
desugarings.add(new ConstantDynamicInstructionDesugaring(appView));
desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
- if (appView.options().rewriteInvokeToPrivateInDesugar) {
+ if (appView.options().isGeneratingClassFiles()) {
+ // Nest desugaring has to be enabled to avoid other invokevirtual to private methods.
+ assert nestBasedAccessDesugaring != null;
desugarings.add(new InvokeToPrivateRewriter());
}
desugarings.add(new StringConcatInstructionDesugaring(appView));
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
index fb96fe6..fee2962 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedInterfaceDescriptor.java
@@ -9,6 +9,13 @@
import java.util.Map;
import java.util.Objects;
+/**
+ * An EmulatedInterfaceDescriptor describes how emulated interfaces are desugared. The
+ * emulatedMethods encode the emulated dispatch logic for default methods. Note that there is an
+ * implicit decision here: If interface method desugaring is enabled (< 24), the static methods in
+ * the emulated interface are going to be desugared, else they are left in place. This means static
+ * methods need to be manually retargeted when interface method desugaring is not enabled.
+ */
public class EmulatedInterfaceDescriptor implements SpecificationDescriptor {
private final DexType rewrittenType;
private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedMethods;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
index 567cc79..c6ea5b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LibraryValidator.java
@@ -28,6 +28,8 @@
levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;");
} else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.N)) {
levelType = app.dexItemFactory.createType("Ljava/util/function/Supplier;");
+ } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.T)) {
+ levelType = app.dexItemFactory.createType("Ljava/lang/invoke/VarHandle;");
} else {
app.options.reporter.warning(
"Unsupported requiredCompilationAPILevel: " + requiredCompilationAPILevel);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 16821510..eb710e9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.desugar.itf;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfStackInstruction;
@@ -29,6 +31,7 @@
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.BooleanUtils;
@@ -374,7 +377,12 @@
private final Map<DexProgramClass, List<ClassTypeSignature>> newExtraInterfaceSignatures =
new ConcurrentHashMap<>();
- ClassProcessor(AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) {
+ private final InterfaceMethodDesugaringMode desugaringMode;
+
+ ClassProcessor(
+ AppView<?> appView,
+ Predicate<ProgramMethod> isLiveMethod,
+ InterfaceMethodDesugaringMode desugaringMode) {
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.helper = new InterfaceDesugaringSyntheticHelper(appView);
@@ -382,6 +390,7 @@
!appView.options().canUseDefaultAndStaticInterfaceMethods()
&& !appView.options().machineDesugaredLibrarySpecification.isEmpty();
this.isLiveMethod = isLiveMethod;
+ this.desugaringMode = desugaringMode;
}
private boolean isLiveMethod(DexClassAndMethod method) {
@@ -440,6 +449,9 @@
assert iface.isInterface();
assert iface.superType == dexItemFactory.objectType;
assert !helper.isEmulatedInterface(iface.type);
+ if (desugaringMode == EMULATED_INTERFACE_ONLY) {
+ return SignaturesInfo.EMPTY;
+ }
// Add non-library default methods as well as those for desugared library classes.
if (!iface.isLibraryClass() || (needsLibraryInfo() && helper.isInDesugaredLibrary(iface))) {
MethodSignatures signatures = getDefaultMethods(iface);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index eda530c..4ac2b73 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -72,6 +72,23 @@
this.shouldIgnoreFromReportsPredicate = getShouldIgnoreFromReportsPredicate(appView);
}
+ public enum InterfaceMethodDesugaringMode {
+ ALL,
+ EMULATED_INTERFACE_ONLY,
+ NONE
+ }
+
+ public static InterfaceMethodDesugaringMode getInterfaceMethodDesugaringMode(
+ InternalOptions options) {
+ if (options.isInterfaceMethodDesugaringEnabled()) {
+ return InterfaceMethodDesugaringMode.ALL;
+ }
+ if (options.machineDesugaredLibrarySpecification.getEmulatedInterfaces().isEmpty()) {
+ return InterfaceMethodDesugaringMode.NONE;
+ }
+ return InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+ }
+
boolean isEmulatedInterface(DexType itf) {
return appView
.options()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
index 434238a..a5ec16a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Iterables;
@@ -28,11 +29,13 @@
AppView<?> appView,
Flavor flavour,
Predicate<ProgramMethod> isLiveMethod,
- InterfaceProcessor interfaceProcessor) {
+ InterfaceProcessor interfaceProcessor,
+ InterfaceMethodDesugaringMode desugaringMode) {
this.appView = appView;
this.flavour = flavour;
+ assert interfaceProcessor != null;
this.interfaceProcessor = interfaceProcessor;
- this.classProcessor = new ClassProcessor(appView, isLiveMethod);
+ this.classProcessor = new ClassProcessor(appView, isLiveMethod, desugaringMode);
}
private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 955183a..e91925d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -4,6 +4,11 @@
package com.android.tools.r8.ir.desugar.itf;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.ALL;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.NONE;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceMethodDesugaringMode;
+
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfInstruction;
@@ -38,6 +43,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
@@ -87,6 +93,9 @@
private final AppView<?> appView;
private final InternalOptions options;
final DexItemFactory factory;
+ // If this is true, this will desugar only default methods from emulated interfaces present in
+ // the emulated interface descriptor.
+ private final InterfaceMethodDesugaringMode desugaringMode;
private final InterfaceDesugaringSyntheticHelper helper;
// The emulatedMethod set is there to avoid doing the emulated look-up too often.
private final Set<DexString> emulatedMethods = Sets.newIdentityHashSet();
@@ -108,12 +117,28 @@
ExcludeDexResources
}
- public InterfaceMethodRewriter(
+ public static InterfaceMethodRewriter create(
AppView<?> appView, Set<CfInstructionDesugaring> precedingDesugarings) {
+ InterfaceMethodDesugaringMode desugaringMode =
+ getInterfaceMethodDesugaringMode(appView.options());
+ if (desugaringMode == NONE) {
+ return null;
+ }
+ return new InterfaceMethodRewriter(appView, precedingDesugarings, desugaringMode);
+ }
+
+ public InterfaceMethodRewriter(
+ AppView<?> appView,
+ Set<CfInstructionDesugaring> precedingDesugarings,
+ InterfaceMethodDesugaringMode desugaringMode) {
this.appView = appView;
this.precedingDesugarings = precedingDesugarings;
this.options = appView.options();
this.factory = appView.dexItemFactory();
+ assert desugaringMode == EMULATED_INTERFACE_ONLY || desugaringMode == ALL;
+ this.desugaringMode = desugaringMode;
+ assert desugaringMode == EMULATED_INTERFACE_ONLY
+ || appView.options().isInterfaceMethodDesugaringEnabled();
this.helper = new InterfaceDesugaringSyntheticHelper(appView);
initializeEmulatedInterfaceVariables();
}
@@ -274,15 +299,18 @@
.build();
}
// Continue with invoke type logic.
+ if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
+ return computeInvokeVirtualDispatch(holder, invoke, context);
+ }
+ if (desugaringMode == EMULATED_INTERFACE_ONLY) {
+ return DesugarDescription.nothing();
+ }
if (invoke.isInvokeStatic()) {
return computeInvokeStatic(holder, invoke, context);
}
if (invoke.isInvokeSpecial()) {
return computeInvokeSpecial(holder, invoke, context);
}
- if (invoke.isInvokeVirtual() || invoke.isInvokeInterface()) {
- return computeInvokeVirtualDispatch(holder, invoke, context);
- }
return DesugarDescription.nothing();
}
@@ -839,14 +867,16 @@
public InterfaceMethodProcessorFacade getPostProcessingDesugaringD8(
Flavor flavour, InterfaceProcessor interfaceProcessor) {
- return new InterfaceMethodProcessorFacade(appView, flavour, m -> true, interfaceProcessor);
+ return new InterfaceMethodProcessorFacade(
+ appView, flavour, m -> true, interfaceProcessor, desugaringMode);
}
public InterfaceMethodProcessorFacade getPostProcessingDesugaringR8(
Flavor flavour,
Predicate<ProgramMethod> isLiveMethod,
InterfaceProcessor interfaceProcessor) {
- return new InterfaceMethodProcessorFacade(appView, flavour, isLiveMethod, interfaceProcessor);
+ return new InterfaceMethodProcessorFacade(
+ appView, flavour, isLiveMethod, interfaceProcessor, desugaringMode);
}
private Origin getMethodOrigin(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 59dcd83..8d2c8c6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.ir.desugar.itf;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.EMULATED_INTERFACE_ONLY;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode.NONE;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceMethodDesugaringMode;
+
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.dex.code.DexInstruction;
@@ -25,6 +29,8 @@
import com.android.tools.r8.graph.MethodCollection;
import com.android.tools.r8.graph.NestedGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.InterfaceMethodDesugaringMode;
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
@@ -56,10 +62,21 @@
private final InterfaceDesugaringSyntheticHelper helper;
private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos =
new ConcurrentHashMap<>();
+ private final InterfaceMethodDesugaringMode desugaringMode;
- public InterfaceProcessor(AppView<?> appView) {
+ public static InterfaceProcessor create(AppView<?> appView) {
+ InterfaceMethodDesugaringMode desugaringMode =
+ getInterfaceMethodDesugaringMode(appView.options());
+ if (desugaringMode == NONE) {
+ return null;
+ }
+ return new InterfaceProcessor(appView, desugaringMode);
+ }
+
+ public InterfaceProcessor(AppView<?> appView, InterfaceMethodDesugaringMode desugaringMode) {
this.appView = appView;
helper = new InterfaceDesugaringSyntheticHelper(appView);
+ this.desugaringMode = desugaringMode;
}
public InterfaceDesugaringSyntheticHelper getHelper() {
@@ -72,6 +89,10 @@
if (!method.getHolder().isInterface()) {
return;
}
+ if (desugaringMode == EMULATED_INTERFACE_ONLY) {
+ processEmulatedInterfaceOnly(method);
+ return;
+ }
if (method.getDefinition().belongsToDirectPool()) {
processDirectInterfaceMethod(method, eventConsumer);
} else {
@@ -83,6 +104,25 @@
}
}
+ private void processEmulatedInterfaceOnly(ProgramMethod method) {
+ if (!appView.options().isDesugaredLibraryCompilation()) {
+ return;
+ }
+ if (method.getDefinition().belongsToDirectPool()) {
+ return;
+ }
+ if (helper.isEmulatedInterface(method.getHolderType())) {
+ EmulatedDispatchMethodDescriptor emulatedDispatchDescriptor =
+ helper.getEmulatedDispatchDescriptor(method.getHolder(), method);
+ if (emulatedDispatchDescriptor != null) {
+ processVirtualInterfaceMethod(method);
+ if (!interfaceMethodRemovalChangesApi(method)) {
+ getPostProcessingInterfaceInfo(method.getHolder()).setHasBridgesToRemove();
+ }
+ }
+ }
+ }
+
static ProgramMethod ensureCompanionMethod(
DexProgramClass iface,
DexString methodName,
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index c19aa5a..b9bc6bc 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -90,7 +90,7 @@
}
@Override
- ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
+ public ProguardMap.Builder setCurrentMapVersion(MapVersionMappingInformation mapVersion) {
mapVersions.add(mapVersion);
return this;
}
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 0b872f0..5b3e838 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SegmentTree;
import com.android.tools.r8.utils.ThrowingBiFunction;
@@ -43,9 +44,9 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
+import java.util.TreeMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
-import java.util.function.Function;
public class ComposingBuilder {
@@ -72,6 +73,11 @@
private final ComposingData committed = new ComposingData();
private ComposingData current;
+ private final InternalOptions options;
+
+ public ComposingBuilder(InternalOptions options) {
+ this.options = options;
+ }
public void compose(ClassNameMapper classNameMapper) throws MappingComposeException {
current = new ComposingData();
@@ -105,7 +111,7 @@
String originalName = classMapping.originalName;
String renamedName = classMapping.renamedName;
ComposingClassBuilder composingClassBuilder =
- new ComposingClassBuilder(originalName, renamedName, committed, current);
+ new ComposingClassBuilder(originalName, renamedName, committed, current, options);
ComposingClassBuilder duplicateMapping =
current.classBuilders.put(renamedName, composingClassBuilder);
if (duplicateMapping != null) {
@@ -411,13 +417,19 @@
private final ComposingData current;
private final ComposingClassBuilder committedPreviousClassBuilder;
+ private final InternalOptions options;
private ComposingClassBuilder(
- String originalName, String renamedName, ComposingData committed, ComposingData current) {
+ String originalName,
+ String renamedName,
+ ComposingData committed,
+ ComposingData current,
+ InternalOptions options) {
this.originalName = originalName;
this.renamedName = renamedName;
this.current = current;
this.committed = committed;
+ this.options = options;
committedPreviousClassBuilder = committed.classBuilders.get(originalName);
}
@@ -502,7 +514,8 @@
// The original can be discarded if it no longer exists or if the method is
// non-throwing.
if (mappedRangeResult.startOriginalPosition > 0
- && (originalRange == null || !newMappedRange.originalRange.isPreamble())) {
+ && (originalRange == null || !newMappedRange.originalRange.isPreamble())
+ && !options.mappingComposeOptions().allowNonExistingOriginalRanges) {
throw new MappingComposeException(
"Could not find original starting position of '"
+ mappedRangeResult.lastRange
@@ -657,7 +670,7 @@
// 6:10:void caller():19:19 -> y
// ...
Box<Range> originalRange = new Box<>();
- Function<Integer, List<MappedRange>> mappedRangesForPosition =
+ ExistingMapping mappedRangesForPosition =
getExistingMapping(
existingRanges, (start, end) -> originalRange.set(new Range(start, end)));
List<MappedRange> newComposedRanges = new ArrayList<>();
@@ -682,7 +695,7 @@
assert newRange.minifiedRange != null;
// First check if the original range matches the existing minified range.
List<MappedRange> existingMappedRanges =
- mappedRangesForPosition.apply(
+ mappedRangesForPosition.getMappedRangesForPosition(
newRange.getFirstPositionOfOriginalRange(NO_RANGE_FROM));
if (existingMappedRanges == null) {
// If we cannot lookup the original position because it has been removed we compose with
@@ -700,8 +713,11 @@
"Unexpected missing original position for '" + newRange + "'.");
}
}
- // Otherwise, we have found an existing range for the original position.
- if (ListUtils.last(existingMappedRanges).minifiedRange.equals(newRange.originalRange)) {
+ // We have found an existing range for the original position.
+ MappedRange lastExistingMappedRange = ListUtils.last(existingMappedRanges);
+ // If the existing mapped minified range is equal to the original range of the new range
+ // then we have a perfect mapping that we can translate directly.
+ if (lastExistingMappedRange.minifiedRange.equals(newRange.originalRange)) {
computeComposedMappedRange(
newComposedRanges,
newRange,
@@ -720,14 +736,17 @@
position <= newRange.minifiedRange.to;
position++) {
List<MappedRange> existingMappedRangesForPosition =
- mappedRangesForPosition.apply(newRange.getOriginalLineNumber(position));
- if (existingMappedRangesForPosition == null) {
- throw new MappingComposeException(
- "Unexpected missing original position for '" + newRange + "'.");
+ mappedRangesForPosition.getMappedRangesForPosition(
+ newRange.getOriginalLineNumber(position));
+ MappedRange lastExistingMappedRangeForPosition = null;
+ if (existingMappedRangesForPosition != null) {
+ lastExistingMappedRangeForPosition =
+ ListUtils.last(existingMappedRangesForPosition);
}
- if (!ListUtils.last(existingMappedRanges)
- .minifiedRange
- .equals(ListUtils.last(existingMappedRangesForPosition).minifiedRange)) {
+ if (lastExistingMappedRangeForPosition == null
+ || !lastExistingMappedRange.minifiedRange.equals(
+ lastExistingMappedRangeForPosition.minifiedRange)) {
+ // We have seen an existing range we have to compute a splitting for.
computeComposedMappedRange(
newComposedRanges,
newRange,
@@ -735,8 +754,49 @@
computedOutlineInformation,
lastStartingMinifiedFrom,
position - 1);
+ // Advance the last starting position to this point and advance the existing mapped
+ // ranges for this position.
lastStartingMinifiedFrom = position;
- existingMappedRanges = existingMappedRangesForPosition;
+ if (existingMappedRangesForPosition == null) {
+ if (!options.mappingComposeOptions().allowNonExistingOriginalRanges) {
+ throw new MappingComposeException(
+ "Unexpected missing original position for '" + newRange + "'.");
+ }
+ // We are at the first position of a hole. If we have existing ranges:
+ // 1:1:void foo():41:41 -> a
+ // 9:9:void foo():49:49 -> a
+ // We may have a new range that is:
+ // 21:29:void foo():1:9
+ // We end up here at position 2 and we have already committed
+ // 21:21:void foo():41:41.
+ // We then introduce a "fake" normal range to simulate the result of retracing one
+ // after the other to end up with:
+ // 21:21:void foo():41:41
+ // 22:28:void foo():2:8
+ // 29:29:void foo():49:49.
+ int startOriginalPosition = newRange.getOriginalLineNumber(position);
+ Integer endOriginalPosition =
+ mappedRangesForPosition.getCeilingForPosition(position);
+ if (endOriginalPosition == null) {
+ endOriginalPosition = newRange.getLastPositionOfOriginalRange() + 1;
+ }
+ Range newOriginalRange =
+ new Range(startOriginalPosition, endOriginalPosition - 1);
+ MappedRange nonExistingMappedRange =
+ new MappedRange(
+ newOriginalRange,
+ lastExistingMappedRange.getOriginalSignature().asMethodSignature(),
+ newOriginalRange,
+ lastExistingMappedRange.renamedName);
+ nonExistingMappedRange.setResidualSignatureInternal(
+ lastExistingRange.getResidualSignatureInternal());
+ lastExistingMappedRange = nonExistingMappedRange;
+ existingMappedRanges = Collections.singletonList(nonExistingMappedRange);
+ position += (endOriginalPosition - startOriginalPosition) - 1;
+ } else {
+ lastExistingMappedRange = lastExistingMappedRangeForPosition;
+ existingMappedRanges = existingMappedRangesForPosition;
+ }
}
}
computeComposedMappedRange(
@@ -781,14 +841,20 @@
return newComposedRanges;
}
+ public interface ExistingMapping {
+
+ Integer getCeilingForPosition(int i);
+
+ List<MappedRange> getMappedRangesForPosition(int i);
+ }
+
/***
* Builds a position to mapped ranges for mappings for looking up all mapped ranges for a given
* position.
*/
- private Function<Integer, List<MappedRange>> getExistingMapping(
+ private ExistingMapping getExistingMapping(
List<MappedRange> existingRanges, BiConsumer<Integer, Integer> positions) {
- Int2ReferenceMap<List<MappedRange>> mappedRangesForPosition =
- new Int2ReferenceOpenHashMap<>();
+ TreeMap<Integer, List<MappedRange>> mappedRangesForPosition = new TreeMap<>();
List<MappedRange> currentRangesForPosition = new ArrayList<>();
int startExisting = NO_RANGE_FROM;
int endExisting = NO_RANGE_FROM;
@@ -818,12 +884,19 @@
if (startExisting > NO_RANGE_FROM) {
positions.accept(startExisting, endExisting);
}
- if (isCatchAll) {
- List<MappedRange> finalMappedRangeList = currentRangesForPosition;
- return ignored -> finalMappedRangeList;
- } else {
- return mappedRangesForPosition::get;
- }
+ boolean finalIsCatchAll = isCatchAll;
+ List<MappedRange> finalCurrentRangesForPosition = currentRangesForPosition;
+ return new ExistingMapping() {
+ @Override
+ public Integer getCeilingForPosition(int i) {
+ return finalIsCatchAll ? i : mappedRangesForPosition.ceilingKey(i);
+ }
+
+ @Override
+ public List<MappedRange> getMappedRangesForPosition(int i) {
+ return finalIsCatchAll ? finalCurrentRangesForPosition : mappedRangesForPosition.get(i);
+ }
+ };
}
private void computeComposedMappedRange(
@@ -980,7 +1053,8 @@
public ComposingClassBuilder commit(ComposingClassBuilder classBuilder)
throws MappingComposeException {
ComposingClassBuilder newClassBuilder =
- new ComposingClassBuilder(originalName, classBuilder.renamedName, committed, null);
+ new ComposingClassBuilder(
+ originalName, classBuilder.renamedName, committed, null, options);
composeMappingInformation(
classBuilder.additionalMappingInfo,
additionalMappingInfo,
diff --git a/src/main/java/com/android/tools/r8/naming/MappingComposer.java b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
index 583bc05..636e699 100644
--- a/src/main/java/com/android/tools/r8/naming/MappingComposer.java
+++ b/src/main/java/com/android/tools/r8/naming/MappingComposer.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.naming;
+import com.android.tools.r8.utils.InternalOptions;
+
/**
* MappingComposer is a utility to do composition of mapping files to map line numbers correctly
* when having shrunken input that will end up using DEX PC mappings.
@@ -11,8 +13,13 @@
public class MappingComposer {
public static String compose(ClassNameMapper... classNameMappers) throws MappingComposeException {
+ return compose(new InternalOptions(), classNameMappers);
+ }
+
+ public static String compose(InternalOptions options, ClassNameMapper... classNameMappers)
+ throws MappingComposeException {
assert classNameMappers.length > 0;
- ComposingBuilder builder = new ComposingBuilder();
+ ComposingBuilder builder = new ComposingBuilder(options);
for (ClassNameMapper classNameMapper : classNameMappers) {
builder.compose(classNameMapper);
}
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 9f3e5ed..043bb4c 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -87,6 +87,15 @@
this.position = position;
}
+ /** This is used internally in google3. */
+ @Deprecated
+ public Signature getRenamedSignature() {
+ if (residualSignature != null) {
+ return residualSignature;
+ }
+ return getOriginalSignature().asRenamed(renamedName);
+ }
+
@Override
public Signature getOriginalSignature() {
return signature;
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 74202d3..1b77e82 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -510,7 +510,7 @@
liveFields = new LiveFieldsSet(graphReporter::registerField);
if (mode.isInitialTreeShaking()) {
desugaring = CfInstructionDesugaringCollection.create(appView, appView.apiLevelCompute());
- interfaceProcessor = new InterfaceProcessor(appView);
+ interfaceProcessor = InterfaceProcessor.create(appView);
} else {
desugaring = CfInstructionDesugaringCollection.empty();
interfaceProcessor = null;
@@ -4055,6 +4055,7 @@
// Move the pending methods and mark them live and ready for tracing.
for (ProgramMethod method : pendingMethodMove) {
+ assert interfaceProcessor != null;
ProgramMethod companion =
interfaceProcessor
.getHelper()
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 fddac9e..9360393 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.errors.AssumeValuesMissingStaticFieldDiagnostic;
import com.android.tools.r8.errors.InlinableStaticFinalFieldPreconditionDiagnostic;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.errors.UnusedProguardKeepRuleDiagnostic;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
@@ -1884,9 +1885,7 @@
}
}
if (!rule.isUsed() && options.testing.reportUnusedProguardConfigurationRules) {
- String message = "Proguard configuration rule does not match anything: `" + rule + "`";
- StringDiagnostic diagnostic = new StringDiagnostic(message, rule.getOrigin());
- options.reporter.info(diagnostic);
+ options.reporter.info(new UnusedProguardKeepRuleDiagnostic(rule));
}
}
}
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 3939dd5..f07a328 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -69,6 +69,7 @@
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
+import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
import com.android.tools.r8.origin.Origin;
@@ -422,11 +423,6 @@
public boolean createSingletonsForStatelessLambdas =
System.getProperty("com.android.tools.r8.createSingletonsForStatelessLambdas") != null;
- // Flag to control the representation of stateless lambdas.
- // See b/222081665 for context.
- public boolean rewriteInvokeToPrivateInDesugar =
- System.getProperty("com.android.tools.r8.rewriteInvokeToPrivateInDesugar") != null;
-
// Flag to allow record annotations in DEX. See b/231930852 for context.
public boolean emitRecordAnnotationsInDex =
System.getProperty("com.android.tools.r8.emitRecordAnnotationsInDex") != null;
@@ -832,6 +828,7 @@
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
+ private final MappingComposeOptions mappingComposeOptions = new MappingComposeOptions();
private final ArtProfileOptions artProfileOptions = new ArtProfileOptions();
private final StartupOptions startupOptions = new StartupOptions();
private final StartupInstrumentationOptions startupInstrumentationOptions =
@@ -879,6 +876,10 @@
return apiModelTestingOptions;
}
+ public MappingComposeOptions mappingComposeOptions() {
+ return mappingComposeOptions;
+ }
+
public DesugarSpecificOptions desugarSpecificOptions() {
return desugarSpecificOptions;
}
@@ -1732,6 +1733,14 @@
}
}
+ public static class MappingComposeOptions {
+ // TODO(b/241763080): Remove when enabled.
+ public boolean enableExperimentalMappingComposition = false;
+ // TODO(b/247136434): Disable for internal builds.
+ public boolean allowNonExistingOriginalRanges = true;
+ public Consumer<ClassNameMapper> generatedClassNameMapperConsumer = null;
+ }
+
public static class ApiModelTestingOptions {
// Flag to specify if we should load the database or not. The api database is used for
@@ -1750,6 +1759,8 @@
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public boolean enableOutliningOfMethods =
System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+ public boolean reportUnknownApiReferences =
+ System.getProperty("com.android.tools.r8.reportUnknownApiReferences") != null;
// TODO(b/232823652): Enable when we can compute the offset correctly.
public boolean useMemoryMappedByteBuffer = false;
diff --git a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
index de478a0..993ca5f 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.debuginfo.DebugRepresentation.DebugRepresentationPredicate;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
@@ -19,6 +20,8 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.MappingComposeException;
+import com.android.tools.r8.naming.MappingComposer;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -27,6 +30,7 @@
import com.android.tools.r8.utils.OriginalSourceFiles;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.positions.MappedPositionToClassNameMapperBuilder.MappedPositionToClassNamingBuilder;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
@@ -46,6 +50,21 @@
timing.begin("Line number remapping");
ClassNameMapper mapper = run(appView, inputApp, originalSourceFiles, representation);
timing.end();
+ if (appView.options().mappingComposeOptions().generatedClassNameMapperConsumer != null) {
+ appView.options().mappingComposeOptions().generatedClassNameMapperConsumer.accept(mapper);
+ }
+ if (appView.options().mappingComposeOptions().enableExperimentalMappingComposition) {
+ timing.begin("Proguard map composition");
+ try {
+ mapper =
+ ClassNameMapper.mapperFromStringWithExperimental(
+ MappingComposer.compose(
+ appView.options(), appView.appInfo().app().getProguardMap(), mapper));
+ } catch (IOException | MappingComposeException e) {
+ throw new CompilationError(e.getMessage(), e);
+ }
+ timing.end();
+ }
timing.begin("Write proguard map");
ProguardMapId mapId = ProguardMapSupplier.create(mapper, appView.options()).writeProguardMap();
timing.end();
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 2b51ed8..b87a2b5 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -66,7 +66,7 @@
private final OriginalSourceFiles originalSourceFiles;
private final AppView<?> appView;
- private final ClassNameMapper.Builder classNameMapperBuilder = ClassNameMapper.builder();
+ private final ClassNameMapper.Builder classNameMapperBuilder;
private final Map<DexMethod, OutlineFixupBuilder> outlinesToFix = new IdentityHashMap<>();
private final Map<DexType, String> prunedInlinedClasses = new IdentityHashMap<>();
@@ -79,6 +79,9 @@
AppView<?> appView, OriginalSourceFiles originalSourceFiles) {
this.appView = appView;
this.originalSourceFiles = originalSourceFiles;
+ classNameMapperBuilder = ClassNameMapper.builder();
+ classNameMapperBuilder.setCurrentMapVersion(
+ appView.options().getMapFileVersion().toMapVersionMappingInformation());
}
public static int getMaxLineNumber() {
diff --git a/src/test/examplesJava11/collectiontoarray/Main.java b/src/test/examplesJava11/collectiontoarray/Main.java
new file mode 100644
index 0000000..91bbd8b
--- /dev/null
+++ b/src/test/examplesJava11/collectiontoarray/Main.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 collectiontoarray;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Main {
+ public static void main(String[] args) {
+ List<String> list = new ArrayList<>();
+ list.add("one");
+ list.add("two");
+ // This default method was added in Android T.
+ String[] toArray = list.toArray(String[]::new);
+ System.out.println(Arrays.toString(toArray));
+ }
+}
diff --git a/src/test/examplesJava9/flow/FlowExample.java b/src/test/examplesJava9/flow/FlowExample.java
index 4b02fca..caa0c24 100644
--- a/src/test/examplesJava9/flow/FlowExample.java
+++ b/src/test/examplesJava9/flow/FlowExample.java
@@ -21,7 +21,7 @@
oneShotPublisher.awaitPublishing();
}
- static class OneShotPublisher implements Publisher<Boolean> {
+ public static class OneShotPublisher implements Publisher<Boolean> {
private final ForkJoinPool executor = new ForkJoinPool(); // daemon-based
diff --git a/src/test/examplesJava9/flow/FlowExample2.java b/src/test/examplesJava9/flow/FlowExample2.java
new file mode 100644
index 0000000..5a69730
--- /dev/null
+++ b/src/test/examplesJava9/flow/FlowExample2.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 flow;
+
+import java.util.concurrent.Flow.Publisher;
+
+public class FlowExample2 extends flowlib.FlowLib {
+
+ public static void main(String[] args) {
+ System.out.println(new FlowExample2().getPublisher().getClass().getSimpleName());
+ }
+
+ public Publisher<?> getPublisher() {
+ return new FlowExample.OneShotPublisher();
+ }
+}
diff --git a/src/test/examplesJava9/flowlib/FlowLib.java b/src/test/examplesJava9/flowlib/FlowLib.java
new file mode 100644
index 0000000..f9fa74c
--- /dev/null
+++ b/src/test/examplesJava9/flowlib/FlowLib.java
@@ -0,0 +1,11 @@
+// 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 flowlib;
+
+import java.util.concurrent.Flow.Publisher;
+
+public abstract class FlowLib {
+ public abstract Publisher<?> getPublisher();
+}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 9b80d3b..d91646a 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.benchmarks.BenchmarkResults;
-import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
@@ -119,7 +118,8 @@
}
String proguardMap =
Files.exists(mapFile) ? FileUtils.readTextFile(mapFile, Charsets.UTF_8) : "";
- return new ProguardTestCompileResult(getState(), outJar, getMinApiLevel(), proguardMap);
+ return new ProguardTestCompileResult(
+ result, getState(), outJar, getMinApiLevel(), proguardMap);
} catch (IOException e) {
throw new CompilationFailedException(e);
}
diff --git a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
index 01cc662..bf1ad43 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestCompileResult.java
@@ -14,15 +14,18 @@
public class ProguardTestCompileResult
extends TestCompileResult<ProguardTestCompileResult, ProguardTestRunResult> {
+ private final ProcessResult result;
private final Path outputJar;
private final String proguardMap;
- ProguardTestCompileResult(TestState state, Path outputJar, int minApiLevel, String proguardMap) {
+ ProguardTestCompileResult(
+ ProcessResult result, TestState state, Path outputJar, int minApiLevel, String proguardMap) {
super(
state,
AndroidApp.builder().addProgramFiles(outputJar).build(),
minApiLevel,
OutputMode.ClassFile);
+ this.result = result;
this.outputJar = outputJar;
this.proguardMap = proguardMap;
}
@@ -48,12 +51,12 @@
@Override
public String getStdout() {
- throw new Unimplemented("Unexpected attempt to access stdout from Proguard");
+ return result.stdout;
}
@Override
public String getStderr() {
- throw new Unimplemented("Unexpected attempt to access stderr from Proguard");
+ return result.stderr;
}
@Override
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
index 66dbfec..cf69ff4 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -8,13 +8,14 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DiagnosticsMatcher;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestShrinkerBuilder;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.List;
import org.junit.Test;
@@ -47,7 +48,12 @@
testForD8()
.addProgramFiles(kotlinTestParameters.getCompiler().getKotlinStdlibJar())
.setMinApi(parameters.getApiLevel())
- .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages);
+ // TODO(b/248244467): Remove if fixed.
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertAllWarningsMatch(
+ DiagnosticsMatcher.diagnosticType(
+ InterfaceDesugarMissingTypeDiagnostic.class)));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
index 4055ea0..3ba706d 100644
--- a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import org.junit.Test;
@@ -30,16 +29,11 @@
@Parameter(1)
public CfVersion inputCfVersion;
- @Parameter(2)
- public boolean rewriteInvokeToPrivateInDesugar;
-
- @Parameterized.Parameters(
- name = "{0}, Input CfVersion = {1}, rewriteInvokeToPrivateInDesugar = {2}")
+ @Parameterized.Parameters(name = "{0}, Input CfVersion = {1})")
public static Iterable<?> data() {
return buildParameters(
getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevelsAlsoForCf().build(),
- CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15),
- BooleanUtils.values());
+ CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15));
}
private static final String EXPECTED_OUTPUT = StringUtils.unixLines("Hello, world!", "21", "6");
@@ -53,7 +47,6 @@
public void testReference() throws Exception {
assumeTrue(parameters.getRuntime().isCf());
assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
- assumeTrue(rewriteInvokeToPrivateInDesugar);
testForJvm()
.addProgramClassFileData(transformIToPrivate(inputCfVersion))
@@ -76,9 +69,7 @@
@Test
public void testDesugar() throws Exception {
- testForDesugaring(
- parameters,
- options -> options.rewriteInvokeToPrivateInDesugar = rewriteInvokeToPrivateInDesugar)
+ testForDesugaring(parameters)
.addProgramClassFileData(transformIToPrivate(inputCfVersion))
.addProgramClasses(TestRunner.class)
.run(parameters.getRuntime(), TestRunner.class)
@@ -101,7 +92,7 @@
&& parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11)
&& (DesugarTestConfiguration.isNotDesugared(c)
|| (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
- && !rewriteInvokeToPrivateInDesugar)),
+ && c == DesugarTestConfiguration.D8_DEX)),
r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
// All other conditions succeed.
r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
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 bb51f2f..b5ca98b 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
@@ -131,6 +131,17 @@
+ " java.nio.file.spi.FileSystemProvider.newDirectoryStream(java.nio.file.Path,"
+ " java.nio.file.DirectoryStream$Filter)");
+ // TODO(b/238179854): Investigate how to fix these.
+ private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_FLOW =
+ ImmutableSet.of(
+ "int java.util.concurrent.SubmissionPublisher.offer(java.lang.Object,"
+ + " java.util.function.BiPredicate)",
+ "java.util.List java.util.concurrent.SubmissionPublisher.getSubscribers()",
+ "void java.util.concurrent.SubmissionPublisher.<init>(java.util.concurrent.Executor, int,"
+ + " java.util.function.BiConsumer)",
+ "int java.util.concurrent.SubmissionPublisher.offer(java.lang.Object, long,"
+ + " java.util.concurrent.TimeUnit, java.util.function.BiPredicate)");
+
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@Parameters(name = "{0}, spec: {1}")
@@ -158,6 +169,9 @@
if (libraryDesugaringSpecification == JDK11_PATH) {
missing.addAll(MISSING_GENERIC_TYPE_CONVERSION_PATH);
}
+ if (libraryDesugaringSpecification != JDK8) {
+ missing.addAll(MISSING_GENERIC_TYPE_CONVERSION_FLOW);
+ }
return missing;
}
@@ -194,7 +208,9 @@
|| type.startsWith("java.security.")
|| type.startsWith("java.net.")
|| type.startsWith("java.awt.")
- || type.startsWith("java.util.concurrent.")
+ || (type.startsWith("java.util.concurrent.")
+ && (!type.startsWith("java.util.concurrent.Flow")
+ || libraryDesugaringSpecification == JDK8))
|| (!libraryDesugaringSpecification.hasNioFileDesugaring(AndroidApiLevel.B)
&& type.startsWith("java.nio."));
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/CollectionToArrayTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/CollectionToArrayTest.java
new file mode 100644
index 0000000..d3d901a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/CollectionToArrayTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class CollectionToArrayTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final Path INPUT_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA11_JAR_DIR + "collectiontoarray.jar");
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("[one, two]");
+ private static final String MAIN_CLASS = "collectiontoarray.Main";
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ ImmutableList.of(JDK11, JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public CollectionToArrayTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/Flow2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/Flow2Test.java
new file mode 100644
index 0000000..b0b4116
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/Flow2Test.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary.jdk11;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.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_PATH;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.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.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class Flow2Test extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.R;
+ private static final Path INPUT_JAR = Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "flow.jar");
+ private static final Path INPUT_LIB_JAR =
+ Paths.get(ToolHelper.EXAMPLES_JAVA9_BUILD_DIR + "flowlib.jar");
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("OneShotPublisher");
+ private static final String MAIN_CLASS = "flow.FlowExample2";
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ // The actual min version is V11 but we don't have it.
+ .withDexRuntimesStartingFromIncluding(Version.V12_0_0)
+ .withApiLevelsEndingAtExcluding(MIN_SUPPORTED)
+ .build(),
+ ImmutableList.of(JDK11, JDK11_LEGACY, JDK11_PATH),
+ ImmutableList.of(D8_L8DEBUG));
+ }
+
+ public Flow2Test(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(INPUT_JAR)
+ .addKeepMainRule(MAIN_CLASS)
+ .setCustomLibrarySpecification(
+ new CustomLibrarySpecification(INPUT_LIB_JAR, AndroidApiLevel.S))
+ .compile()
+ .inspect(this::assertWrapping)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private void assertWrapping(CodeInspector inspector) {
+ List<FoundMethodSubject> getPublisherMethods =
+ inspector
+ .clazz("flow.FlowExample2")
+ .allMethods(m -> m.getMethod().getName().toString().equals("getPublisher"));
+ int numMethods = parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.Q) ? 2 : 1;
+ assertEquals(numMethods, getPublisherMethods.size());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java
index cd4de6e..1c38f5a 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/CustomLibrarySpecification.java
@@ -4,29 +4,44 @@
package com.android.tools.r8.desugar.desugaredlibrary.test;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestBuilder;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
import java.util.Collection;
public class CustomLibrarySpecification {
+ private final Collection<Path> jars;
private final Collection<Class<?>> classes;
private final AndroidApiLevel minApi;
- public CustomLibrarySpecification(Class<?> clazz, AndroidApiLevel minApi) {
- this(ImmutableList.of(clazz), minApi);
+ public CustomLibrarySpecification(Path jar, AndroidApiLevel minApi) {
+ this(ImmutableList.of(jar), ImmutableList.of(), minApi);
}
- public CustomLibrarySpecification(Collection<Class<?>> classes, AndroidApiLevel minApi) {
+ public CustomLibrarySpecification(Class<?> clazz, AndroidApiLevel minApi) {
+ this(ImmutableList.of(), ImmutableList.of(clazz), minApi);
+ }
+
+ public CustomLibrarySpecification(
+ Collection<Path> jars, Collection<Class<?>> classes, AndroidApiLevel minApi) {
+ this.jars = jars;
this.classes = classes;
this.minApi = minApi;
}
- public Collection<Class<?>> getClasses() {
- return classes;
+ public D8TestCompileResult compileCustomLibrary(D8TestBuilder builder)
+ throws CompilationFailedException {
+ return builder.addProgramClasses(classes).addProgramFiles(jars).setMinApi(minApi).compile();
}
- public AndroidApiLevel getMinApi() {
- return minApi;
+ public void addLibraryClasses(
+ TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> builder) {
+ builder.addLibraryClasses(classes).addLibraryFiles(jars);
}
}
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 e68c8bc..b67d511 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
@@ -106,7 +106,7 @@
public DesugaredLibraryTestBuilder<T> setCustomLibrarySpecification(
CustomLibrarySpecification customLibrarySpecification) {
this.customLibrarySpecification = customLibrarySpecification;
- builder.addLibraryClasses(customLibrarySpecification.getClasses());
+ customLibrarySpecification.addLibraryClasses(builder);
return this;
}
@@ -376,10 +376,7 @@
if (customLibrarySpecification == null) {
return null;
}
- return test.testForD8(parameters.getBackend())
- .addProgramClasses(customLibrarySpecification.getClasses())
- .setMinApi(customLibrarySpecification.getMinApi())
- .compile();
+ return customLibrarySpecification.compileCustomLibrary(test.testForD8(parameters.getBackend()));
}
private L8TestCompileResult compileDesugaredLibrary(
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 0222cb7..9ccafc8 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
@@ -34,7 +34,7 @@
public class LibraryDesugaringSpecification {
public static Descriptor JDK8_DESCRIPTOR = new Descriptor(24, 26, -1, 26, 24);
- public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 30, -1, 30, -1);
+ public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 30, -1, 32, -1);
public static Descriptor EMPTY_DESCRIPTOR_24 = new Descriptor(-1, -1, -1, 24, -1);
public static Descriptor JDK11_PATH_DESCRIPTOR = new Descriptor(24, 30, 26, 32, -1);
public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(24, 10000, -1, 10000, 24);
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
index 09a05ed..3c4c536 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
@@ -168,22 +168,36 @@
// Compile rewritten R8 with R8 to obtain first level
Pair<Path, Path> r8OfR8 = compileR8WithR8(rewrittenR8Jar);
- List<String> firstLevelStackTraces = generateStackTraces(r8OfR8.getFirst());
// If we retrace the entire file we should get the same result as the original.
- List<String> retracedFirstLevelStackTraces = new ArrayList<>();
- Retrace.run(
- RetraceCommand.builder()
- .setRetracedStackTraceConsumer(retracedFirstLevelStackTraces::addAll)
- .setStackTrace(firstLevelStackTraces)
- .setMappingSupplier(
- ProguardMappingSupplier.builder()
- .setProguardMapProducer(ProguardMapProducer.fromPath(r8OfR8.getSecond()))
- .build())
- .build());
+ List<String> retracedFirstLevelStackTraces =
+ retrace(r8OfR8.getSecond(), generateStackTraces(r8OfR8.getFirst()));
Map<String, List<String>> firstRoundPartitions =
partitionStacktraces(retracedFirstLevelStackTraces);
comparePartitionedStackTraces(originalPartitions, firstRoundPartitions);
+
+ Pair<Path, Path> d8OfR8ofR8 = compileWithD8(r8OfR8.getFirst(), r8OfR8.getSecond());
+ List<String> retracedSecondLevelStackTraces =
+ retrace(d8OfR8ofR8.getSecond(), generateStackTraces(d8OfR8ofR8.getFirst()));
+ Map<String, List<String>> secondRoundPartitions =
+ partitionStacktraces(retracedSecondLevelStackTraces);
+ comparePartitionedStackTraces(originalPartitions, secondRoundPartitions);
+ }
+
+ private List<String> retrace(Path mappingFile, List<String> stacktraces) {
+ List<String> retracedLines = new ArrayList<>();
+ Retrace.run(
+ RetraceCommand.builder()
+ .setRetracedStackTraceConsumer(retracedLines::addAll)
+ .setStackTrace(stacktraces)
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromPath(mappingFile))
+ // TODO(b/241763080): Remove when stable.
+ .setAllowExperimental(true)
+ .build())
+ .build());
+ return retracedLines;
}
private void comparePartitionedStackTraces(
@@ -231,6 +245,8 @@
.setMode(CompilationMode.RELEASE)
.addProgramFiles(r8Input)
.addKeepRuleFiles(MAIN_KEEP)
+ // TODO(b/241763080): Remove when stable version is default.
+ .enableExperimentalMapFileVersion()
.allowUnusedProguardConfigurationRules()
.addDontObfuscate()
.compile()
@@ -239,6 +255,28 @@
return Pair.create(jar, map);
}
+ private Pair<Path, Path> compileWithD8(Path r8Input, Path previousMappingFile) throws Exception {
+ StringBuilder mappingComposed = new StringBuilder();
+ Path jar = temp.newFolder().toPath().resolve("out.jar");
+ Path map = temp.newFolder().toPath().resolve("out.map");
+ testForD8(Backend.CF)
+ .setMode(CompilationMode.RELEASE)
+ .addProgramFiles(r8Input)
+ .enableExperimentalMapFileVersion()
+ // TODO(b/241763080): Enable CF PC test mapping for this compilation.
+ .addOptionsModification(
+ options -> options.mappingComposeOptions().enableExperimentalMappingComposition = true)
+ .apply(
+ b ->
+ b.getBuilder()
+ .setProguardInputMapFile(previousMappingFile)
+ .setProguardMapConsumer((string, handler) -> mappingComposed.append(string)))
+ .compile()
+ .writeToZip(jar);
+ FileUtils.writeTextFile(map, mappingComposed.toString());
+ return Pair.create(jar, map);
+ }
+
private List<String> generateStackTraces(Path r8Jar) throws Exception {
File stacktraceOutput = new File(temp.newFolder(), "stacktraces.txt");
stacktraceOutput.createNewFile();
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfFieldTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfFieldTest.java
new file mode 100644
index 0000000..fa8bf0b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepIfFieldTest.java
@@ -0,0 +1,88 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestShrinkerBuilder;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+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 ConditionalKeepIfFieldTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultCfRuntime().build();
+ }
+
+ private TestParameters parameters;
+
+ public ConditionalKeepIfFieldTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ run(testForR8(parameters.getBackend()));
+ }
+
+ @Test
+ public void testPG() throws Exception {
+ assertTrue(parameters.isCfRuntime());
+ run(testForProguard(ProguardVersion.getLatest()).addDontWarn(getClass()));
+ }
+
+ private void run(TestShrinkerBuilder<?, ?, ?, ?, ?> builder)
+ throws IOException, ExecutionException, CompilationFailedException {
+ builder
+ .addProgramClasses(A.class, B.class, TestClass.class)
+ .addKeepRules("-if class * { *** *; } -keep class <1> { *; }")
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("42", "0")
+ .inspect(
+ inspector -> {
+ // Check that the above rule only ends up keeping the class with an actual field.
+ assertThat(inspector.clazz(A.class), isAbsent());
+ assertThat(inspector.clazz(B.class), isPresent());
+ });
+ }
+
+ static class A {
+
+ public static void foo() {
+ System.out.println(42);
+ }
+ }
+
+ static class B {
+ public static int x;
+
+ public static void foo() {
+ System.out.println("" + x);
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ A.foo();
+ B.x = args.length;
+ B.foo();
+ }
+ }
+}