| // 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.test; |
| |
| import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_11_LIB_JAR; |
| import static com.android.tools.r8.ToolHelper.DESUGARED_JDK_8_LIB_JAR; |
| import static com.android.tools.r8.ToolHelper.DESUGARED_LIB_RELEASES_DIR; |
| import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LATEST; |
| import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion.LEGACY; |
| |
| import com.android.tools.r8.L8TestBuilder; |
| import com.android.tools.r8.TestParameters; |
| import com.android.tools.r8.ToolHelper; |
| import com.android.tools.r8.ToolHelper.DexVm.Version; |
| import com.android.tools.r8.desugar.desugaredlibrary.jdk11.DesugaredLibraryJDK11Undesugarer; |
| import com.android.tools.r8.dex.ApplicationReader; |
| import com.android.tools.r8.graph.DexApplication; |
| import com.android.tools.r8.utils.AndroidApiLevel; |
| import com.android.tools.r8.utils.AndroidApp; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.ThreadUtils; |
| import com.android.tools.r8.utils.Timing; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import java.io.IOException; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| import org.junit.rules.TemporaryFolder; |
| |
| public class LibraryDesugaringSpecification { |
| |
| public static Descriptor JDK8_DESCRIPTOR = new Descriptor(24, 26, -1, 26, 24); |
| public static Descriptor JDK11_DESCRIPTOR = new Descriptor(24, 26, -1, 10000, -1); |
| public static Descriptor EMPTY_DESCRIPTOR_24 = new Descriptor(-1, -1, -1, 24, -1); |
| public static Descriptor JDK11_PATH_DESCRIPTOR = new Descriptor(24, 26, 26, 10000, -1); |
| public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(24, 26, -1, 32, 24); |
| |
| private static class Descriptor { |
| |
| // Above this level emulated interface are not *entirely* desugared. |
| private final int emulatedInterfaceDesugaring; |
| // Above this level java.time is not *entirely* desugared. |
| private final int timeDesugaring; |
| // Above this level java.nio.file is not *entirely* desugared. |
| private final int nioFileDesugaring; |
| // Above this level no desugaring is required. |
| private final int anyDesugaring; |
| // Above this level java.function is used, below j$.function is used. |
| private final int jDollarFunction; |
| |
| private Descriptor( |
| int emulatedInterfaceDesugaring, |
| int timeDesugaring, |
| int nioFileDesugaring, |
| int anyDesugaring, |
| int jDollarFunction) { |
| this.emulatedInterfaceDesugaring = emulatedInterfaceDesugaring; |
| this.timeDesugaring = timeDesugaring; |
| this.nioFileDesugaring = nioFileDesugaring; |
| this.anyDesugaring = anyDesugaring; |
| this.jDollarFunction = jDollarFunction; |
| } |
| |
| public int getEmulatedInterfaceDesugaring() { |
| return emulatedInterfaceDesugaring; |
| } |
| |
| public int getTimeDesugaring() { |
| return timeDesugaring; |
| } |
| |
| public int getNioFileDesugaring() { |
| return nioFileDesugaring; |
| } |
| |
| public int getAnyDesugaring() { |
| return anyDesugaring; |
| } |
| |
| public int getJDollarFunction() { |
| return jDollarFunction; |
| } |
| } |
| |
| public enum CustomConversionVersion { |
| LEGACY, |
| LATEST |
| } |
| |
| private static final Path tempLibraryJDK11Undesugar = createUndesugaredJdk11LibJarForTesting(); |
| |
| private static Path createUndesugaredJdk11LibJarForTesting() { |
| try { |
| TemporaryFolder staticTemp = ToolHelper.getTemporaryFolderForTest(); |
| staticTemp.create(); |
| Path jdklib_desugaring = staticTemp.newFolder("jdklib_desugaring").toPath(); |
| return DesugaredLibraryJDK11Undesugarer.undesugaredJarJDK11( |
| jdklib_desugaring, DESUGARED_JDK_11_LIB_JAR); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| public static Path getTempLibraryJDK11Undesugar() { |
| return tempLibraryJDK11Undesugar; |
| } |
| |
| // Main head specifications. |
| public static LibraryDesugaringSpecification JDK8 = |
| new LibraryDesugaringSpecification( |
| "JDK8", |
| DESUGARED_JDK_8_LIB_JAR, |
| "desugar_jdk_libs.json", |
| AndroidApiLevel.P, |
| JDK8_DESCRIPTOR, |
| LEGACY); |
| public static LibraryDesugaringSpecification JDK11 = |
| new LibraryDesugaringSpecification( |
| "JDK11", |
| tempLibraryJDK11Undesugar, |
| "jdk11/desugar_jdk_libs.json", |
| AndroidApiLevel.R, |
| JDK11_DESCRIPTOR, |
| LATEST); |
| public static LibraryDesugaringSpecification JDK11_MINIMAL = |
| new LibraryDesugaringSpecification( |
| "JDK11_MINIMAL", |
| tempLibraryJDK11Undesugar, |
| "jdk11/desugar_jdk_libs_minimal.json", |
| AndroidApiLevel.R, |
| EMPTY_DESCRIPTOR_24, |
| LATEST); |
| public static LibraryDesugaringSpecification JDK11_PATH = |
| new LibraryDesugaringSpecification( |
| "JDK11_PATH", |
| tempLibraryJDK11Undesugar, |
| "jdk11/desugar_jdk_libs_nio.json", |
| AndroidApiLevel.R, |
| JDK11_PATH_DESCRIPTOR, |
| LATEST); |
| |
| // Legacy specifications. |
| public static LibraryDesugaringSpecification JDK11_PATH_ALTERNATIVE_3 = |
| new LibraryDesugaringSpecification( |
| "JDK11_PATH_ALTERNATIVE_3", |
| tempLibraryJDK11Undesugar, |
| "jdk11/desugar_jdk_libs_nio_alternative_3.json", |
| AndroidApiLevel.R, |
| JDK11_PATH_DESCRIPTOR, |
| LATEST); |
| public static LibraryDesugaringSpecification JDK11_CHM_ONLY = |
| new LibraryDesugaringSpecification( |
| "JDK11_CHM_ONLY", |
| tempLibraryJDK11Undesugar, |
| "jdk11/chm_only_desugar_jdk_libs.json", |
| AndroidApiLevel.R, |
| EMPTY_DESCRIPTOR_24, |
| LATEST); |
| public static LibraryDesugaringSpecification JDK11_LEGACY = |
| new LibraryDesugaringSpecification( |
| "JDK11_LEGACY", |
| // The legacy specification is not using the undesugared JAR. |
| DESUGARED_JDK_11_LIB_JAR, |
| "jdk11/desugar_jdk_libs_legacy.json", |
| AndroidApiLevel.T, |
| JDK11_LEGACY_DESCRIPTOR, |
| LEGACY); |
| public static final LibraryDesugaringSpecification RELEASED_1_0_9 = |
| new LibraryDesugaringSpecification("1.0.9", AndroidApiLevel.P); |
| public static final LibraryDesugaringSpecification RELEASED_1_0_10 = |
| new LibraryDesugaringSpecification("1.0.10", AndroidApiLevel.P); |
| public static final LibraryDesugaringSpecification RELEASED_1_1_0 = |
| new LibraryDesugaringSpecification("1.1.0", AndroidApiLevel.P); |
| public static final LibraryDesugaringSpecification RELEASED_1_1_1 = |
| new LibraryDesugaringSpecification("1.1.1", AndroidApiLevel.P); |
| public static final LibraryDesugaringSpecification RELEASED_1_1_5 = |
| new LibraryDesugaringSpecification("1.1.5", AndroidApiLevel.P); |
| |
| private final String name; |
| private final Set<Path> desugarJdkLibs; |
| private final Path specification; |
| private final Set<Path> libraryFiles; |
| private final Descriptor descriptor; |
| private final String extraKeepRules; |
| |
| public LibraryDesugaringSpecification( |
| String name, |
| Path desugarJdkLibs, |
| String specificationPath, |
| AndroidApiLevel androidJarLevel, |
| Descriptor descriptor, |
| CustomConversionVersion legacy) { |
| this( |
| name, |
| ImmutableSet.of(desugarJdkLibs, ToolHelper.getDesugarLibConversions(legacy)), |
| Paths.get("src/library_desugar/" + specificationPath), |
| ImmutableSet.of(ToolHelper.getAndroidJar(androidJarLevel)), |
| descriptor, |
| ""); |
| } |
| |
| // This can be used to build custom specifications for testing purposes. |
| public LibraryDesugaringSpecification( |
| String name, |
| Set<Path> desugarJdkLibs, |
| Path specification, |
| Set<Path> libraryFiles, |
| Descriptor descriptor, |
| String extraKeepRules) { |
| this.name = name; |
| this.desugarJdkLibs = desugarJdkLibs; |
| this.specification = specification; |
| this.libraryFiles = libraryFiles; |
| this.descriptor = descriptor; |
| this.extraKeepRules = extraKeepRules; |
| } |
| |
| private LibraryDesugaringSpecification(String version, AndroidApiLevel androidJarLevel) { |
| this( |
| "RELEASED_" + version, |
| ImmutableSet.of( |
| Paths.get(DESUGARED_LIB_RELEASES_DIR, version, "desugar_jdk_libs.jar"), |
| Paths.get(DESUGARED_LIB_RELEASES_DIR, version, "desugar_jdk_libs_configuration.jar")), |
| Paths.get(DESUGARED_LIB_RELEASES_DIR, version, "desugar.json"), |
| ImmutableSet.of(ToolHelper.getAndroidJar(androidJarLevel)), |
| JDK8_DESCRIPTOR, |
| ""); |
| } |
| |
| @Override |
| public String toString() { |
| return name; |
| } |
| |
| public Set<Path> getDesugarJdkLibs() { |
| return desugarJdkLibs; |
| } |
| |
| public Path getSpecification() { |
| return specification; |
| } |
| |
| public Set<Path> getLibraryFiles() { |
| return libraryFiles; |
| } |
| |
| public Descriptor getDescriptor() { |
| return descriptor; |
| } |
| |
| public String getExtraKeepRules() { |
| return extraKeepRules; |
| } |
| |
| public void configureL8TestBuilder(L8TestBuilder l8TestBuilder) { |
| configureL8TestBuilder(l8TestBuilder, false, ""); |
| } |
| |
| public void configureL8TestBuilder( |
| L8TestBuilder l8TestBuilder, boolean l8Shrink, String keepRule) { |
| l8TestBuilder |
| .addProgramFiles(getDesugarJdkLibs()) |
| .addLibraryFiles(getLibraryFiles()) |
| .setDesugaredLibrarySpecification(getSpecification()) |
| .applyIf( |
| l8Shrink, |
| builder -> { |
| if (keepRule != null && !keepRule.trim().isEmpty()) { |
| String totalKeepRules = keepRule + "\n" + getExtraKeepRules(); |
| builder.addGeneratedKeepRules(totalKeepRules); |
| } |
| }, |
| L8TestBuilder::setDebug); |
| } |
| |
| public static List<LibraryDesugaringSpecification> getReleased() { |
| return ImmutableList.of( |
| RELEASED_1_0_9, RELEASED_1_0_10, RELEASED_1_1_0, RELEASED_1_1_1, RELEASED_1_1_5); |
| } |
| |
| public static List<LibraryDesugaringSpecification> getJdk8Jdk11() { |
| return ImmutableList.of(JDK8, JDK11); |
| } |
| |
| public DexApplication getAppForTesting(InternalOptions options, boolean libraryCompilation) |
| throws IOException { |
| AndroidApp.Builder builder = AndroidApp.builder(); |
| if (libraryCompilation) { |
| builder.addProgramFiles(getDesugarJdkLibs()); |
| } |
| AndroidApp inputApp = builder.addLibraryFiles(getLibraryFiles()).build(); |
| return internalReadApp(inputApp, options); |
| } |
| |
| private DexApplication internalReadApp(AndroidApp inputApp, InternalOptions options) |
| throws IOException { |
| ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty()); |
| ExecutorService executorService = ThreadUtils.getExecutorService(options); |
| assert !options.ignoreJavaLibraryOverride; |
| options.ignoreJavaLibraryOverride = true; |
| DexApplication app = applicationReader.read(executorService); |
| options.ignoreJavaLibraryOverride = false; |
| return app; |
| } |
| |
| public boolean hasEmulatedInterfaceDesugaring(TestParameters parameters) { |
| return parameters.getApiLevel().getLevel() < descriptor.getEmulatedInterfaceDesugaring(); |
| } |
| |
| public boolean hasTimeDesugaring(TestParameters parameters) { |
| return parameters.getApiLevel().getLevel() < descriptor.getTimeDesugaring(); |
| } |
| |
| public boolean hasNioFileDesugaring(TestParameters parameters) { |
| return hasNioFileDesugaring(parameters.getApiLevel()); |
| } |
| |
| public boolean hasNioFileDesugaring(AndroidApiLevel apiLevel) { |
| return apiLevel.getLevel() < descriptor.getNioFileDesugaring(); |
| } |
| |
| public boolean hasNioChannelDesugaring(TestParameters parameters) { |
| return hasNioFileDesugaring(parameters) && parameters.getApiLevel().getLevel() < 24; |
| } |
| |
| public boolean usesPlatformFileSystem(TestParameters parameters) { |
| return parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0); |
| } |
| |
| public boolean hasAnyDesugaring(TestParameters parameters) { |
| return hasAnyDesugaring(parameters.getApiLevel()); |
| } |
| |
| public boolean hasAnyDesugaring(AndroidApiLevel apiLevel) { |
| return apiLevel.getLevel() < descriptor.getAnyDesugaring(); |
| } |
| |
| public boolean hasJDollarFunction(TestParameters parameters) { |
| return parameters.getApiLevel().getLevel() < descriptor.getJDollarFunction(); |
| } |
| |
| public String functionPrefix(TestParameters parameters) { |
| return hasJDollarFunction(parameters) ? "j$" : "java"; |
| } |
| } |