Support maintain_prefix for function types
- Enqueuer support with multiple definition of the same class
- Revert change to alternative_3 file (so it can be released to google3)
Bug: 222647019
Change-Id: I2417b1dff182c401e9482e72db197ec90740bf3f
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
new file mode 100644
index 0000000..73fa330
--- /dev/null
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_minimal.json
@@ -0,0 +1,24 @@
+{
+ "identifier": "com.tools.android:desugar_jdk_libs_minimal:2.0.0",
+ "configuration_format_version": 100,
+ "required_compilation_api_level": 24,
+ "synthesized_library_classes_package_prefix": "j$.",
+ "support_all_callbacks_from_library": false,
+ "common_flags": [
+ {
+ "api_level_below_or_equal": 23,
+ "maintain_prefix": [
+ "java.util.function.",
+ "java.util.Optional"
+ ]
+ }
+ ],
+ "program_flags": [],
+ "library_flags": [],
+ "shrinker_config": [
+ "-keeppackagenames java.**",
+ "-keepattributes Signature",
+ "-keepattributes EnclosingMethod",
+ "-keepattributes InnerClasses"
+ ]
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index f259f26..8d8b855 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -983,6 +983,8 @@
internal.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
internal.synthesizedClassPrefix = synthesizedClassPrefix;
+ // TODO(b/214382176): Enable all the time.
+ internal.loadAllClassDefinitions = !synthesizedClassPrefix.isEmpty();
internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
// Set up the map and source file providers.
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index 7bfd947..6b77d5b 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -66,17 +67,26 @@
this.options = options;
}
- private boolean shouldKeep(DexType type) {
- return namingLens.prefixRewrittenType(type) != null
- || options.machineDesugaredLibrarySpecification.getMaintainType().contains(type)
- || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(type)
- || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(type)
+ private boolean shouldKeep(DexType givenType) {
+ if (namingLens.prefixRewrittenType(givenType) != null
+ || options.machineDesugaredLibrarySpecification.isCustomConversionRewrittenType(givenType)
+ || options.machineDesugaredLibrarySpecification.isEmulatedInterfaceRewrittenType(
+ givenType)
// TODO(b/158632510): This should prefix match on DexString.
- || type.toDescriptorString()
+ || givenType
+ .toDescriptorString()
.startsWith(
"L"
+ options.machineDesugaredLibrarySpecification
- .getSynthesizedLibraryClassesPackagePrefix());
+ .getSynthesizedLibraryClassesPackagePrefix())) {
+ return true;
+ }
+ DexType type =
+ InterfaceDesugaringSyntheticHelper.isCompanionClassType(givenType)
+ ? InterfaceDesugaringSyntheticHelper.getInterfaceClassType(
+ givenType, options.dexItemFactory())
+ : givenType;
+ return options.machineDesugaredLibrarySpecification.getMaintainType().contains(type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
index 4eca4e1..f11571a 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassResolutionResult.java
@@ -14,6 +14,11 @@
DexClass toSingleClassWithProgramOverLibrary();
+ // The alternative class is:
+ // - the other class than the single class if the resolution resolves into multiple classes,
+ // - null if the resolution resolves into a single class.
+ DexClass toAlternativeClassWithProgramOverLibrary();
+
void forEachClassResolutionResult(Consumer<DexClass> consumer);
static Builder builder() {
@@ -86,6 +91,11 @@
}
@Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return null;
+ }
+
+ @Override
public void forEachClassResolutionResult(Consumer<DexClass> consumer) {
// Intentionally empty
}
@@ -126,6 +136,11 @@
public DexClass toSingleClassWithProgramOverLibrary() {
return programOrClasspathClass;
}
+
+ @Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return libraryClass;
+ }
}
class ClasspathAndLibraryClassResolutionResult
@@ -140,5 +155,10 @@
public DexClass toSingleClassWithProgramOverLibrary() {
return libraryClass;
}
+
+ @Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return programOrClasspathClass;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 4fac278..a615b1f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -150,6 +150,11 @@
return this;
}
+ @Override
+ public DexClass toAlternativeClassWithProgramOverLibrary() {
+ return null;
+ }
+
public abstract void accept(
Consumer<DexProgramClass> programClassConsumer,
Consumer<DexClasspathClass> classpathClassConsumer,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index d6ca793..d2b1d3b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -331,8 +331,8 @@
if (definition.isStatic() != isStatic
|| appView.isCfByteCodePassThrough(getContext().getDefinition())
- || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()
|| !resolutionResult.isSingleProgramFieldResolutionResult()
+ || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()
|| appView.appInfo().isNeverReprocessMethod(getContext())) {
recordAccessThatCannotBeOptimized(field, definition);
return;
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 eb5a30b..c039f9d 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
@@ -249,7 +249,7 @@
return;
}
AndroidApiLevel theApi = apiLevel.asKnownApiLevel().getApiLevel();
- if (appView.typeRewriter.hasRewrittenType(type, appView)) {
+ if (typeIsInDesugaredLibrary(type)) {
assert theApi.equals(appView.options().getMinApiLevel());
return;
}
@@ -258,6 +258,15 @@
return true;
}
+ private boolean typeIsInDesugaredLibrary(DexType type) {
+ return appView.typeRewriter.hasRewrittenType(type, appView)
+ || appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getMaintainType()
+ .contains(type);
+ }
+
private boolean typeIsAbsentOrPresentWithoutBackportsFrom(
DexType type, AndroidApiLevel apiLevel) {
return !typeIsPresent(type) || typeIsPresentWithoutBackportsFrom(type, apiLevel);
@@ -268,7 +277,7 @@
}
private boolean typeIsPresentWithoutBackportsFrom(DexType type, AndroidApiLevel methodsMinAPI) {
- if (appView.typeRewriter.hasRewrittenType(type, appView)) {
+ if (typeIsInDesugaredLibrary(type)) {
// Desugared library is enabled, the methods are present if desugared library specifies it.
return methodsMinAPI.isGreaterThan(AndroidApiLevel.N)
&& !appView.options().machineDesugaredLibrarySpecification.includesJDK11Methods();
@@ -284,7 +293,7 @@
private boolean typeIsPresent(DexType type) {
// TODO(b/224954240): Always use the apiDatabase when always available.
return appView.options().getMinApiLevel().isGreaterThanOrEqualTo(typeMinApi.get(type))
- || appView.typeRewriter.hasRewrittenType(type, appView);
+ || typeIsInDesugaredLibrary(type);
}
boolean isEmpty() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
index b0c7920..50e98e1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/HumanRewritingFlags.java
@@ -172,6 +172,7 @@
public boolean isEmpty() {
return rewritePrefix.isEmpty()
&& rewriteDerivedPrefix.isEmpty()
+ && maintainPrefix.isEmpty()
&& emulatedInterfaces.isEmpty()
&& retargetMethod.isEmpty()
&& retargetMethodEmulatedDispatch.isEmpty()
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index d974c56..0fafbac 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -52,6 +52,10 @@
this.rewritingFlags = rewritingFlags;
}
+ public boolean isEmpty() {
+ return rewritingFlags.isEmpty();
+ }
+
public boolean isLibraryCompilation() {
return libraryCompilation;
}
@@ -167,7 +171,10 @@
public boolean isSupported(DexReference reference) {
// Support through type rewriting.
- if (rewritingFlags.getRewriteType().containsKey(reference.getContextType())) {
+ if (getRewriteType().containsKey(reference.getContextType())) {
+ return true;
+ }
+ if (getMaintainType().contains(reference.getContextType())) {
return true;
}
if (!reference.isDexMethod()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
index dcaa744..1a301da 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -196,6 +196,15 @@
return emulatedInterfaces.get(method.getHolderType()).getEmulatedMethods().get(method);
}
+ public boolean isEmpty() {
+ return rewriteType.isEmpty()
+ && maintainType.isEmpty()
+ && rewriteDerivedTypeOnly.isEmpty()
+ && !hasRetargeting()
+ && emulatedInterfaces.isEmpty()
+ && legacyBackport.isEmpty();
+ }
+
public static class Builder {
Builder() {}
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 ef19eb6..567cc79 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
@@ -26,6 +26,8 @@
levelType = app.dexItemFactory.createType("Ljava/time/LocalTime;");
} else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.R)) {
levelType = app.dexItemFactory.createType("Ljava/util/concurrent/Flow;");
+ } else if (requiredCompilationAPILevel.isEqualTo(AndroidApiLevel.N)) {
+ levelType = app.dexItemFactory.createType("Ljava/util/function/Supplier;");
} 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 4446275..266b1c5 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
@@ -377,7 +377,8 @@
this.dexItemFactory = appView.dexItemFactory();
this.helper = new InterfaceDesugaringSyntheticHelper(appView);
needsLibraryInfo =
- appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces();
+ !appView.options().canUseDefaultAndStaticInterfaceMethods()
+ && !appView.options().machineDesugaredLibrarySpecification.isEmpty();
this.isLiveMethod = isLiveMethod;
}
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 7d54b09..a26f167 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassDefinition;
+import com.android.tools.r8.graph.ClassResolutionResult;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.ClasspathOrLibraryDefinition;
import com.android.tools.r8.graph.Definition;
@@ -160,6 +161,7 @@
import com.google.common.collect.Sets.SetView;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import java.lang.reflect.InvocationHandler;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
@@ -667,26 +669,39 @@
return definitionFor(type, context, this::recordNonProgramClass, this::reportMissingClass);
}
+ public boolean hasAlternativeLibraryDefinition(DexProgramClass programClass) {
+ ClassResolutionResult classResolutionResult =
+ internalDefinitionFor(
+ programClass.type, programClass, this::recordNonProgramClass, this::reportMissingClass);
+ assert classResolutionResult.hasClassResolutionResult();
+ DexClass alternativeClass = classResolutionResult.toAlternativeClassWithProgramOverLibrary();
+ assert alternativeClass == null || alternativeClass.isLibraryClass();
+ return alternativeClass != null;
+ }
+
private DexClass definitionFor(
DexType type,
ProgramDerivedContext context,
BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
- return internalDefinitionFor(type, context, foundClassConsumer, missingClassConsumer);
+ return internalDefinitionFor(type, context, foundClassConsumer, missingClassConsumer)
+ .toSingleClassWithProgramOverLibrary();
}
- private DexClass internalDefinitionFor(
+ private ClassResolutionResult internalDefinitionFor(
DexType type,
ProgramDerivedContext context,
BiConsumer<DexClass, ProgramDerivedContext> foundClassConsumer,
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer) {
- DexClass clazz = appInfo().definitionFor(type);
- if (clazz == null) {
+ ClassResolutionResult classResolutionResult =
+ appInfo().contextIndependentDefinitionForWithResolutionResult(type);
+ if (classResolutionResult.hasClassResolutionResult()) {
+ classResolutionResult.forEachClassResolutionResult(
+ clazz -> foundClassConsumer.accept(clazz, context));
+ } else {
missingClassConsumer.accept(type, context);
- return null;
}
- foundClassConsumer.accept(clazz, context);
- return clazz;
+ return classResolutionResult;
}
public FieldAccessInfoCollectionImpl getFieldAccessInfoCollection() {
@@ -783,12 +798,18 @@
if (!type.isClassType()) {
return;
}
- DexClass clazz = appView.definitionFor(type);
- if (clazz == null) {
+ ClassResolutionResult classResolutionResult =
+ appView.contextIndependentDefinitionForWithResolutionResult(type);
+ if (!classResolutionResult.hasClassResolutionResult()) {
missingClassConsumer.accept(type, context);
- } else if (!clazz.isProgramClass()) {
- classAdder.accept(clazz.asClasspathOrLibraryClass());
+ return;
}
+ classResolutionResult.forEachClassResolutionResult(
+ clazz -> {
+ if (!clazz.isProgramClass()) {
+ classAdder.accept(clazz.asClasspathOrLibraryClass());
+ }
+ });
}
private DexProgramClass getProgramClassOrNull(DexType type, ProgramDefinition context) {
@@ -2086,6 +2107,9 @@
// Update keep info.
applyMinimumKeepInfo(clazz);
applyMinimumKeepInfoDependentOn(new LiveClassEnqueuerEvent(clazz));
+ if (hasAlternativeLibraryDefinition(clazz)) {
+ getKeepInfo().keepClass(clazz);
+ }
processAnnotations(clazz);
@@ -2472,6 +2496,16 @@
if (clazz == null) {
return;
}
+ DexClass alternativeResolutionResult =
+ appInfo()
+ .contextIndependentDefinitionForWithResolutionResult(type)
+ .toAlternativeClassWithProgramOverLibrary();
+ if (alternativeResolutionResult != null && alternativeResolutionResult.isLibraryClass()) {
+ // We are in a situation where a library class inherits from a library class, which has a
+ // program class duplicated version for low API levels.
+ recordNonProgramClass(alternativeResolutionResult, clazz);
+ return;
+ }
if (forceProguardCompatibility) {
// To ensure that the program works correctly we have to pin all super types and members
// in the tree.
@@ -3000,6 +3034,9 @@
// Update keep info.
applyMinimumKeepInfo(field);
+ if (hasAlternativeLibraryDefinition(field.getHolder()) && !field.getDefinition().isPrivate()) {
+ getKeepInfo().keepField(field);
+ }
// Notify analyses.
analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context, workList));
@@ -4561,6 +4598,10 @@
// Update keep info.
applyMinimumKeepInfo(method);
+ if (hasAlternativeLibraryDefinition(method.getHolder())
+ && !method.getDefinition().isPrivateMethod()) {
+ getKeepInfo().keepMethod(method);
+ }
}
private void traceNonDesugaredCode(ProgramMethod method) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 5b8bb7a..7bcdfb6 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -196,6 +196,10 @@
return System.getProperty("desugar_jdk_json_dir", "src/library_desugar");
}
+ public static Path getDesugarLibJsonMinimalForTesting() {
+ return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs_minimal.json");
+ }
+
public static Path getDesugarLibJsonForTesting() {
return Paths.get(getDesugarLibraryJsonDir(), "desugar_jdk_libs.json");
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FunctionOnlyTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FunctionOnlyTest.java
new file mode 100644
index 0000000..0b443a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/FunctionOnlyTest.java
@@ -0,0 +1,277 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.function.DoublePredicate;
+import java.util.function.DoubleSupplier;
+import java.util.function.Function;
+import java.util.function.IntSupplier;
+import java.util.function.LongConsumer;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+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 FunctionOnlyTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(" true true true", "2", "false", "3", "true", "5", "42.0");
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevelsAlsoForCf().build(),
+ BooleanUtils.values());
+ }
+
+ public FunctionOnlyTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testFunctionCompositionD8() throws Exception {
+ testForD8()
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(getClass())
+ .enableCoreLibraryDesugaring(minimalConfiguration())
+ .compile()
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ private LibraryDesugaringTestConfiguration minimalConfiguration() {
+ if (!isJDK11DesugaredLibrary()) {
+ return LibraryDesugaringTestConfiguration.builder()
+ .setMinApi(parameters.getApiLevel())
+ .setMode(CompilationMode.RELEASE)
+ .withKeepRuleConsumer()
+ .build();
+ }
+ return LibraryDesugaringTestConfiguration.builder()
+ .setMinApi(parameters.getApiLevel())
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonMinimalForTesting()))
+ .setMode(shrinkDesugaredLibrary ? CompilationMode.RELEASE : CompilationMode.DEBUG)
+ .withKeepRuleConsumer()
+ .build();
+ }
+
+ @Test
+ public void testFunctionCompositionD8Cf2Cf() throws Exception {
+ Assume.assumeTrue(isJDK11DesugaredLibrary());
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ Path jar =
+ testForD8(Backend.CF)
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addInnerClasses(FunctionOnlyTest.class)
+ .enableCoreLibraryDesugaring(
+ parameters.getApiLevel(),
+ keepRuleConsumer,
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonMinimalForTesting()))
+ .compile()
+ .writeToZip();
+ String desugaredLibraryKeepRules = "";
+ if (shrinkDesugaredLibrary && keepRuleConsumer.get() != null) {
+ // Collection keep rules is only implemented in the DEX writer.
+ assertEquals(0, keepRuleConsumer.get().length());
+ desugaredLibraryKeepRules = "-keep class java.util.function.* { *; }";
+ }
+ if (parameters.getRuntime().isDex()) {
+ testForD8()
+ .addProgramFiles(jar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ (api, keep, shrink) ->
+ buildDesugaredLibrary(
+ api,
+ keep,
+ shrink,
+ ImmutableList.of(),
+ opt ->
+ opt.setDesugaredLibrarySpecification(
+ DesugaredLibrarySpecificationParser
+ .parseDesugaredLibrarySpecification(
+ StringResource.fromFile(
+ ToolHelper.getDesugarLibJsonMinimalForTesting()),
+ opt.itemFactory,
+ opt.reporter,
+ true,
+ parameters.getApiLevel().getLevel()))),
+ parameters.getApiLevel(),
+ desugaredLibraryKeepRules,
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+
+ } else {
+ testForJvm()
+ .addProgramFiles(jar)
+ .addRunClasspathFiles(getDesugaredLibraryInCFMinimal(parameters, options -> {}))
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+ }
+
+ public Path getDesugaredLibraryInCFMinimal(
+ TestParameters parameters, Consumer<InternalOptions> configurationForLibraryCompilation)
+ throws IOException, CompilationFailedException {
+ Path desugaredLib = temp.newFolder().toPath().resolve("desugar_jdk_libs.jar");
+ L8Command.Builder l8Builder =
+ L8Command.builder()
+ .addLibraryFiles(getLibraryFile())
+ .addProgramFiles(ToolHelper.getDesugarJDKLibs())
+ .addProgramFiles(ToolHelper.DESUGAR_LIB_CONVERSIONS)
+ .setMode(CompilationMode.DEBUG)
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.getDesugarLibJsonMinimalForTesting()))
+ .setMinApiLevel(parameters.getApiLevel().getLevel())
+ .setOutput(desugaredLib, OutputMode.ClassFile);
+ ToolHelper.runL8(l8Builder.build(), configurationForLibraryCompilation);
+ return desugaredLib;
+ }
+
+ @Test
+ public void testFunctionCompositionR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryFiles(getLibraryFile())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Executor.class)
+ .addInnerClasses(getClass())
+ .enableCoreLibraryDesugaring(minimalConfiguration())
+ .allowStdoutMessages()
+ .compile()
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ Function<Executor.Object1, Executor.Object3> function =
+ FunctionClass.composeFunction(Executor.Object2::new, Executor.Object3::new);
+ System.out.println(function.apply(new Executor.Object1()).toString());
+ BiFunction<String, String, Character> biFunction =
+ FunctionClass.composeBiFunctions(
+ (String i, String j) -> i + j, (String s) -> s.charAt(1));
+ System.out.println(biFunction.apply("1", "2"));
+ BooleanSupplier booleanSupplier =
+ () -> FunctionClass.composeBoolSuppliers(() -> true, () -> false).get();
+ System.out.println(booleanSupplier.getAsBoolean());
+ LongConsumer longConsumer = FunctionClass.composeLong(() -> 1L, System.out::println);
+ longConsumer.accept(2L);
+ DoublePredicate doublePredicate =
+ FunctionClass.composePredicate(d -> d > 1.0, d -> d == 2.0, d -> d < 3.0);
+ System.out.println(doublePredicate.test(2.0));
+ System.out.println(FunctionClass.extractInt(() -> 5));
+ System.out.println(FunctionClass.getDoubleSupplier().get());
+ }
+
+ static class Object1 {}
+
+ static class Object2 {
+
+ private Executor.Object1 field;
+
+ Object2(Executor.Object1 o) {
+ this.field = o;
+ }
+ }
+
+ static class Object3 {
+
+ private Executor.Object2 field;
+
+ Object3(Executor.Object2 o) {
+ this.field = o;
+ }
+
+ @Override
+ public String toString() {
+ return " "
+ + (field.field.getClass() == Executor.Object1.class)
+ + " "
+ + (field.getClass() == Executor.Object2.class)
+ + " "
+ + (getClass() == Executor.Object3.class);
+ }
+ }
+ }
+
+ static class FunctionClass {
+
+ public static <T, Q, R> Function<T, R> composeFunction(Function<T, Q> f1, Function<Q, R> f2) {
+ return f1.andThen(f2);
+ }
+
+ public static <T, R> BiFunction<T, T, R> composeBiFunctions(
+ BinaryOperator<T> operator, Function<T, R> function) {
+ return operator.andThen(function);
+ }
+
+ // BooleanSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static Supplier<Boolean> composeBoolSuppliers(
+ Supplier<Boolean> supplier1, Supplier<Boolean> supplier2) {
+ BooleanSupplier wrap1 = supplier1::get;
+ BooleanSupplier wrap2 = supplier2::get;
+ return () -> wrap1.getAsBoolean() && wrap2.getAsBoolean();
+ }
+
+ // LongSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static LongConsumer composeLong(Supplier<Long> supplier, LongConsumer consumer) {
+ LongSupplier wrap = supplier::get;
+ return l -> consumer.accept(l + wrap.getAsLong());
+ }
+
+ public static DoublePredicate composePredicate(
+ DoublePredicate predicate1, DoublePredicate predicate2, DoublePredicate predicate3) {
+ return predicate1.and(predicate2).and(predicate3);
+ }
+
+ // IntSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static int extractInt(Supplier<Integer> supplier) {
+ IntSupplier wrap = supplier::get;
+ return wrap.getAsInt();
+ }
+
+ // DoubleSupplier is not a wrapped type, so it can't be placed on the boundary.
+ public static Supplier<Double> getDoubleSupplier() {
+ DoubleSupplier supplier = () -> 42.0;
+ return supplier::getAsDouble;
+ }
+ }
+}