Delegate wrapper conversion to subtypes
- Support OpenOption and BasicFileAttributes conversions
without custom conversion
- Fix issues with wrapping/unwrapping and subtypes
Change-Id: I056809fc3192887830796b0ce0f379631b4a3af8
diff --git a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
index 137ac12..2b46e36 100644
--- a/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
+++ b/buildSrc/src/main/java/desugaredlibrary/CustomConversionAsmRewriteDescription.java
@@ -17,8 +17,6 @@
private static final Set<String> ENUM_WRAP_CONVERT_OWNER =
ImmutableSet.of(
- "j$/nio/file/StandardOpenOption",
- "j$/nio/file/LinkOption",
"j$/nio/file/attribute/PosixFilePermission",
"j$/util/stream/Collector$Characteristics");
private static final Set<String> WRAP_CONVERT_OWNER =
@@ -27,18 +25,14 @@
"j$/nio/file/spi/FileTypeDetector",
"j$/nio/file/Path",
"j$/nio/file/WatchEvent",
- "j$/nio/file/attribute/BasicFileAttributes",
- "j$/nio/file/attribute/BasicFileAttributeView",
- "j$/nio/file/attribute/FileOwnerAttributeView",
- "j$/nio/file/attribute/PosixFileAttributes",
- "j$/nio/file/attribute/PosixFileAttributeView");
+ "j$/nio/file/OpenOption");
static Map<String, String> getJavaWrapConvertOwnerMap() {
- return computeConvertOwnerMap("$VivifiedWrapper");
+ return computeConvertOwnerMap("$Wrapper");
}
static Map<String, String> getJ$WrapConvertOwnerMap() {
- return computeConvertOwnerMap("$Wrapper");
+ return computeConvertOwnerMap("$VivifiedWrapper");
}
private static HashMap<String, String> computeConvertOwnerMap(String suffix) {
diff --git a/src/library_desugar/java/j$/nio/file/OpenOption.java b/src/library_desugar/java/j$/nio/file/OpenOption.java
index 8b063d1..a22eb7c 100644
--- a/src/library_desugar/java/j$/nio/file/OpenOption.java
+++ b/src/library_desugar/java/j$/nio/file/OpenOption.java
@@ -4,4 +4,12 @@
package j$.nio.file;
-public class OpenOption {}
+public class OpenOption {
+ public static java.nio.file.OpenOption wrap_convert(j$.nio.file.OpenOption option) {
+ return null;
+ }
+
+ public static j$.nio.file.OpenOption wrap_convert(java.nio.file.OpenOption option) {
+ return null;
+ }
+}
diff --git a/src/library_desugar/java/j$/nio/file/Path.java b/src/library_desugar/java/j$/nio/file/Path.java
index cb01ec0..781ab5f 100644
--- a/src/library_desugar/java/j$/nio/file/Path.java
+++ b/src/library_desugar/java/j$/nio/file/Path.java
@@ -6,7 +6,7 @@
public class Path {
- public static j$.nio.file.Path inverted_wrap_convert(java.nio.file.Path path) {
+ public static j$.nio.file.Path wrap_convert(java.nio.file.Path path) {
return null;
}
}
diff --git a/src/library_desugar/java/j$/nio/file/StandardOpenOption.java b/src/library_desugar/java/j$/nio/file/StandardOpenOption.java
deleted file mode 100644
index a65da37..0000000
--- a/src/library_desugar/java/j$/nio/file/StandardOpenOption.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package j$.nio.file;
-
-public class StandardOpenOption extends OpenOption {
-
- public static java.nio.file.StandardOpenOption wrap_convert(
- j$.nio.file.StandardOpenOption option) {
- return null;
- }
-
- public static j$.nio.file.StandardOpenOption wrap_convert(
- java.nio.file.StandardOpenOption option) {
- return null;
- }
-}
diff --git a/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java b/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
index 38152b3..8db22b3 100644
--- a/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
+++ b/src/library_desugar/java/j$/nio/file/spi/FileSystemProvider.java
@@ -5,7 +5,8 @@
package j$.nio.file.spi;
public class FileSystemProvider {
- public static java.nio.file.spi.FileSystemProvider wrap_convert(FileSystemProvider provider) {
+ public static java.nio.file.spi.FileSystemProvider inverted_wrap_convert(
+ j$.nio.file.spi.FileSystemProvider provider) {
// Rewritten in ASM to the wrapper method.
return null;
}
diff --git a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
index d02344d..5909b09 100644
--- a/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
+++ b/src/library_desugar/java/java/adapter/HybridFileSystemProvider.java
@@ -27,7 +27,7 @@
Class.forName("java.nio.file.FileSystems");
j$.nio.file.FileSystem fileSystem = FileSystems.getDefault();
j$.nio.file.spi.FileSystemProvider provider = fileSystem.provider();
- return j$.nio.file.spi.FileSystemProvider.wrap_convert(provider);
+ return j$.nio.file.spi.FileSystemProvider.inverted_wrap_convert(provider);
} catch (ClassNotFoundException ignored) {
// We reach this path is API < 26.
}
diff --git a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
index af2c8f7..da93736 100644
--- a/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
+++ b/src/library_desugar/java/java/adapter/HybridFileTypeDetector.java
@@ -29,7 +29,7 @@
static class PlatformFileTypeDetector extends FileTypeDetector {
@Override
public String probeContentType(Path path) throws IOException {
- return j$.nio.file.Files.probeContentType(j$.nio.file.Path.inverted_wrap_convert(path));
+ return j$.nio.file.Files.probeContentType(j$.nio.file.Path.wrap_convert(path));
}
}
}
diff --git a/src/library_desugar/java/java/nio/file/FileApiFlips.java b/src/library_desugar/java/java/nio/file/FileApiFlips.java
index 71b172a..b68aeb2 100644
--- a/src/library_desugar/java/java/nio/file/FileApiFlips.java
+++ b/src/library_desugar/java/java/nio/file/FileApiFlips.java
@@ -78,7 +78,7 @@
} catch (ClassCastException cce) {
throw exceptionOpenOption(cce);
}
- convertedSet.add(OpenOptionConversions.convert(option));
+ convertedSet.add(j$.nio.file.OpenOption.wrap_convert(option));
}
return convertedSet;
}
@@ -90,7 +90,7 @@
} catch (ClassCastException cce) {
throw exceptionOpenOption(cce);
}
- convertedSet.add(OpenOptionConversions.convert(option));
+ convertedSet.add(j$.nio.file.OpenOption.wrap_convert(option));
}
return convertedSet;
}
diff --git a/src/library_desugar/java/java/nio/file/OpenOptionConversions.java b/src/library_desugar/java/java/nio/file/OpenOptionConversions.java
deleted file mode 100644
index bfe4968..0000000
--- a/src/library_desugar/java/java/nio/file/OpenOptionConversions.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package java.nio.file;
-
-import static java.util.ConversionRuntimeException.exception;
-
-public class OpenOptionConversions {
- public static java.nio.file.OpenOption convert(j$.nio.file.OpenOption option) {
- if (option == null) {
- return null;
- }
- if (option instanceof j$.nio.file.StandardOpenOption) {
- return j$.nio.file.StandardOpenOption.wrap_convert((j$.nio.file.StandardOpenOption) option);
- }
- if (option instanceof j$.nio.file.LinkOption) {
- return j$.nio.file.LinkOption.wrap_convert((j$.nio.file.LinkOption) option);
- }
- throw exception("java.nio.file.OpenOption", option);
- }
-
- public static j$.nio.file.OpenOption convert(java.nio.file.OpenOption option) {
- if (option == null) {
- return null;
- }
- if (option instanceof java.nio.file.StandardOpenOption) {
- return j$.nio.file.StandardOpenOption.wrap_convert((java.nio.file.StandardOpenOption) option);
- }
- if (option instanceof java.nio.file.LinkOption) {
- return j$.nio.file.LinkOption.wrap_convert((java.nio.file.LinkOption) option);
- }
- throw exception("java.nio.file.OpenOption", option);
- }
-}
diff --git a/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java b/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
index 332c89b..c895d52 100644
--- a/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
+++ b/src/library_desugar/java/java/nio/file/attribute/FileAttributeConversions.java
@@ -4,8 +4,6 @@
package java.nio.file.attribute;
-import static java.util.ConversionRuntimeException.exception;
-
public class FileAttributeConversions {
public static java.nio.file.attribute.FileTime convert(j$.nio.file.attribute.FileTime fileTime) {
@@ -22,67 +20,4 @@
return j$.nio.file.attribute.FileTime.fromMillis(fileTime.toMillis());
}
- public static java.nio.file.attribute.FileAttributeView convert(
- j$.nio.file.attribute.FileAttributeView fileAttributeView) {
- if (fileAttributeView == null) {
- return null;
- }
- if (fileAttributeView instanceof j$.nio.file.attribute.PosixFileAttributeView) {
- return j$.nio.file.attribute.PosixFileAttributeView.wrap_convert(
- (j$.nio.file.attribute.PosixFileAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof j$.nio.file.attribute.FileOwnerAttributeView) {
- return j$.nio.file.attribute.FileOwnerAttributeView.wrap_convert(
- (j$.nio.file.attribute.FileOwnerAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof j$.nio.file.attribute.BasicFileAttributeView) {
- return j$.nio.file.attribute.BasicFileAttributeView.wrap_convert(
- (j$.nio.file.attribute.BasicFileAttributeView) fileAttributeView);
- }
- throw exception("java.nio.file.attribute.FileAttributeView", fileAttributeView);
- }
-
- public static j$.nio.file.attribute.FileAttributeView convert(
- java.nio.file.attribute.FileAttributeView fileAttributeView) {
- if (fileAttributeView == null) {
- return null;
- }
- if (fileAttributeView instanceof java.nio.file.attribute.PosixFileAttributeView) {
- return j$.nio.file.attribute.PosixFileAttributeView.wrap_convert(
- (java.nio.file.attribute.PosixFileAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof java.nio.file.attribute.FileOwnerAttributeView) {
- return j$.nio.file.attribute.FileOwnerAttributeView.wrap_convert(
- (java.nio.file.attribute.FileOwnerAttributeView) fileAttributeView);
- }
- if (fileAttributeView instanceof java.nio.file.attribute.BasicFileAttributeView) {
- return j$.nio.file.attribute.BasicFileAttributeView.wrap_convert(
- (java.nio.file.attribute.BasicFileAttributeView) fileAttributeView);
- }
- throw exception("java.nio.file.attribute.FileAttributeView", fileAttributeView);
- }
-
- public static java.nio.file.attribute.BasicFileAttributes convert(
- j$.nio.file.attribute.BasicFileAttributes fileAttributes) {
- if (fileAttributes == null) {
- return null;
- }
- if (fileAttributes instanceof j$.nio.file.attribute.PosixFileAttributes) {
- return j$.nio.file.attribute.PosixFileAttributes.wrap_convert(
- (j$.nio.file.attribute.PosixFileAttributes) fileAttributes);
- }
- return j$.nio.file.attribute.BasicFileAttributes.wrap_convert(fileAttributes);
- }
-
- public static j$.nio.file.attribute.BasicFileAttributes convert(
- java.nio.file.attribute.BasicFileAttributes fileAttributes) {
- if (fileAttributes == null) {
- return null;
- }
- if (fileAttributes instanceof java.nio.file.attribute.PosixFileAttributes) {
- return j$.nio.file.attribute.PosixFileAttributes.wrap_convert(
- (java.nio.file.attribute.PosixFileAttributes) fileAttributes);
- }
- return j$.nio.file.attribute.BasicFileAttributes.wrap_convert(fileAttributes);
- }
}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path.json b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
index 7b767e1..f2e565f 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
@@ -132,6 +132,7 @@
"java.nio.file.spi.FileSystemProvider",
"java.nio.file.spi.FileTypeDetector",
"java.nio.file.AccessMode",
+ "java.nio.file.OpenOption",
"java.nio.file.StandardOpenOption",
"java.nio.file.LinkOption",
"java.nio.file.CopyOption",
@@ -145,6 +146,7 @@
"java.nio.file.attribute.PosixFilePermission",
"java.nio.file.attribute.BasicFileAttributes",
"java.nio.file.attribute.PosixFileAttributes",
+ "java.nio.file.attribute.FileAttributeView",
"java.nio.file.attribute.FileOwnerAttributeView",
"java.nio.file.attribute.PosixFileAttributeView",
"java.nio.file.attribute.BasicFileAttributeView"
@@ -157,10 +159,7 @@
]
},
"custom_conversion": {
- "java.nio.file.OpenOption": "java.nio.file.OpenOptionConversions",
- "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions",
- "java.nio.file.attribute.BasicFileAttributes": "java.nio.file.attribute.FileAttributeConversions",
- "java.nio.file.attribute.FileAttributeView": "java.nio.file.attribute.FileAttributeConversions"
+ "java.nio.file.attribute.FileTime": "java.nio.file.attribute.FileAttributeConversions"
}
},
{
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
index d28ccaf..41ab157 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryWrapperSynthesizer.java
@@ -46,6 +46,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
@@ -363,7 +364,12 @@
}
assert context.isNotProgramClass();
Iterable<DexMethod> methods =
- appView.options().machineDesugaredLibrarySpecification.getWrappers().get(context.type);
+ appView
+ .options()
+ .machineDesugaredLibrarySpecification
+ .getWrappers()
+ .get(context.type)
+ .getMethods();
assert methods != null;
ClasspathOrLibraryClass classpathOrLibraryContext = context.asClasspathOrLibraryClass();
DexType type = context.type;
@@ -474,13 +480,24 @@
eventConsumer::acceptWrapperClasspathClass);
}
- private void getExistingProgramConversionMethod(
+ private void synthesizeProgramConversionMethod(
SyntheticKindSelector kindSelector,
DexProgramClass context,
+ List<DexType> subwrappers,
DexClass wrapper,
DexClass reverseWrapper) {
DexField wrapperField = getWrapperUniqueField(wrapper);
DexField reverseWrapperField = getWrapperUniqueField(reverseWrapper);
+ List<DexMethod> subwrapperConvertList = new ArrayList<>();
+ for (DexType subwrapper : subwrappers) {
+ DexClass subwrapperClass = appView.definitionFor(subwrapper);
+ assert subwrapperClass != null;
+ DexProgramClass subwrapperWrapper =
+ getExistingProgramWrapper(
+ subwrapperClass,
+ subwrapperClass.isEnum() ? kinds -> kinds.ENUM_CONVERSION : kindSelector);
+ subwrapperConvertList.add(getConversion(subwrapperWrapper));
+ }
DexProto proto = factory.createProto(reverseWrapperField.type, wrapperField.type);
appView
.getSyntheticItems()
@@ -496,14 +513,27 @@
methodBuilder,
proto,
computeProgramConversionMethodCode(
- wrapperField, reverseWrapperField, context)));
+ wrapperField, reverseWrapperField, context, subwrapperConvertList)));
+ }
+
+ private DexMethod getConversion(DexProgramClass subwrapperWrapper) {
+ Iterator<DexEncodedMethod> iterator = subwrapperWrapper.directMethods().iterator();
+ DexEncodedMethod method;
+ do {
+ method = iterator.next();
+ } while (!method.isStatic());
+ assert method.getName() == factory.convertMethodName;
+ return method.getReference();
}
private CfCode computeProgramConversionMethodCode(
- DexField wrapperField, DexField reverseWrapperField, DexClass context) {
+ DexField wrapperField,
+ DexField reverseWrapperField,
+ DexClass context,
+ List<DexMethod> subwrapperConvertList) {
assert context.isProgramClass();
return new NullableConversionCfCodeProvider.WrapperConversionCfCodeProvider(
- appView, reverseWrapperField, wrapperField)
+ appView, reverseWrapperField, wrapperField, subwrapperConvertList)
.generateCfCode();
}
@@ -635,7 +665,7 @@
librarySpecification
.getWrappers()
.forEach(
- (type, methods) -> {
+ (type, descriptor) -> {
DexClass validClassToWrap = getValidClassToWrap(type);
// In broken set-ups we can end up having a json files containing wrappers of non
// desugared classes. Such wrappers are not required since the class won't be
@@ -644,20 +674,25 @@
if (validClassToWrap.isEnum()) {
enumConverter.ensureProgramEnumConversionClass(validClassToWrap, eventConsumer);
} else {
- validClassesToWrap.put(validClassToWrap.asProgramClass(), methods);
- ensureProgramWrappersWithoutVirtualMethods(validClassToWrap, eventConsumer);
+ validClassesToWrap.put(
+ validClassToWrap.asProgramClass(), descriptor.getMethods());
+ synthesizeProgramWrappersWithoutVirtualMethods(
+ validClassToWrap, descriptor.getSubwrappers(), eventConsumer);
}
}
});
validClassesToWrap.forEach(
(clazz, methods) ->
- ensureProgramWrappersVirtualMethods(clazz, methods, eventConsumer, processingContext));
+ synthesizeProgramWrappersVirtualMethods(
+ clazz, methods, eventConsumer, processingContext));
}
// We generate first the two wrappers with the constructor method and the fields, then we
// the two conversion methods which requires the wrappers to know both fields.
- private void ensureProgramWrappersWithoutVirtualMethods(
- DexClass context, DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
+ private void synthesizeProgramWrappersWithoutVirtualMethods(
+ DexClass context,
+ List<DexType> subwrappers,
+ DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer eventConsumer) {
assert eventConsumer != null;
assert context.isProgramClass();
DexType type = context.type;
@@ -673,13 +708,13 @@
vivifiedTypeFor(type),
programContext,
eventConsumer);
- getExistingProgramConversionMethod(
- kinds -> kinds.WRAPPER, programContext, wrapper, vivifiedWrapper);
- getExistingProgramConversionMethod(
- kinds -> kinds.VIVIFIED_WRAPPER, programContext, vivifiedWrapper, wrapper);
+ synthesizeProgramConversionMethod(
+ kinds -> kinds.WRAPPER, programContext, subwrappers, wrapper, vivifiedWrapper);
+ synthesizeProgramConversionMethod(
+ kinds -> kinds.VIVIFIED_WRAPPER, programContext, subwrappers, vivifiedWrapper, wrapper);
}
- private void ensureProgramWrappersVirtualMethods(
+ private void synthesizeProgramWrappersVirtualMethods(
DexProgramClass context,
Iterable<DexMethod> methods,
CfClassSynthesizerDesugaringEventConsumer eventConsumer,
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 f17235f..5c70c34 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
@@ -15,11 +15,13 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
public class HumanRewritingFlags {
@@ -443,6 +445,7 @@
}
public HumanRewritingFlags build() {
+ validate();
return new HumanRewritingFlags(
ImmutableMap.copyOf(rewritePrefix),
ImmutableSet.copyOf(dontRewritePrefix),
@@ -462,5 +465,19 @@
ImmutableMap.copyOf(amendLibraryMethod),
ImmutableMap.copyOf(amendLibraryField));
}
+
+ private void validate() {
+ SetView<DexType> dups =
+ Sets.intersection(customConversions.keySet(), wrapperConversions.keySet());
+ if (!dups.isEmpty()) {
+ throw reporter.fatalError(
+ new StringDiagnostic(
+ "Invalid desugared library configuration. "
+ + "Duplicate types in custom conversions and wrapper conversions: "
+ + String.join(
+ ", ", dups.stream().map(DexType::toString).collect(Collectors.toSet())),
+ origin));
+ }
+ }
}
}
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 16b0b9f..4aa99b6 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
@@ -145,7 +145,7 @@
return rewritingFlags.isEmulatedInterfaceRewrittenType(type);
}
- public Map<DexType, List<DexMethod>> getWrappers() {
+ public Map<DexType, WrapperDescriptor> getWrappers() {
return rewritingFlags.getWrappers();
}
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 e125c53..5ad734b 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
@@ -10,13 +10,12 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.IdentityHashMap;
-import java.util.List;
+import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
@@ -39,7 +38,7 @@
Map<DexMethod, DexMethod> emulatedVirtualRetargetThroughEmulatedInterface,
Map<DexMethod, DexMethod[]> apiGenericTypesConversion,
Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces,
- Map<DexType, List<DexMethod>> wrappers,
+ LinkedHashMap<DexType, WrapperDescriptor> wrappers,
Map<DexType, DexType> legacyBackport,
Set<DexType> dontRetarget,
Map<DexType, CustomConversionDescriptor> customConversions,
@@ -101,8 +100,8 @@
// Emulated interface descriptors.
private final Map<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces;
- // Wrappers and the list of methods they implement.
- private final Map<DexType, List<DexMethod>> wrappers;
+ // Wrapper descriptors.
+ private final LinkedHashMap<DexType, WrapperDescriptor> wrappers;
private final Map<DexType, DexType> legacyBackport;
private final Set<DexType> dontRetarget;
@@ -160,7 +159,7 @@
return emulatedInterfaces;
}
- public Map<DexType, List<DexMethod>> getWrappers() {
+ public Map<DexType, WrapperDescriptor> getWrappers() {
return wrappers;
}
@@ -249,7 +248,7 @@
ImmutableMap.builder();
private final ImmutableMap.Builder<DexType, EmulatedInterfaceDescriptor> emulatedInterfaces =
ImmutableMap.builder();
- private final ImmutableMap.Builder<DexType, List<DexMethod>> wrappers = ImmutableMap.builder();
+ private final LinkedHashMap<DexType, WrapperDescriptor> wrappers = new LinkedHashMap<>();
private final ImmutableMap.Builder<DexType, DexType> legacyBackport = ImmutableMap.builder();
private final ImmutableSet.Builder<DexType> dontRetarget = ImmutableSet.builder();
private final ImmutableMap.Builder<DexType, CustomConversionDescriptor> customConversions =
@@ -308,8 +307,8 @@
apiGenericTypesConversion.put(method, conversions);
}
- public void addWrapper(DexType wrapperConversion, List<DexMethod> methods) {
- wrappers.put(wrapperConversion, ImmutableList.copyOf(methods));
+ public void addWrapper(DexType type, WrapperDescriptor descriptor) {
+ this.wrappers.put(type, descriptor);
}
public void putLegacyBackport(DexType src, DexType target) {
@@ -367,7 +366,7 @@
emulatedVirtualRetargetThroughEmulatedInterface.build(),
apiGenericTypesConversion.build(),
emulatedInterfaces.build(),
- wrappers.build(),
+ wrappers,
legacyBackport.build(),
dontRetarget.build(),
customConversions.build(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java
new file mode 100644
index 0000000..f4a158c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/WrapperDescriptor.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import java.util.List;
+
+public class WrapperDescriptor {
+ private final List<DexMethod> methods;
+ private final List<DexType> subwrappers;
+
+ public WrapperDescriptor(List<DexMethod> methods, List<DexType> directSubtypes) {
+ this.methods = methods;
+ this.subwrappers = directSubtypes;
+ }
+
+ public List<DexMethod> getMethods() {
+ return methods;
+ }
+
+ public List<DexType> getSubwrappers() {
+ return subwrappers;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
index c0d9157..a7ed82c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineWrapperConverter.java
@@ -12,14 +12,19 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.WrapperDescriptor;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
@@ -37,6 +42,53 @@
HumanRewritingFlags rewritingFlags,
MachineRewritingFlags.Builder builder,
BiConsumer<String, Set<? extends DexReference>> warnConsumer) {
+ Map<DexType, WrapperDescriptorBuilder> descriptors = initializeDescriptors(rewritingFlags);
+ fillDescriptors(rewritingFlags, descriptors);
+ // The descriptors have to be ordered so that when processing a type, subtypes have been
+ // processed before.
+ LinkedHashMap<DexType, WrapperDescriptorBuilder> orderedDescriptors =
+ orderDescriptors(descriptors);
+ finalizeWrapperDescriptors(orderedDescriptors, builder);
+ warnConsumer.accept("The following types to wrap are missing: ", missingClasses);
+ }
+
+ private static class WrapperDescriptorBuilder {
+ private final List<DexMethod> methods = new ArrayList<>();
+ private final List<DexType> subwrappers = new ArrayList<>();
+
+ public WrapperDescriptorBuilder() {}
+
+ public List<DexMethod> getMethods() {
+ return methods;
+ }
+
+ public List<DexType> getSubwrappers() {
+ return subwrappers;
+ }
+
+ public void addSubwrapper(DexType type) {
+ subwrappers.add(type);
+ }
+
+ public WrapperDescriptor toWrapperDescriptor() {
+ methods.sort(DexMethod::compareTo);
+ subwrappers.sort(DexType::compareTo);
+ return new WrapperDescriptor(
+ ImmutableList.copyOf(methods), ImmutableList.copyOf(subwrappers));
+ }
+ }
+
+ private Map<DexType, WrapperDescriptorBuilder> initializeDescriptors(
+ HumanRewritingFlags rewritingFlags) {
+ Map<DexType, WrapperDescriptorBuilder> descriptors = new IdentityHashMap<>();
+ for (DexType wrapperType : rewritingFlags.getWrapperConversions().keySet()) {
+ descriptors.put(wrapperType, new WrapperDescriptorBuilder());
+ }
+ return descriptors;
+ }
+
+ private void fillDescriptors(
+ HumanRewritingFlags rewritingFlags, Map<DexType, WrapperDescriptorBuilder> descriptors) {
rewritingFlags
.getWrapperConversions()
.forEach(
@@ -44,53 +96,91 @@
DexClass wrapperClass = appInfo.definitionFor(wrapperType);
if (wrapperClass == null) {
missingClasses.add(wrapperType);
+ descriptors.remove(wrapperType);
return;
}
- List<DexMethod> methods;
- if (wrapperClass.isEnum()) {
- methods = ImmutableList.of();
- } else {
- methods = allImplementedMethods(wrapperClass, excludedMethods);
- methods.sort(DexMethod::compareTo);
- }
- builder.addWrapper(wrapperType, methods);
+ WrapperDescriptorBuilder descriptor = descriptors.get(wrapperType);
+ fillDescriptors(wrapperClass, excludedMethods, descriptor, descriptors);
});
- warnConsumer.accept("The following types to wrap are missing: ", missingClasses);
}
- private List<DexMethod> allImplementedMethods(
- DexClass wrapperClass, Set<DexMethod> excludedMethods) {
+ private LinkedHashMap<DexType, WrapperDescriptorBuilder> orderDescriptors(
+ Map<DexType, WrapperDescriptorBuilder> descriptors) {
+ LinkedHashMap<DexType, WrapperDescriptorBuilder> orderedDescriptors = new LinkedHashMap<>();
+ List<DexType> preOrdered = new ArrayList<>(descriptors.keySet());
+ preOrdered.sort(DexType::compareTo);
+ LinkedList<DexType> workList = new LinkedList<>(preOrdered);
+ while (!workList.isEmpty()) {
+ DexType dexType = workList.removeFirst();
+ WrapperDescriptorBuilder descriptor = descriptors.get(dexType);
+ List<DexType> subwrappers = descriptor.getSubwrappers();
+ if (Iterables.all(subwrappers, orderedDescriptors::containsKey)) {
+ orderedDescriptors.put(dexType, descriptor);
+ } else {
+ workList.addLast(dexType);
+ }
+ }
+ return orderedDescriptors;
+ }
+
+ private void finalizeWrapperDescriptors(
+ LinkedHashMap<DexType, WrapperDescriptorBuilder> descriptors,
+ MachineRewritingFlags.Builder builder) {
+ descriptors.forEach(
+ (type, descriptor) -> {
+ LinkedList<DexType> workList = new LinkedList<>(descriptor.getSubwrappers());
+ while (!workList.isEmpty()) {
+ DexType dexType = workList.removeFirst();
+ List<DexType> subwrappers = descriptors.get(dexType).getSubwrappers();
+ descriptor.getSubwrappers().removeAll(subwrappers);
+ workList.addAll(subwrappers);
+ }
+ builder.addWrapper(type, descriptor.toWrapperDescriptor());
+ });
+ }
+
+ private void fillDescriptors(
+ DexClass wrapperClass,
+ Set<DexMethod> excludedMethods,
+ WrapperDescriptorBuilder descriptor,
+ Map<DexType, WrapperDescriptorBuilder> descriptors) {
HashSet<Wrapper<DexMethod>> wrappers = new HashSet<>();
for (DexMethod excludedMethod : excludedMethods) {
wrappers.add(equivalence.wrap(excludedMethod));
}
LinkedList<DexClass> workList = new LinkedList<>();
- List<DexMethod> implementedMethods = new ArrayList<>();
+ List<DexMethod> implementedMethods = descriptor.getMethods();
workList.add(wrapperClass);
while (!workList.isEmpty()) {
DexClass dexClass = workList.removeFirst();
- for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) {
- if (!virtualMethod.isPrivateMethod()
- // Don't include hashCode and equals overrides, as hashCode and equals are added to
- // all wrappers regardless.
- && (!appInfo.dexItemFactory().objectMembers.hashCode.match(virtualMethod))
- && (!appInfo.dexItemFactory().objectMembers.equals.match(virtualMethod))) {
- assert virtualMethod.isProtectedMethod() || virtualMethod.isPublicMethod();
- boolean alreadyAdded = wrappers.contains(equivalence.wrap(virtualMethod.getReference()));
- // This looks quadratic but given the size of the collections met in practice for
- // desugared libraries (Max ~15) it does not matter.
- if (!alreadyAdded) {
- for (DexMethod alreadyImplementedMethod : implementedMethods) {
- if (alreadyImplementedMethod.match(virtualMethod.getReference())) {
- alreadyAdded = true;
- break;
+ if (dexClass != wrapperClass && descriptors.containsKey(dexClass.type)) {
+ descriptors.get(dexClass.type).addSubwrapper(wrapperClass.type);
+ }
+ if (!wrapperClass.isEnum()) {
+ for (DexEncodedMethod virtualMethod : dexClass.virtualMethods()) {
+ if (!virtualMethod.isPrivateMethod()
+ // Don't include hashCode and equals overrides, as hashCode and equals are added to
+ // all wrappers regardless.
+ && (!appInfo.dexItemFactory().objectMembers.hashCode.match(virtualMethod))
+ && (!appInfo.dexItemFactory().objectMembers.equals.match(virtualMethod))) {
+ assert virtualMethod.isProtectedMethod() || virtualMethod.isPublicMethod();
+ boolean alreadyAdded =
+ wrappers.contains(equivalence.wrap(virtualMethod.getReference()));
+ // This looks quadratic but given the size of the collections met in practice for
+ // desugared libraries (Max ~15) it does not matter.
+ if (!alreadyAdded) {
+ for (DexMethod alreadyImplementedMethod : implementedMethods) {
+ if (alreadyImplementedMethod.match(virtualMethod.getReference())) {
+ alreadyAdded = true;
+ break;
+ }
}
}
- }
- if (!alreadyAdded) {
- assert !virtualMethod.isFinal()
- : "Cannot wrap final method " + virtualMethod + " while wrapping " + wrapperClass;
- implementedMethods.add(virtualMethod.getReference());
+ if (!alreadyAdded) {
+ assert !virtualMethod.isFinal()
+ : "Cannot wrap final method " + virtualMethod + " while wrapping " + wrapperClass;
+ implementedMethods.add(virtualMethod.getReference());
+ }
}
}
}
@@ -107,6 +197,5 @@
workList.add(superClass);
}
}
- return implementedMethods;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
index a32d51e..8f0f2f4 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/apiconverter/NullableConversionCfCodeProvider.java
@@ -193,14 +193,19 @@
public static class WrapperConversionCfCodeProvider extends NullableConversionCfCodeProvider {
- DexField reverseWrapperField;
- DexField wrapperField;
+ private final DexField reverseWrapperField;
+ private final DexField wrapperField;
+ private final List<DexMethod> subwrapperConvertList;
public WrapperConversionCfCodeProvider(
- AppView<?> appView, DexField reverseWrapperField, DexField wrapperField) {
+ AppView<?> appView,
+ DexField reverseWrapperField,
+ DexField wrapperField,
+ List<DexMethod> subwrapperConvertList) {
super(appView, wrapperField.holder);
this.reverseWrapperField = reverseWrapperField;
this.wrapperField = wrapperField;
+ this.subwrapperConvertList = subwrapperConvertList;
}
@Override
@@ -228,6 +233,23 @@
instructions.add(unwrapDest);
instructions.add(frame.clone());
+ // if (arg instanceOf Subtype) {
+ // return SubtypeWrapper.convert((Subtype) arg)
+ // };
+ for (DexMethod convert : subwrapperConvertList) {
+ CfLabel dest = new CfLabel();
+ DexType convertArgType = convert.getArgumentType(0, true);
+ instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
+ instructions.add(new CfInstanceOf(convertArgType));
+ instructions.add(new CfIf(If.Type.EQ, ValueType.INT, dest));
+ instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
+ instructions.add(new CfCheckCast(convertArgType));
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, convert, false));
+ instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type)));
+ instructions.add(dest);
+ instructions.add(frame.clone());
+ }
+
// return new Wrapper(wrappedValue);
instructions.add(new CfNew(wrapperField.holder));
instructions.add(CfStackInstruction.fromAsm(Opcodes.DUP));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
index 278ec26..a61445b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/UnwrapConversionTest.java
@@ -14,9 +14,12 @@
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
import java.util.List;
import java.util.function.DoubleConsumer;
import java.util.function.IntConsumer;
+import java.util.stream.BaseStream;
+import java.util.stream.IntStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -30,7 +33,7 @@
private final CompilationSpecification compilationSpecification;
private static final AndroidApiLevel MIN_SUPPORTED = AndroidApiLevel.N;
- private static final String EXPECTED_RESULT = StringUtils.lines("true", "true");
+ private static final String EXPECTED_RESULT = StringUtils.lines("true", "true", "true", "true");
@Parameters(name = "{0}, spec: {1}, {2}")
public static List<Object[]> data() {
@@ -64,6 +67,22 @@
@SuppressWarnings("all")
public static void main(String[] args) {
+ consumerTest();
+ streamTest();
+ }
+
+ private static void streamTest() {
+ // Type wrapper.
+ IntStream intStream = Arrays.stream(new int[] {1});
+ BaseStream<?, ?> unwrapped = CustomLibClass.identity(intStream);
+ System.out.println(unwrapped == intStream);
+
+ // Vivified wrapper.
+ IntStream consumer = CustomLibClass.getStream();
+ System.out.println(CustomLibClass.testStream(consumer));
+ }
+
+ private static void consumerTest() {
// Type wrapper.
IntConsumer intConsumer = i -> {};
IntConsumer unwrappedIntConsumer = CustomLibClass.identity(intConsumer);
@@ -95,5 +114,21 @@
public static boolean testConsumer(DoubleConsumer doubleConsumer) {
return doubleConsumer == consumer;
}
+
+ private static IntStream intStream = Arrays.stream(new int[] {0});
+
+ @SuppressWarnings("WeakerAccess")
+ public static BaseStream<Integer, IntStream> identity(IntStream arg) {
+ return arg;
+ }
+
+ public static IntStream getStream() {
+ return intStream;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ public static boolean testStream(BaseStream<?, ?> stream) {
+ return stream == intStream;
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
index 54c88bf..7721551 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/InputStreamTransferToTest.java
@@ -4,7 +4,7 @@
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.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
import com.android.tools.r8.TestParameters;
@@ -38,9 +38,9 @@
@Parameters(name = "{0}, spec: {1}, {2}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(),
+ getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build(),
ImmutableList.of(JDK11_PATH),
- DEFAULT_SPECIFICATIONS);
+ SPECIFICATIONS_WITH_CF2CF);
}
public InputStreamTransferToTest(