Merge commit '2d4b80a04683884aa3a71fe18b81e32b8d01412f' into dev-release
diff --git a/.gitignore b/.gitignore
index 405e837..59981e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -130,6 +130,8 @@
third_party/kotlin/kotlin-compiler-dev
third_party/kotlinx-coroutines-1.3.6.tar.gz
third_party/kotlinx-coroutines-1.3.6
+third_party/multidex
+third_party/multidex.tar.gz
third_party/nest/*
third_party/openjdk/desugar_jdk_libs
third_party/openjdk/desugar_jdk_libs.tar.gz
diff --git a/build.gradle b/build.gradle
index 812b438..eca19a7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,8 +8,8 @@
import dx.DxTask
import net.ltgt.gradle.errorprone.CheckSeverity
import org.gradle.internal.os.OperatingSystem
-import smali.SmaliTask
import tasks.DownloadDependency
+import smali.SmaliTask
import tasks.GetJarsFromConfiguration
import utils.Utils
@@ -378,6 +378,7 @@
"kotlin/kotlin-compiler-1.6.0",
"kotlin/kotlin-compiler-1.7.0",
"kotlinx-coroutines-1.3.6",
+ "multidex",
"openjdk/openjdk-rt-1.8",
"openjdk/desugar_jdk_libs",
"openjdk/desugar_jdk_libs_11",
diff --git a/src/library_desugar/java/j$/nio/file/Path.java b/src/library_desugar/java/j$/nio/file/Path.java
index 781ab5f..6030764 100644
--- a/src/library_desugar/java/j$/nio/file/Path.java
+++ b/src/library_desugar/java/j$/nio/file/Path.java
@@ -9,4 +9,8 @@
public static j$.nio.file.Path wrap_convert(java.nio.file.Path path) {
return null;
}
+
+ public static java.nio.file.Path wrap_convert(j$.nio.file.Path path) {
+ return null;
+ }
}
diff --git a/src/library_desugar/java/java/nio/file/PathApiFlips.java b/src/library_desugar/java/java/nio/file/PathApiFlips.java
new file mode 100644
index 0000000..7314b7f
--- /dev/null
+++ b/src/library_desugar/java/java/nio/file/PathApiFlips.java
@@ -0,0 +1,137 @@
+// 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 java.io.IOException;
+import java.util.Iterator;
+import java.util.function.Consumer;
+
+/** For all wrappers, the spliterator is based on the iterator so no need to wrap it. */
+public class PathApiFlips {
+
+ public static Iterator<?> flipIteratorPath(Iterator<?> iterator) {
+ return new IteratorPathWrapper<>(iterator);
+ }
+
+ public static Iterable<?> flipIterablePath(Iterable<?> iterable) {
+ return new IterablePathWrapper<>(iterable);
+ }
+
+ public static java.nio.file.DirectoryStream<?> flipDirectoryStreamPath(
+ java.nio.file.DirectoryStream<?> directoryStream) {
+ return new DirectoryStreamPathWrapper<>(directoryStream);
+ }
+
+ public static java.nio.file.DirectoryStream.Filter<?> flipDirectoryStreamFilterPath(
+ java.nio.file.DirectoryStream.Filter<?> filter) {
+ return new DirectoryStreamFilterWrapper<>(filter);
+ }
+
+ /**
+ * The generic types inherit from java.lang.Object even if in practice it seems they are
+ * exclusively used with Path. To be conservative, we return the parameter if it's not a path so
+ * the code works for non Path objects.
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T convertPath(T maybePath) {
+ if (maybePath == null) {
+ return null;
+ }
+ if (maybePath instanceof java.nio.file.Path) {
+ return (T) j$.nio.file.Path.wrap_convert((java.nio.file.Path) maybePath);
+ }
+ if (maybePath instanceof j$.nio.file.Path) {
+ return (T) j$.nio.file.Path.wrap_convert((j$.nio.file.Path) maybePath);
+ }
+ return maybePath;
+ }
+
+ public static class DirectoryStreamFilterWrapper<T>
+ implements java.nio.file.DirectoryStream.Filter<T> {
+
+ private final java.nio.file.DirectoryStream.Filter<T> filter;
+
+ public DirectoryStreamFilterWrapper(java.nio.file.DirectoryStream.Filter<T> filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public boolean accept(T t) throws IOException {
+ return filter.accept(convertPath(t));
+ }
+ }
+
+ public static class IterablePathWrapper<T> implements Iterable<T> {
+
+ private final Iterable<T> iterable;
+
+ public IterablePathWrapper(Iterable<T> iterable) {
+ this.iterable = iterable;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new IteratorPathWrapper<>(iterable.iterator());
+ }
+
+ @Override
+ public void forEach(Consumer<? super T> action) {
+ iterable.forEach(path -> action.accept(convertPath(path)));
+ }
+ }
+
+ public static class IteratorPathWrapper<T> implements Iterator<T> {
+
+ private final Iterator<T> iterator;
+
+ public IteratorPathWrapper(Iterator<T> iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public T next() {
+ return convertPath(iterator.next());
+ }
+
+ @Override
+ public void remove() {
+ iterator.remove();
+ }
+
+ @Override
+ public void forEachRemaining(Consumer<? super T> action) {
+ iterator.forEachRemaining(path -> action.accept(convertPath(path)));
+ }
+ }
+
+ public static class DirectoryStreamPathWrapper<T> implements DirectoryStream<T> {
+
+ private final DirectoryStream<T> directoryStream;
+
+ public DirectoryStreamPathWrapper(DirectoryStream<T> directoryStream) {
+ this.directoryStream = directoryStream;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return new IteratorPathWrapper<>(directoryStream.iterator());
+ }
+
+ @Override
+ public void forEach(Consumer<? super T> action) {
+ directoryStream.forEach(path -> action.accept(convertPath(path)));
+ }
+
+ @Override
+ public void close() throws IOException {
+ directoryStream.close();
+ }
+ }
+}
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs.json b/src/library_desugar/jdk11/desugar_jdk_libs.json
index 9cc01cc..f47fe72 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs.json
@@ -6,6 +6,22 @@
"support_all_callbacks_from_library": true,
"common_flags": [
{
+ "api_level_below_or_equal": 10000,
+ "api_level_greater_or_equal": 24,
+ "rewrite_prefix": {
+ "java.util.stream.DesugarDoubleStream": "j$.util.stream.DesugarDoubleStream",
+ "java.util.stream.DesugarIntStream": "j$.util.stream.DesugarIntStream",
+ "java.util.stream.DesugarLongStream": "j$.util.stream.DesugarLongStream",
+ "java.util.stream.DesugarStream": "j$.util.stream.DesugarStream"
+ },
+ "retarget_method": {
+ "java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)": "java.util.stream.DesugarDoubleStream",
+ "java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)": "java.util.stream.DesugarIntStream",
+ "java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)": "java.util.stream.DesugarLongStream",
+ "java.util.stream.Stream java.util.stream.Stream#iterate(java.lang.Object, java.util.function.Predicate, java.util.function.UnaryOperator)": "java.util.stream.DesugarStream"
+ }
+ },
+ {
"api_level_below_or_equal": 32,
"rewrite_prefix": {
"java.util.concurrent.DesugarTimeUnit": "j$.util.concurrent.DesugarTimeUnit"
@@ -229,6 +245,15 @@
],
"program_flags": [
{
+ "api_level_below_or_equal": 10000,
+ "amend_library_method": [
+ "public static java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)",
+ "public static java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)",
+ "public static java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)",
+ "public static java.util.stream.Stream java.util.stream.Stream#iterate(java.lang.Object, java.util.function.Predicate, java.util.function.UnaryOperator)"
+ ]
+ },
+ {
"api_level_below_or_equal": 33,
"amend_library_method": [
"public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
index c62cb34..ff05a80 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_nio.json
@@ -6,6 +6,22 @@
"support_all_callbacks_from_library": true,
"common_flags": [
{
+ "api_level_below_or_equal": 10000,
+ "api_level_greater_or_equal": 24,
+ "rewrite_prefix": {
+ "java.util.stream.DesugarDoubleStream": "j$.util.stream.DesugarDoubleStream",
+ "java.util.stream.DesugarIntStream": "j$.util.stream.DesugarIntStream",
+ "java.util.stream.DesugarLongStream": "j$.util.stream.DesugarLongStream",
+ "java.util.stream.DesugarStream": "j$.util.stream.DesugarStream"
+ },
+ "retarget_method": {
+ "java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)": "java.util.stream.DesugarDoubleStream",
+ "java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)": "java.util.stream.DesugarIntStream",
+ "java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)": "java.util.stream.DesugarLongStream",
+ "java.util.stream.Stream java.util.stream.Stream#iterate(java.lang.Object, java.util.function.Predicate, java.util.function.UnaryOperator)": "java.util.stream.DesugarStream"
+ }
+ },
+ {
"api_level_below_or_equal": 32,
"rewrite_prefix": {
"java.net.URLDecoder": "j$.net.URLDecoder",
@@ -152,6 +168,7 @@
"java.nio.file.ClosedWatchServiceException",
"java.nio.file.DirectoryIteratorException",
"java.nio.file.DirectoryNotEmptyException",
+ "java.nio.file.DirectoryStream",
"java.nio.file.FileAlreadyExistsException",
"java.nio.file.FileSystemAlreadyExistsException",
"java.nio.file.FileSystemException",
@@ -173,6 +190,7 @@
"java.nio.file.ClosedWatchServiceException",
"java.nio.file.DirectoryIteratorException",
"java.nio.file.DirectoryNotEmptyException",
+ "java.nio.file.DirectoryStream",
"java.nio.file.FileAlreadyExistsException",
"java.nio.file.FileSystemAlreadyExistsException",
"java.nio.file.FileSystemException",
@@ -199,7 +217,10 @@
"java.nio.file.attribute.BasicFileAttributes java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.Class, java.nio.file.LinkOption[])": [1, "java.lang.Class java.nio.file.FileApiFlips#flipFileAttributes(java.lang.Class)"],
"java.util.Set java.nio.file.attribute.PosixFileAttributes#permissions()": [-1, "java.util.Set java.nio.file.FileApiFlips#flipPosixPermissionSet(java.util.Set)"],
"void java.nio.file.attribute.PosixFileAttributeView#setPermissions(java.util.Set)": [0, "java.util.Set java.nio.file.FileApiFlips#flipPosixPermissionSet(java.util.Set)"],
- "java.util.Map java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption[])" : [-1, "java.util.Map java.nio.file.FileApiFlips#flipMapWithMaybeFileTimeValues(java.util.Map)"]
+ "java.util.Map java.nio.file.spi.FileSystemProvider#readAttributes(java.nio.file.Path, java.lang.String, java.nio.file.LinkOption[])" : [-1, "java.util.Map java.nio.file.FileApiFlips#flipMapWithMaybeFileTimeValues(java.util.Map)"],
+ "java.lang.Iterable java.nio.file.FileSystem#getRootDirectories()": [-1, "java.lang.Iterable java.nio.file.PathApiFlips#flipIterablePath(java.lang.Iterable)"],
+ "java.util.Iterator java.nio.file.Path#iterator()": [-1, "java.util.Iterator java.nio.file.PathApiFlips#flipIteratorPath(java.util.Iterator)"],
+ "java.nio.file.DirectoryStream java.nio.file.spi.FileSystemProvider#newDirectoryStream(java.nio.file.Path, java.nio.file.DirectoryStream$Filter)": [-1, "java.nio.file.DirectoryStream java.nio.file.PathApiFlips#flipDirectoryStreamPath(java.nio.file.DirectoryStream)", 1, "java.nio.file.DirectoryStream$Filter java.nio.file.PathApiFlips#flipDirectoryStreamFilterPath(java.nio.file.DirectoryStream$Filter)"]
},
"wrapper_conversion": [
"java.nio.channels.CompletionHandler",
@@ -219,13 +240,12 @@
"java.nio.file.StandardOpenOption",
"java.nio.file.LinkOption",
"java.nio.file.CopyOption",
+ "java.nio.file.StandardCopyOption",
"java.nio.file.attribute.GroupPrincipal",
"java.nio.file.attribute.FileAttribute",
"java.nio.file.attribute.UserPrincipal",
"java.nio.file.FileStore",
"java.nio.file.attribute.FileStoreAttributeView",
- "java.nio.file.DirectoryStream$Filter",
- "java.nio.file.DirectoryStream",
"java.nio.file.attribute.PosixFilePermission",
"java.nio.file.attribute.BasicFileAttributes",
"java.nio.file.attribute.PosixFileAttributes",
@@ -374,6 +394,15 @@
],
"program_flags": [
{
+ "api_level_below_or_equal": 10000,
+ "amend_library_method": [
+ "public static java.util.stream.DoubleStream java.util.stream.DoubleStream#iterate(double, java.util.function.DoublePredicate, java.util.function.DoubleUnaryOperator)",
+ "public static java.util.stream.IntStream java.util.stream.IntStream#iterate(int, java.util.function.IntPredicate, java.util.function.IntUnaryOperator)",
+ "public static java.util.stream.LongStream java.util.stream.LongStream#iterate(long, java.util.function.LongPredicate, java.util.function.LongUnaryOperator)",
+ "public static java.util.stream.Stream java.util.stream.Stream#iterate(java.lang.Object, java.util.function.Predicate, java.util.function.UnaryOperator)"
+ ]
+ },
+ {
"api_level_below_or_equal": 33,
"amend_library_method": [
"public java.lang.Object[] java.util.Collection#toArray(java.util.function.IntFunction)"
@@ -418,12 +447,6 @@
}
},
{
- "api_level_below_or_equal": 19,
- "dont_retarget": [
- "android.support.multidex.MultiDexExtractor$ExtractedDex"
- ]
- },
- {
"api_level_below_or_equal": 18,
"rewrite_prefix": {
"java.lang.DesugarCharacter": "j$.lang.DesugarCharacter"
@@ -556,6 +579,11 @@
"rewrite_prefix": {
"java.lang.DesugarCharacter": "j$.lang.DesugarCharacter"
},
+ "retarget_method" : {
+ "boolean java.lang.Character#isSurrogate(char)" : "java.lang.DesugarCharacter",
+ "char java.lang.Character#highSurrogate(int)" : "java.lang.DesugarCharacter",
+ "char java.lang.Character#lowSurrogate(int)" : "java.lang.DesugarCharacter"
+ },
"retarget_static_field": {
"sun.nio.cs.US_ASCII sun.nio.cs.US_ASCII#INSTANCE": "java.nio.charset.Charset java.nio.charset.StandardCharsets#US_ASCII",
"sun.nio.cs.ISO_8859_1 sun.nio.cs.ISO_8859_1#INSTANCE": "java.nio.charset.Charset java.nio.charset.StandardCharsets#ISO_8859_1",
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 a5d6098..24d6362 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -98,7 +98,7 @@
DexType baseType = method.holder.toBaseType(appView.dexItemFactory());
if (shouldKeep(baseType)) {
keepClass(baseType);
- if (!method.holder.isArrayType()) {
+ if (!method.holder.isArrayType() && !isVivifiedType(baseType)) {
toKeep.get(method.holder).methods.add(method);
}
}
@@ -117,7 +117,7 @@
DexType baseType = field.holder.toBaseType(appView.dexItemFactory());
if (shouldKeep(baseType)) {
keepClass(baseType);
- if (!field.holder.isArrayType()) {
+ if (!field.holder.isArrayType() && !isVivifiedType(baseType)) {
toKeep.get(field.holder).fields.add(field);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index b8613b8..ad59604 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.dex.code.DexReturnVoid;
import com.android.tools.r8.dex.code.DexSwitchPayload;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEvent.SetPositionFrame;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
@@ -508,7 +507,11 @@
DexInstruction lastInstruction = ArrayUtils.last(instructions);
debugInfo = advanceToOffset(lastInstruction.getOffset(), debugInfo, debugInfoIterator);
if (debugInfo != null) {
- throw new Unreachable("Could not print all debug information.");
+ builder
+ .append("(warning: has unhandled debug events @ pc:")
+ .append(debugInfo.address)
+ .append(", line:")
+ .append(debugInfo.line);
} else {
builder.append("(has debug events past last pc)\n");
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 3ee7c50..d838596 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -469,6 +469,45 @@
floatBufferType,
doubleBufferType);
+ private static final List<String> MULTIDEX_PREFIXES =
+ ImmutableList.of("androidx/", "android/support/");
+ private static final List<String> MULTIDEX_SUFFIXES =
+ ImmutableList.of(
+ "multidex/MultiDex$V14$ElementConstructor;",
+ "multidex/MultiDex$V14$ICSElementConstructor;",
+ "multidex/MultiDex$V14$JBMR11ElementConstructor;",
+ "multidex/MultiDex$V14$JBMR2ElementConstructor;",
+ "multidex/MultiDex$V14;",
+ "multidex/MultiDex$V19;",
+ "multidex/MultiDex$V21_PLUS;",
+ "multidex/MultiDex$V4;",
+ "multidex/MultiDexApplication;",
+ "multidex/MultiDexExtractor$1;",
+ "multidex/MultiDexExtractor$ExtractedDex;",
+ "multidex/MultiDexExtractor;",
+ "multidex/MultiDex;",
+ "multidex/ZipUtil;",
+ "multidex/ZipUtil$CentralDirectory;");
+ private static final List<String> MULTIDEX_INSTRUMENTATION =
+ ImmutableList.of(
+ "Landroid/support/multidex/instrumentation/BuildConfig;",
+ "Landroid/test/runner/MultiDexTestRunner;");
+
+ private List<DexType> createMultiDexTypes() {
+ ImmutableList.Builder<DexType> builder = ImmutableList.builder();
+ for (String prefix : MULTIDEX_PREFIXES) {
+ for (String suffix : MULTIDEX_SUFFIXES) {
+ builder.add(createType("L" + prefix + suffix));
+ }
+ }
+ for (String typeString : MULTIDEX_INSTRUMENTATION) {
+ builder.add(createType(typeString));
+ }
+ return builder.build();
+ }
+
+ public List<DexType> multiDexTypes = createMultiDexTypes();
+
public final DexType doubleConsumer =
createStaticallyKnownType("Ljava/util/function/DoubleConsumer;");
public final DexType longConsumer =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 09b456b..8770cd1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPICallbackSynthesizer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.DesugaredLibraryDisableDesugarerPostProcessor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterPostProcessor;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
@@ -72,6 +73,11 @@
if (recordRewriter != null) {
desugarings.add(recordRewriter);
}
+ DesugaredLibraryDisableDesugarerPostProcessor disableDesugarer =
+ DesugaredLibraryDisableDesugarerPostProcessor.create(appView);
+ if (disableDesugarer != null) {
+ desugarings.add(disableDesugarer);
+ }
if (desugarings.isEmpty()) {
return empty();
}
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 a42d6e5..54837a8 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.desugar.apimodel.ApiInvokeOutlinerDesugaring;
import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicInstructionDesugaring;
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer.DesugaredLibraryDisableDesugarer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.icce.AlwaysThrowingInstructionDesugaring;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
@@ -57,6 +58,7 @@
private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final InterfaceMethodRewriter interfaceMethodRewriter;
private final DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
+ private final DesugaredLibraryDisableDesugarer disableDesugarer;
private final AndroidApiLevelCompute apiLevelCompute;
NonEmptyCfInstructionDesugaringCollection(
@@ -76,6 +78,7 @@
this.desugaredLibraryRetargeter = null;
this.interfaceMethodRewriter = null;
this.desugaredLibraryAPIConverter = null;
+ this.disableDesugarer = null;
return;
}
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
@@ -87,6 +90,10 @@
if (desugaredLibraryRetargeter != null) {
desugarings.add(desugaredLibraryRetargeter);
}
+ disableDesugarer = DesugaredLibraryDisableDesugarer.create(appView);
+ if (disableDesugarer != null) {
+ desugarings.add(disableDesugarer);
+ }
if (appView.options().apiModelingOptions().enableOutliningOfMethods) {
yieldingDesugarings.add(new ApiInvokeOutlinerDesugaring(appView, apiLevelCompute));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
index eeebd32..2d72b93 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPIConverter.java
@@ -103,6 +103,9 @@
if (isAPIConversionSyntheticType(context.getHolderType(), wrapperSynthesizor, appView)) {
return false;
}
+ if (appView.dexItemFactory().multiDexTypes.contains(context.getHolderType())) {
+ return false;
+ }
return shouldRewriteInvoke(instruction.asInvoke(), context);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarer.java
new file mode 100644
index 0000000..bd4b771
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarer.java
@@ -0,0 +1,101 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer;
+
+import com.android.tools.r8.cf.code.CfFieldInstruction;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfTypeInstruction;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Disables the rewriting of types in specific classes declared in the desugared library
+ * specification, typically classes that are used pre-native multidex.
+ */
+public class DesugaredLibraryDisableDesugarer implements CfInstructionDesugaring {
+
+ private final AppView<?> appView;
+ private final DesugaredLibraryDisableDesugarerHelper helper;
+
+ public DesugaredLibraryDisableDesugarer(AppView<?> appView) {
+ this.appView = appView;
+ this.helper = new DesugaredLibraryDisableDesugarerHelper(appView);
+ }
+
+ public static DesugaredLibraryDisableDesugarer create(AppView<?> appView) {
+ return DesugaredLibraryDisableDesugarerHelper.shouldCreate(appView)
+ ? new DesugaredLibraryDisableDesugarer(appView)
+ : null;
+ }
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ CfInstructionDesugaringCollection desugaringCollection,
+ DexItemFactory dexItemFactory) {
+ CfInstruction replacement = rewriteInstruction(instruction, context);
+ return replacement == null ? null : Collections.singleton(replacement);
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return rewriteInstruction(instruction, context) != null;
+ }
+
+ private CfInstruction rewriteInstruction(CfInstruction instruction, ProgramMethod context) {
+ if (!appView.dexItemFactory().multiDexTypes.contains(context.getHolderType())) {
+ return null;
+ }
+ if (instruction.isTypeInstruction()) {
+ return rewriteTypeInstruction(instruction.asTypeInstruction());
+ }
+ if (instruction.isFieldInstruction()) {
+ return rewriteFieldInstruction(instruction.asFieldInstruction(), context);
+ }
+ if (instruction.isInvoke()) {
+ return rewriteInvokeInstruction(instruction.asInvoke(), context);
+ }
+ return null;
+ }
+
+ private CfInstruction rewriteInvokeInstruction(CfInvoke invoke, ProgramMethod context) {
+ DexMethod rewrittenMethod =
+ helper.rewriteMethod(invoke.getMethod(), invoke.isInterface(), context);
+ return rewrittenMethod != null
+ ? new CfInvoke(invoke.getOpcode(), rewrittenMethod, invoke.isInterface())
+ : null;
+ }
+
+ private CfFieldInstruction rewriteFieldInstruction(
+ CfFieldInstruction fieldInstruction, ProgramMethod context) {
+ DexField rewrittenField = helper.rewriteField(fieldInstruction.getField(), context);
+ return rewrittenField != null ? fieldInstruction.createWithField(rewrittenField) : null;
+ }
+
+ private CfInstruction rewriteTypeInstruction(CfTypeInstruction typeInstruction) {
+ DexType rewrittenType = helper.rewriteType(typeInstruction.getType());
+ return rewrittenType != typeInstruction.getType()
+ ? typeInstruction.withType(rewrittenType)
+ : null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarerHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarerHelper.java
new file mode 100644
index 0000000..f4526ae
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarerHelper.java
@@ -0,0 +1,118 @@
+// 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.disabledesugarer;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.methodWithVivifiedTypeInSignature;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter.vivifiedTypeFor;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldResolutionResult;
+import com.android.tools.r8.graph.MemberResolutionResult;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.SuccessfulMemberResolutionResult;
+
+public class DesugaredLibraryDisableDesugarerHelper {
+
+ private final AppView<?> appView;
+
+ public DesugaredLibraryDisableDesugarerHelper(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ static boolean shouldCreate(AppView<?> appView) {
+ for (DexType multiDexType : appView.dexItemFactory().multiDexTypes) {
+ if (appView.appInfo().definitionForWithoutExistenceAssert(multiDexType) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ DexMethod rewriteMethod(DexMethod method, boolean isInterface, ProgramMethod context) {
+ DexType newHolder = rewriteType(method.getHolderType());
+ DexMethod rewrittenMethod = methodWithVivifiedTypeInSignature(method, newHolder, appView);
+ if (rewrittenMethod == method) {
+ return null;
+ }
+ MethodResolutionResult methodResolutionResult =
+ appView.appInfoForDesugaring().resolveMethodLegacy(method, isInterface);
+ warnIfInvalidResolution(methodResolutionResult, method, context);
+ return rewrittenMethod;
+ }
+
+ DexField rewriteField(DexField field, ProgramDefinition context) {
+ if (isRewrittenType(field.getHolderType())) {
+ // This case never happens within the supported set of classes. We can support it if required.
+ appView
+ .options()
+ .reporter
+ .error("Cannot prevent the desugaring of " + field + " in " + context);
+ return null;
+ }
+ DexType rewrittenFieldType = rewriteType(field.getType());
+ if (rewrittenFieldType == field.getType()) {
+ return null;
+ }
+ FieldResolutionResult fieldResolutionResult =
+ appView.appInfoForDesugaring().resolveField(field);
+ warnIfInvalidResolution(fieldResolutionResult, field, context);
+ return field.withType(rewrittenFieldType, appView.dexItemFactory());
+ }
+
+ /**
+ * All rewritings should apply within private members of multidex types or with library accesses,
+ * else we are leaving escapes of non rewritten types in the program which will lead to runtime
+ * errors. Note that this is conservative and we could allow more escapes if required.
+ */
+ private boolean isValidResolution(MemberResolutionResult<?, ?> resolutionResult) {
+ if (resolutionResult == null || !resolutionResult.isSuccessfulMemberResolutionResult()) {
+ return false;
+ }
+ SuccessfulMemberResolutionResult<?, ?> successfulResult =
+ resolutionResult.asSuccessfulMemberResolutionResult();
+ if (successfulResult.getResolvedHolder().isLibraryClass()) {
+ return true;
+ }
+ return appView
+ .dexItemFactory()
+ .multiDexTypes
+ .contains(successfulResult.getResolvedHolder().getType())
+ && successfulResult.getResolvedMember().isPrivate();
+ }
+
+ private void warnIfInvalidResolution(
+ MemberResolutionResult<?, ?> resolutionResult,
+ DexMember<?, ?> member,
+ ProgramDefinition context) {
+ if (isValidResolution(resolutionResult)) {
+ return;
+ }
+ appView
+ .reporter()
+ .warning(
+ "Preventing the desugaring of "
+ + member
+ + " in "
+ + context
+ + " which could be an invalid escape into the program. ");
+ }
+
+ DexType rewriteType(DexType type) {
+ if (isRewrittenType(type)) {
+ return vivifiedTypeFor(type, appView);
+ }
+ return type;
+ }
+
+ boolean isRewrittenType(DexType type) {
+ return appView.typeRewriter.hasRewrittenType(type, appView);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarerPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarerPostProcessor.java
new file mode 100644
index 0000000..2e26d1e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/disabledesugarer/DesugaredLibraryDisableDesugarerPostProcessor.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.desugar.desugaredlibrary.disabledesugarer;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class DesugaredLibraryDisableDesugarerPostProcessor implements CfPostProcessingDesugaring {
+
+ private final AppView<?> appView;
+ private final DesugaredLibraryDisableDesugarerHelper helper;
+
+ public DesugaredLibraryDisableDesugarerPostProcessor(AppView<?> appView) {
+ this.appView = appView;
+ this.helper = new DesugaredLibraryDisableDesugarerHelper(appView);
+ }
+
+ public static DesugaredLibraryDisableDesugarerPostProcessor create(AppView<?> appView) {
+ return DesugaredLibraryDisableDesugarerHelper.shouldCreate(appView)
+ ? new DesugaredLibraryDisableDesugarerPostProcessor(appView)
+ : null;
+ }
+
+ @Override
+ public void postProcessingDesugaring(
+ Collection<DexProgramClass> programClasses,
+ CfPostProcessingDesugaringEventConsumer eventConsumer,
+ ExecutorService executorService)
+ throws ExecutionException {
+ for (DexType multiDexType : appView.dexItemFactory().multiDexTypes) {
+ DexClass clazz =
+ appView.appInfoForDesugaring().definitionForWithoutExistenceAssert(multiDexType);
+ if (clazz != null && clazz.isProgramClass()) {
+ rewriteMultiDexProgramClass(clazz.asProgramClass());
+ }
+ }
+ }
+
+ private void rewriteMultiDexProgramClass(DexProgramClass multiDexProgramClass) {
+ multiDexProgramClass.setInstanceFields(
+ rewriteFields(multiDexProgramClass.instanceFields(), multiDexProgramClass));
+ multiDexProgramClass.setStaticFields(
+ rewriteFields(multiDexProgramClass.staticFields(), multiDexProgramClass));
+ }
+
+ private DexEncodedField[] rewriteFields(
+ List<DexEncodedField> fields, DexProgramClass multiDexProgramClass) {
+ List<DexEncodedField> newFields = new ArrayList<>();
+ for (DexEncodedField field : fields) {
+ DexField rewrittenField = helper.rewriteField(field.getReference(), multiDexProgramClass);
+ newFields.add(
+ rewrittenField != null ? field.toTypeSubstitutedField(appView, rewrittenField) : field);
+ }
+ return newFields.toArray(DexEncodedField[]::new);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
index d77a0bc..736632a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/humanspecification/MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser.CONFIGURATION_FORMAT_VERSION_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.AMEND_LIBRARY_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_GENERIC_TYPES_CONVERSION;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_BELOW_OR_EQUAL_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.API_LEVEL_GREATER_OR_EQUAL_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.BACKPORT_KEY;
@@ -14,13 +15,17 @@
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.CUSTOM_CONVERSION_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_RETARGET_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_REWRITE_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.DONT_REWRITE_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.EMULATE_INTERFACE_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.IDENTIFIER_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.LIBRARY_FLAGS_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.MAINTAIN_PREFIX_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.NEVER_OUTLINE_API_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.PROGRAM_FLAGS_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REQUIRED_COMPILATION_API_LEVEL_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_EMULATED_DISPATCH_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_METHOD_KEY;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.RETARGET_STATIC_FIELD_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_DERIVED_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.REWRITE_PREFIX_KEY;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecificationParser.SHRINKER_CONFIG_KEY;
@@ -32,13 +37,15 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AccessFlags;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.ApiLevelRange;
import com.google.gson.Gson;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -102,12 +109,21 @@
.forEach((k, v) -> rewriteDerivedPrefix.put(k, new TreeMap<>(v)));
toJson.put(REWRITE_DERIVED_PREFIX_KEY, rewriteDerivedPrefix);
}
+ if (!flags.getDontRewritePrefix().isEmpty()) {
+ toJson.put(DONT_REWRITE_PREFIX_KEY, stringSetToString(flags.getDontRewritePrefix()));
+ }
+ if (!flags.getMaintainPrefix().isEmpty()) {
+ toJson.put(MAINTAIN_PREFIX_KEY, stringSetToString(flags.getMaintainPrefix()));
+ }
if (!flags.getEmulatedInterfaces().isEmpty()) {
toJson.put(EMULATE_INTERFACE_KEY, mapToString(flags.getEmulatedInterfaces()));
}
if (!flags.getDontRewriteInvocation().isEmpty()) {
toJson.put(DONT_REWRITE_KEY, setToString(flags.getDontRewriteInvocation()));
}
+ if (!flags.getRetargetStaticField().isEmpty()) {
+ toJson.put(RETARGET_STATIC_FIELD_KEY, mapToString(flags.getRetargetStaticField()));
+ }
if (!flags.getRetargetMethod().isEmpty()) {
toJson.put(RETARGET_METHOD_KEY, mapToString(flags.getRetargetMethod()));
}
@@ -122,15 +138,24 @@
if (!flags.getLegacyBackport().isEmpty()) {
toJson.put(BACKPORT_KEY, mapToString(flags.getLegacyBackport()));
}
+ if (!flags.getApiGenericConversion().isEmpty()) {
+ toJson.put(API_GENERIC_TYPES_CONVERSION, mapArrayToString(flags.getApiGenericConversion()));
+ }
if (!flags.getWrapperConversions().isEmpty()) {
registerWrapperConversions(toJson, flags.getWrapperConversions());
}
if (!flags.getCustomConversions().isEmpty()) {
toJson.put(CUSTOM_CONVERSION_KEY, mapToString(flags.getCustomConversions()));
}
+ if (!flags.getNeverOutlineApi().isEmpty()) {
+ toJson.put(NEVER_OUTLINE_API_KEY, setToString(flags.getNeverOutlineApi()));
+ }
if (!flags.getAmendLibraryMethod().isEmpty()) {
toJson.put(AMEND_LIBRARY_METHOD_KEY, amendLibraryToString(flags.getAmendLibraryMethod()));
}
+ if (!flags.getAmendLibraryField().isEmpty()) {
+ toJson.put(AMEND_LIBRARY_METHOD_KEY, amendLibraryToString(flags.getAmendLibraryField()));
+ }
list.add(toJson);
}
return list;
@@ -148,20 +173,32 @@
stringMap.put(toString(k), setToString(v));
}
});
- toJson.put(WRAPPER_CONVERSION_KEY, stringSet);
- toJson.put(WRAPPER_CONVERSION_EXCLUDING_KEY, stringMap);
+ if (!stringSet.isEmpty()) {
+ toJson.put(WRAPPER_CONVERSION_KEY, stringSet);
+ }
+ if (!stringMap.isEmpty()) {
+ toJson.put(WRAPPER_CONVERSION_EXCLUDING_KEY, stringMap);
+ }
}
- private List<String> amendLibraryToString(Map<DexMethod, MethodAccessFlags> amendLibraryMembers) {
+ private List<String> amendLibraryToString(
+ Map<? extends DexItem, ? extends AccessFlags<?>> amendLibraryMembers) {
List<String> stringSet = new ArrayList<>();
amendLibraryMembers.forEach(
(member, flags) -> stringSet.add(flags.toString() + " " + toString(member)));
return stringSet;
}
+ private List<String> stringSetToString(Set<String> set) {
+ List<String> stringSet = new ArrayList<>(set);
+ Collections.sort(stringSet);
+ return stringSet;
+ }
+
private List<String> setToString(Set<? extends DexItem> set) {
List<String> stringSet = new ArrayList<>();
set.forEach(e -> stringSet.add(toString(e)));
+ Collections.sort(stringSet);
return stringSet;
}
@@ -171,10 +208,36 @@
return stringMap;
}
+ private Map<String, Object[]> mapArrayToString(Map<? extends DexItem, DexMethod[]> map) {
+ Map<String, Object[]> stringMap = new TreeMap<>();
+ map.forEach((k, v) -> stringMap.put(toString(k), arrayToString(v)));
+ return stringMap;
+ }
+
+ private Object[] arrayToString(DexMethod[] array) {
+ List<Object> res = new ArrayList<>();
+ int last = array.length - 1;
+ if (array[last] != null) {
+ res.add(-1);
+ res.add(toString(array[last]));
+ }
+ for (int i = 0; i < array.length - 1; i++) {
+ if (array[i] != null) {
+ res.add(i);
+ res.add(toString(array[i]));
+ }
+ }
+ return res.toArray();
+ }
+
private String toString(DexItem o) {
if (o instanceof DexType) {
return o.toString();
}
+ if (o instanceof DexField) {
+ DexField field = (DexField) o;
+ return field.getType() + " " + field.getHolderType() + "#" + field.getName();
+ }
if (o instanceof DexMethod) {
DexMethod method = (DexMethod) o;
StringBuilder sb =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index 302c08c..11f9c1b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -100,8 +100,9 @@
assert invokeRetargetingResult.hasNewInvokeTarget();
DexMethod newInvokeTarget =
invokeRetargetingResult.getNewInvokeTarget(eventConsumer, methodProcessingContext);
- return Collections.singletonList(
- new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, invoke.isInterface()));
+ assert appView.definitionFor(newInvokeTarget.getHolderType()) != null;
+ assert !appView.definitionFor(newInvokeTarget.getHolderType()).isInterface();
+ return Collections.singletonList(new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, false));
}
@Override
@@ -175,11 +176,7 @@
private InvokeRetargetingResult computeNewInvokeTarget(
CfInvoke instruction, ProgramMethod context) {
- if (appView
- .options()
- .machineDesugaredLibrarySpecification
- .getDontRetarget()
- .contains(context.getContextType())) {
+ if (appView.dexItemFactory().multiDexTypes.contains(context.getContextType())) {
return NO_REWRITING;
}
CfInvoke cfInvoke = instruction.asInvoke();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index bfbf6a8..53b8207 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -125,11 +125,7 @@
// The class has already been desugared.
continue;
}
- if (appView
- .options()
- .machineDesugaredLibrarySpecification
- .getDontRetarget()
- .contains(clazz.getType())) {
+ if (appView.dexItemFactory().multiDexTypes.contains(clazz.getType())) {
continue;
}
clazz.addExtraInterfaces(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
index 34a8e9d..02a5e5a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/DesugaredLibraryConverter.java
@@ -46,14 +46,18 @@
jsonFile,
ImmutableSet.of(desugaredLibraryJar, customConversionsJar),
ImmutableSet.of(androidJar),
- output);
+ output,
+ new InternalOptions());
}
- public static void convertMultiLevelAnythingToMachineSpecification(
- Path jsonSpec, Set<Path> desugaredLibraryFiles, Set<Path> libraryFiles, Path output)
- throws IOException {
-
- InternalOptions options = new InternalOptions();
+ public static MultiAPILevelHumanDesugaredLibrarySpecification
+ convertMultiLevelAnythingToMachineSpecification(
+ Path jsonSpec,
+ Set<Path> desugaredLibraryFiles,
+ Set<Path> libraryFiles,
+ Path output,
+ InternalOptions options)
+ throws IOException {
FileResource jsonResource = StringResource.fromFile(jsonSpec);
JsonObject jsonConfig = parseJsonConfig(options, jsonResource);
@@ -61,7 +65,7 @@
if (isMachineSpecification(jsonConfig, options.reporter, jsonResource.getOrigin())) {
// Nothing to convert;
Files.copy(jsonSpec, output);
- return;
+ return null;
}
DexApplication appForConversion =
@@ -71,6 +75,8 @@
String outputString = convertToMachineSpecification(options, appForConversion, humanSpec);
Files.write(output, Collections.singleton(outputString));
+
+ return humanSpec;
}
private static String convertToMachineSpecification(
@@ -126,7 +132,7 @@
.parseMultiLevelConfiguration(jsonResource);
}
- public static DexApplication getAppForConversion(
+ private static DexApplication getAppForConversion(
InternalOptions options, Set<Path> androidJar, Set<Path> desugaredlibJar) throws IOException {
AndroidApp.Builder builder = AndroidApp.builder();
builder.addProgramFiles(desugaredlibJar);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 0f09fdf..2f5c0f9 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -253,12 +253,8 @@
return partitionedMappings;
}
- public MemberNaming getMemberNaming(
- ClassNamingForNameMapper classNamingForNameMapper, boolean hasMultipleResults) {
+ public MemberNaming getMemberNaming(ClassNamingForNameMapper classNamingForNameMapper) {
MappedRange lastMappedRange = ListUtils.last(mappedRanges);
- if (hasMultipleResults) {
- return null;
- }
MethodSignature signature = lastMappedRange.getResidualSignature();
MemberNaming memberNaming = classNamingForNameMapper.methodMembers.get(signature);
assert memberNaming != null;
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 cc7f927..b6ea11b 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.naming;
-import static com.android.tools.r8.naming.MappedRangeUtils.addAllInlineFramesUntilOutermostCaller;
import static com.android.tools.r8.naming.MappedRangeUtils.isInlineMappedRange;
import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument;
@@ -486,16 +485,23 @@
Map<String, String> inverseClassMapping =
classNameMapper.getObfuscatedToOriginalMapping().inverse;
for (Entry<String, MappedRangesOfName> entry : mapper.mappedRangesByRenamedName.entrySet()) {
- MappedRangesOfName value = entry.getValue();
- List<MappedRange> mappedRanges = value.getMappedRanges();
- MappedRangeResult mappedRangeResult;
- int index = 0;
- while ((mappedRangeResult = getMappedRangesForMethod(mappedRanges, index)) != null) {
- index = mappedRangeResult.endIndex;
- MappedRange newMappedRange = mappedRangeResult.lastRange;
- MethodSignature originalSignature = newMappedRange.signature;
- List<MappedRange> existingMappedRanges = null;
+ MappedRangesOfName mappedRangesOfName = entry.getValue();
+ for (MappedRangesOfName rangesOfName : mappedRangesOfName.partitionOnMethodSignature()) {
+ MemberNaming memberNaming = rangesOfName.getMemberNaming(mapper);
+ List<MappedRange> newMappedRanges = rangesOfName.getMappedRanges();
+ RangeBuilder minified = new RangeBuilder();
+ RangeBuilder original = new RangeBuilder();
+ for (MappedRange mappedRange : newMappedRanges) {
+ minified.addRange(mappedRange.minifiedRange);
+ original.addRange(mappedRange.originalRange);
+ // Register mapping information that is dependent on the residual naming to allow
+ // updating later on.
+ registerMappingInformationFromMappedRanges(mappedRange);
+ }
+ MethodSignature originalSignature =
+ memberNaming.getOriginalSignature().asMethodSignature();
// Remove the existing entry if it exists.
+ List<MappedRange> existingMappedRanges = null;
if (committedPreviousClassBuilder != null) {
SegmentTree<List<MappedRange>> listSegmentTree =
committedPreviousClassBuilder.methodsWithPosition.get(originalSignature);
@@ -505,33 +511,29 @@
// emits `1:1:void foo() -> a` instead of `1:1:void foo():1:1 -> a`, so R8 must
// capture the preamble position by explicitly inserting 0 as original range.
Entry<Integer, List<MappedRange>> existingEntry =
- listSegmentTree.findEntry(mappedRangeResult.startOriginalPosition);
+ listSegmentTree.findEntry(original.getStartOrNoRangeFrom());
// We assume that all new minified ranges for a method are rewritten in the new map
// such that no previous existing positions exists.
if (existingEntry != null) {
listSegmentTree.removeSegment(existingEntry.getKey());
existingMappedRanges = existingEntry.getValue();
} else {
- Range originalRange = newMappedRange.originalRange;
// 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())
+ if (original.hasValue()
+ && !original.isPreamble()
&& !options.mappingComposeOptions().allowNonExistingOriginalRanges) {
throw new MappingComposeException(
"Could not find original starting position of '"
- + mappedRangeResult.lastRange
+ + minified.start
+ "' which should be "
- + mappedRangeResult.startOriginalPosition);
+ + original.start);
}
}
- assert newMappedRange.minifiedRange != null;
+ assert minified.hasValue();
} else {
MappedRange existingMappedRange =
- committedPreviousClassBuilder.methodsWithoutPosition.get(originalSignature);
- if (existingMappedRange != null) {
- committedPreviousClassBuilder.methodsWithoutPosition.remove(originalSignature);
- }
+ committedPreviousClassBuilder.methodsWithoutPosition.remove(originalSignature);
existingMappedRanges =
existingMappedRange == null
? null
@@ -539,18 +541,18 @@
}
}
List<MappedRange> composedRanges =
- composeMappedRangesForMethod(existingMappedRanges, mappedRangeResult.allRanges);
+ composeMappedRangesForMethod(existingMappedRanges, newMappedRanges);
MappedRange lastComposedRange = ListUtils.last(composedRanges);
MethodSignature residualSignature =
- newMappedRange
+ memberNaming
.computeResidualSignature(type -> inverseClassMapping.getOrDefault(type, type))
.asMethodSignature();
if (lastComposedRange.minifiedRange != null) {
methodsWithPosition
.computeIfAbsent(residualSignature, ignored -> new SegmentTree<>(false))
.add(
- mappedRangeResult.startMinifiedPosition,
- newMappedRange.minifiedRange.to,
+ minified.getStartOrNoRangeFrom(),
+ minified.getEndOrNoRangeFrom(),
composedRanges);
} else {
assert composedRanges.size() == 1;
@@ -560,78 +562,32 @@
}
}
- /***
- * Iterates over mapped ranges in order, starting from index, and adds to an internal result as
- * long as the current mapped range is the same method and return a mapped range result
- * containing all ranges for a method along with some additional information.
- */
- private MappedRangeResult getMappedRangesForMethod(List<MappedRange> mappedRanges, int index)
+ private void registerMappingInformationFromMappedRanges(MappedRange mappedRange)
throws MappingComposeException {
- if (index >= mappedRanges.size()) {
- return null;
- }
- int startIndex = index;
- List<MappedRange> seenMappedRanges = new ArrayList<>();
- int startMinifiedPosition = NO_RANGE_FROM;
- int startOriginalPosition = NO_RANGE_FROM;
- MappedRange lastOutermost = null;
- while (index < mappedRanges.size()) {
- List<MappedRange> mappedRangesForThisInterval = new ArrayList<>();
- index =
- addAllInlineFramesUntilOutermostCaller(
- mappedRanges, index, mappedRangesForThisInterval);
- assert mappedRangesForThisInterval.size() > 0;
- MappedRange lastForThisInterval = ListUtils.last(mappedRangesForThisInterval);
- if (lastOutermost == null) {
- startMinifiedPosition =
- lastForThisInterval.minifiedRange != null
- ? lastForThisInterval.minifiedRange.from
- : NO_RANGE_FROM;
- startOriginalPosition =
- lastForThisInterval.getFirstPositionOfOriginalRange(NO_RANGE_FROM);
- }
- if (lastOutermost != null
- && !lastForThisInterval.signature.equals(lastOutermost.signature)) {
- break;
- }
- // Register mapping information that is dependent on the residual naming to allow updating
- // later on.
- for (MappedRange mappedRange : mappedRangesForThisInterval) {
- for (MappingInformation mappingInformation :
- mappedRange.getAdditionalMappingInformation()) {
- if (mappingInformation.isRewriteFrameMappingInformation()) {
- RewriteFrameMappingInformation rewriteFrameMappingInformation =
- mappingInformation.asRewriteFrameMappingInformation();
- rewriteFrameMappingInformation
- .getConditions()
- .forEach(
- condition -> {
- if (condition.isThrowsCondition()) {
- current.rewriteFrameInformation.add(rewriteFrameMappingInformation);
- }
- });
- } else if (mappingInformation.isOutlineCallsiteInformation()) {
- OutlineCallsiteMappingInformation outlineCallsiteInfo =
- mappingInformation.asOutlineCallsiteInformation();
- MethodReference outline = outlineCallsiteInfo.getOutline();
- if (outline == null) {
- throw new MappingComposeException(
- "Unable to compose outline call site information without outline key: "
- + outlineCallsiteInfo.serialize());
- }
- current.addNewOutlineCallsiteInformation(outline, outlineCallsiteInfo);
- }
+ for (MappingInformation mappingInformation : mappedRange.getAdditionalMappingInformation()) {
+ if (mappingInformation.isRewriteFrameMappingInformation()) {
+ RewriteFrameMappingInformation rewriteFrameMappingInformation =
+ mappingInformation.asRewriteFrameMappingInformation();
+ rewriteFrameMappingInformation
+ .getConditions()
+ .forEach(
+ condition -> {
+ if (condition.isThrowsCondition()) {
+ current.rewriteFrameInformation.add(rewriteFrameMappingInformation);
+ }
+ });
+ } else if (mappingInformation.isOutlineCallsiteInformation()) {
+ OutlineCallsiteMappingInformation outlineCallsiteInfo =
+ mappingInformation.asOutlineCallsiteInformation();
+ MethodReference outline = outlineCallsiteInfo.getOutline();
+ if (outline == null) {
+ throw new MappingComposeException(
+ "Unable to compose outline call site information without outline key: "
+ + outlineCallsiteInfo.serialize());
}
- seenMappedRanges.add(mappedRange);
+ current.addNewOutlineCallsiteInformation(outline, outlineCallsiteInfo);
}
- lastOutermost = lastForThisInterval;
}
- return new MappedRangeResult(
- startMinifiedPosition,
- startOriginalPosition,
- startIndex + seenMappedRanges.size(),
- lastOutermost,
- seenMappedRanges);
}
private List<MappedRange> composeMappedRangesForMethod(
@@ -1090,25 +1046,32 @@
}
}
- private static class MappedRangeResult {
+ private static class RangeBuilder {
- private final int startMinifiedPosition;
- private final int startOriginalPosition;
- private final int endIndex;
- private final MappedRange lastRange;
- private final List<MappedRange> allRanges;
+ private int start = Integer.MAX_VALUE;
+ private int end = Integer.MIN_VALUE;
- public MappedRangeResult(
- int startMinifiedPosition,
- int startOriginalPosition,
- int endIndex,
- MappedRange lastRange,
- List<MappedRange> allRanges) {
- this.startMinifiedPosition = startMinifiedPosition;
- this.startOriginalPosition = startOriginalPosition;
- this.endIndex = endIndex;
- this.lastRange = lastRange;
- this.allRanges = allRanges;
+ private void addRange(Range range) {
+ if (range != null) {
+ start = Math.min(start, range.from);
+ end = Math.max(end, range.to);
+ }
+ }
+
+ private boolean hasValue() {
+ return start < Integer.MAX_VALUE;
+ }
+
+ private int getStartOrNoRangeFrom() {
+ return hasValue() ? start : NO_RANGE_FROM;
+ }
+
+ private int getEndOrNoRangeFrom() {
+ return hasValue() ? end : NO_RANGE_FROM;
+ }
+
+ private boolean isPreamble() {
+ return hasValue() && start == end && start == 0;
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index cd8bd3f..eba6b53 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -444,6 +444,8 @@
if (signature.isMethodSignature()) {
MethodSignature residualSignature = null;
+ // Propagate the active residual signature down to the next read mapped range if the
+ // original signature and name is the same - that is, there are no inline frames.
if (activeMappedRange != null
&& activeMappedRange.signature == signature
&& activeMappedRange.renamedName.equals(renamedName)) {
@@ -452,8 +454,18 @@
activeMappedRange =
classNamingBuilder.addMappedRange(
mappedRange, signature.asMethodSignature(), originalRange, renamedName);
- if (residualSignature != null) {
- activeMappedRange.setResidualSignatureInternal(residualSignature);
+ if (activeMappedRange != null) {
+ if (residualSignature != null) {
+ activeMappedRange.setResidualSignatureInternal(residualSignature);
+ } else if (lastAddedNaming != null
+ && lastAddedNaming
+ .getOriginalSignature()
+ .equals(activeMappedRange.getOriginalSignature())) {
+ // If we already parsed a residual signature for the newly read mapped range and have
+ // lost the information about the residual signature we re-create it again.
+ activeMappedRange.setResidualSignatureInternal(
+ lastAddedNaming.getResidualSignature().asMethodSignature());
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index f292409..3bebc24 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -245,9 +245,9 @@
continue;
}
// Already processed packages should have been removed.
+ assert !repackagingConfiguration.isPackageInTargetLocation(pkg);
String newPackageDescriptor =
repackagingConfiguration.getNewPackageDescriptor(pkg, seenPackageDescriptors);
- assert !repackagingConfiguration.isPackageInTargetLocation(pkg);
for (DexProgramClass classToRepackage : classesToRepackage) {
processClass(classToRepackage, pkg, newPackageDescriptor, mappings);
}
@@ -351,7 +351,6 @@
// Preserve full package name under destination package when not minifying
// (no matter which package obfuscation mode is used).
if (newPackageDescriptor.isEmpty()
- || proguardConfiguration.getKeepPackageNamesPatterns().matches(pkg)
|| mayHavePinnedPackagePrivateOrProtectedItem(pkg)) {
return pkg.getPackageDescriptor();
}
@@ -360,8 +359,7 @@
return newPackageDescriptor;
} else if (packageObfuscationMode.isMinification()) {
// Always keep top-level classes since their packages can never be minified.
- if (pkg.getPackageDescriptor().equals("")
- || proguardConfiguration.getKeepPackageNamesPatterns().matches(pkg)
+ if (pkg.getPackageDescriptor().isEmpty()
|| mayHavePinnedPackagePrivateOrProtectedItem(pkg)) {
return pkg.getPackageDescriptor();
}
@@ -386,9 +384,8 @@
if (packageObfuscationMode.isRepackageClasses()) {
return pkg.getPackageDescriptor().equals(newPackageDescriptor);
} else if (packageObfuscationMode.isMinification()) {
- // Always keep top-level classes since there packages can never be minified.
- return pkg.getPackageDescriptor().equals("")
- || proguardConfiguration.getKeepPackageNamesPatterns().matches(pkg)
+ // Always keep top-level classes since their packages can never be minified.
+ return pkg.getPackageDescriptor().isEmpty()
|| mayHavePinnedPackagePrivateOrProtectedItem(pkg);
} else {
assert packageObfuscationMode.isFlattenPackageHierarchy();
@@ -440,7 +437,7 @@
+ simpleName.substring(outerClassSimpleName.length());
repackagedDexType = repackagedDexType.withSimpleName(newSimpleName, dexItemFactory);
} else {
- assert false
+ assert options.disableInnerClassSeparatorValidationWhenRepackaging
: "Unexpected name for inner class: "
+ clazz.getType().toSourceString()
+ " (outer class: "
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUtils.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUtils.java
new file mode 100644
index 0000000..d8e571d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUtils.java
@@ -0,0 +1,30 @@
+// 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.repackaging;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.ProguardClassFilter;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class RepackagingUtils {
+
+ public static boolean isPackageNameKept(DexProgramClass clazz, InternalOptions options) {
+ String packageDescriptor = clazz.getType().getPackageDescriptor();
+ if (packageDescriptor.isEmpty()) {
+ return true;
+ }
+ ProguardClassFilter keepPackageNamesPatterns =
+ options.getProguardConfiguration().getKeepPackageNamesPatterns();
+ if (keepPackageNamesPatterns.isEmpty()) {
+ return false;
+ }
+ if (keepPackageNamesPatterns.matches(
+ options.dexItemFactory().createType("L" + packageDescriptor + ";"))) {
+ return true;
+ }
+ return !options.isForceProguardCompatibilityEnabled()
+ && keepPackageNamesPatterns.matches(clazz.getType());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
index 45ad91a..dc90e36 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
@@ -131,12 +131,11 @@
.getResidualSignature()
.equals(methodSignature));
}
- boolean isAmbiguous = partitions.size() > 1;
return ListUtils.map(
partitions,
mappedRangesOfName ->
new MemberNamingWithMappedRangesOfName(
- mappedRangesOfName.getMemberNaming(mapper, isAmbiguous), mappedRangesOfName));
+ mappedRangesOfName.getMemberNaming(mapper), mappedRangesOfName));
}
private static <T, D extends Definition> void lookupElement(
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
index caefaad..b48e8c2 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFrameResultImpl.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
+import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.Range;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.retrace.RetraceFrameElement;
@@ -26,6 +27,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.OptionalInt;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -98,6 +100,7 @@
methodDefinition.substituteHolder(
classElement.getRetracedClass().getClassReference())),
ImmutableList.of(),
+ Optional.empty(),
mappedRangeData.getPosition(),
retracer));
}
@@ -116,6 +119,7 @@
// This is a new frame
separateAmbiguousOriginalPositions(
classElement,
+ Optional.ofNullable(memberNamingWithMappedRangesOfName.getMemberNaming()),
mappedRangesForElement,
ambiguousFrames,
mappedRangeData.getPosition());
@@ -126,6 +130,7 @@
}
separateAmbiguousOriginalPositions(
classElement,
+ Optional.ofNullable(memberNamingWithMappedRangesOfName.getMemberNaming()),
mappedRangesForElement,
ambiguousFrames,
mappedRangeData.getPosition());
@@ -136,6 +141,7 @@
private void separateAmbiguousOriginalPositions(
RetraceClassElementImpl classElement,
+ Optional<MemberNaming> memberNaming,
List<MappedRange> frames,
List<ElementImpl> allAmbiguousElements,
OptionalInt obfuscatedPosition) {
@@ -145,6 +151,7 @@
allAmbiguousElements.add(
elementFromMappedRanges(
ListUtils.map(frames, MappedRangeForFrame::create),
+ memberNaming,
classElement,
obfuscatedPosition));
return;
@@ -172,11 +179,13 @@
newFrames.forEach(
ambiguousFrames ->
allAmbiguousElements.add(
- elementFromMappedRanges(ambiguousFrames, classElement, obfuscatedPosition)));
+ elementFromMappedRanges(
+ ambiguousFrames, memberNaming, classElement, obfuscatedPosition)));
}
private ElementImpl elementFromMappedRanges(
List<MappedRangeForFrame> mappedRangesForElement,
+ Optional<MemberNaming> memberNaming,
RetraceClassElementImpl classElement,
OptionalInt obfuscatedPosition) {
MappedRangeForFrame topFrame = mappedRangesForElement.get(0);
@@ -188,6 +197,7 @@
classElement,
getRetracedMethod(methodReference, topFrame, obfuscatedPosition),
mappedRangesForElement,
+ memberNaming,
obfuscatedPosition,
retracer);
}
@@ -233,6 +243,7 @@
private final RetraceFrameResultImpl retraceFrameResult;
private final RetraceClassElementImpl classElement;
private final List<MappedRangeForFrame> mappedRanges;
+ private final Optional<MemberNaming> memberNaming;
private final OptionalInt obfuscatedPosition;
private final RetracerImpl retracer;
@@ -241,17 +252,22 @@
RetraceClassElementImpl classElement,
RetracedMethodReferenceImpl methodReference,
List<MappedRangeForFrame> mappedRanges,
+ Optional<MemberNaming> memberNaming,
OptionalInt obfuscatedPosition,
RetracerImpl retracer) {
this.methodReference = methodReference;
this.retraceFrameResult = retraceFrameResult;
this.classElement = classElement;
this.mappedRanges = mappedRanges;
+ this.memberNaming = memberNaming;
this.obfuscatedPosition = obfuscatedPosition;
this.retracer = retracer;
}
private boolean isOuterMostFrameCompilerSynthesized() {
+ if (memberNaming.isPresent()) {
+ return memberNaming.get().isCompilerSynthesized();
+ }
if (mappedRanges == null || mappedRanges.isEmpty()) {
return false;
}
@@ -381,11 +397,21 @@
if (mappedRanges == null
|| mappedRanges.isEmpty()
|| !obfuscatedPosition.isPresent()
- || !ListUtils.last(mappedRanges).getMappedRange().isOutlineFrame()) {
+ || !isOutlineFrame()) {
return RetraceStackTraceContext.empty();
}
return RetraceStackTraceContextImpl.builder().setRewritePosition(obfuscatedPosition).build();
}
+
+ private boolean isOutlineFrame() {
+ if (memberNaming.isPresent()) {
+ return memberNaming.get().isOutlineFrame();
+ }
+ if (mappedRanges == null || mappedRanges.isEmpty()) {
+ return false;
+ }
+ return ListUtils.last(mappedRanges).getMappedRange().isOutlineFrame();
+ }
}
private static class MappedRangeForFrame {
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index a8058ac..387186f 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -57,6 +57,7 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
import com.android.tools.r8.naming.SeedMapper;
+import com.android.tools.r8.repackaging.RepackagingUtils;
import com.android.tools.r8.shaking.KeepInfo.Joiner;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.CollectionUtils;
@@ -64,7 +65,6 @@
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.PredicateSet;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -1089,22 +1089,14 @@
if (!options().isRepackagingEnabled()) {
return false;
}
- if (!keepInfo.getInfo(clazz).isRepackagingAllowed(clazz, options())) {
+ if (!keepInfo.getInfo(clazz).isRepackagingAllowed(options())) {
+ return false;
+ }
+ if (RepackagingUtils.isPackageNameKept(clazz, appView.options())) {
return false;
}
SeedMapper applyMappingSeedMapper = appView.getApplyMappingSeedMapper();
- if (applyMappingSeedMapper != null && applyMappingSeedMapper.hasMapping(clazz.type)) {
- return false;
- }
- return clazz
- .traverseProgramMembers(
- member -> {
- if (keepInfo.getInfo(member).isRepackagingAllowed(member, options())) {
- return TraversalContinuation.doContinue();
- }
- return TraversalContinuation.doBreak();
- })
- .shouldContinue();
+ return applyMappingSeedMapper == null || !applyMappingSeedMapper.hasMapping(clazz.type);
}
public boolean isPinned(DexReference reference) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index 9785028..f504792 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Function;
@@ -15,10 +14,10 @@
public final class KeepClassInfo extends KeepInfo<KeepClassInfo.Builder, KeepClassInfo> {
// Requires all aspects of a class to be kept.
- private static final KeepClassInfo TOP = new Builder().makeTop().build();
+ private static final KeepClassInfo TOP = new Builder().makeTop().disallowRepackaging().build();
// Requires no aspects of a class to be kept.
- private static final KeepClassInfo BOTTOM = new Builder().makeBottom().build();
+ private static final KeepClassInfo BOTTOM = new Builder().makeBottom().allowRepackaging().build();
public static KeepClassInfo top() {
return TOP;
@@ -32,10 +31,12 @@
return bottom().joiner();
}
+ private final boolean allowRepackaging;
private final boolean checkEnumUnboxed;
private KeepClassInfo(Builder builder) {
super(builder);
+ this.allowRepackaging = builder.isRepackagingAllowed();
this.checkEnumUnboxed = builder.isCheckEnumUnboxedEnabled();
}
@@ -57,13 +58,18 @@
return new Joiner(this);
}
- @Override
- public boolean isRepackagingAllowed(
- ProgramDefinition definition, GlobalKeepInfoConfiguration configuration) {
- return configuration.isRepackagingEnabled()
- && internalIsRepackagingAllowed()
- && (definition.getAccessFlags().isPublic()
- || !internalIsAccessModificationRequiredForRepackaging());
+ /**
+ * True if an item may be repackaged.
+ *
+ * <p>This method requires knowledge of the global configuration as that can override the concrete
+ * value on a given item.
+ */
+ public boolean isRepackagingAllowed(GlobalKeepInfoConfiguration configuration) {
+ return configuration.isRepackagingEnabled() && internalIsRepackagingAllowed();
+ }
+
+ boolean internalIsRepackagingAllowed() {
+ return allowRepackaging;
}
public boolean isKotlinMetadataRemovalAllowed(
@@ -98,6 +104,7 @@
public static class Builder extends KeepInfo.Builder<Builder, KeepClassInfo> {
+ private boolean allowRepackaging;
private boolean checkEnumUnboxed;
private Builder() {
@@ -106,6 +113,7 @@
private Builder(KeepClassInfo original) {
super(original);
+ allowRepackaging = original.internalIsRepackagingAllowed();
checkEnumUnboxed = original.internalIsCheckEnumUnboxedEnabled();
}
@@ -120,6 +128,23 @@
return self();
}
+ public boolean isRepackagingAllowed() {
+ return allowRepackaging;
+ }
+
+ private Builder setAllowRepackaging(boolean allowRepackaging) {
+ this.allowRepackaging = allowRepackaging;
+ return self();
+ }
+
+ public Builder allowRepackaging() {
+ return setAllowRepackaging(true);
+ }
+
+ public Builder disallowRepackaging() {
+ return setAllowRepackaging(false);
+ }
+
public Builder setCheckEnumUnboxed() {
return setCheckEnumUnboxed(true);
}
@@ -151,6 +176,7 @@
@Override
boolean internalIsEqualTo(KeepClassInfo other) {
return super.internalIsEqualTo(other)
+ && isRepackagingAllowed() == other.internalIsRepackagingAllowed()
&& isCheckEnumUnboxedEnabled() == other.internalIsCheckEnumUnboxedEnabled();
}
@@ -161,12 +187,12 @@
@Override
public Builder makeTop() {
- return super.makeTop().unsetCheckEnumUnboxed();
+ return super.makeTop().unsetCheckEnumUnboxed().disallowRepackaging();
}
@Override
public Builder makeBottom() {
- return super.makeBottom().unsetCheckEnumUnboxed();
+ return super.makeBottom().unsetCheckEnumUnboxed().allowRepackaging();
}
}
@@ -181,6 +207,11 @@
return self();
}
+ public Joiner disallowRepackaging() {
+ builder.disallowRepackaging();
+ return self();
+ }
+
@Override
public Joiner asClassJoiner() {
return this;
@@ -190,7 +221,8 @@
public Joiner merge(Joiner joiner) {
// Should be extended to merge the fields of this class in case any are added.
return super.merge(joiner)
- .applyIf(joiner.builder.isCheckEnumUnboxedEnabled(), Joiner::setCheckEnumUnboxed);
+ .applyIf(joiner.builder.isCheckEnumUnboxedEnabled(), Joiner::setCheckEnumUnboxed)
+ .applyIf(!joiner.builder.isRepackagingAllowed(), Joiner::disallowRepackaging);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index d9708ce..092f4ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.shaking.KeepInfo.Builder;
import com.android.tools.r8.shaking.KeepReason.ReflectiveUseFrom;
import com.android.tools.r8.utils.InternalOptions;
@@ -22,31 +21,25 @@
private final boolean allowAnnotationRemoval;
private final boolean allowMinification;
private final boolean allowOptimization;
- private final boolean allowRepackaging;
private final boolean allowShrinking;
private final boolean allowSignatureRemoval;
private final boolean checkDiscarded;
- private final boolean requireAccessModificationForRepackaging;
private KeepInfo(
boolean allowAccessModification,
boolean allowAnnotationRemoval,
boolean allowMinification,
boolean allowOptimization,
- boolean allowRepackaging,
boolean allowShrinking,
boolean allowSignatureRemoval,
- boolean checkDiscarded,
- boolean requireAccessModificationForRepackaging) {
+ boolean checkDiscarded) {
this.allowAccessModification = allowAccessModification;
this.allowAnnotationRemoval = allowAnnotationRemoval;
this.allowMinification = allowMinification;
this.allowOptimization = allowOptimization;
- this.allowRepackaging = allowRepackaging;
this.allowShrinking = allowShrinking;
this.allowSignatureRemoval = allowSignatureRemoval;
this.checkDiscarded = checkDiscarded;
- this.requireAccessModificationForRepackaging = requireAccessModificationForRepackaging;
}
KeepInfo(B builder) {
@@ -55,11 +48,9 @@
builder.isAnnotationRemovalAllowed(),
builder.isMinificationAllowed(),
builder.isOptimizationAllowed(),
- builder.isRepackagingAllowed(),
builder.isShrinkingAllowed(),
builder.isSignatureRemovalAllowed(),
- builder.isCheckDiscardedEnabled(),
- builder.isAccessModificationRequiredForRepackaging());
+ builder.isCheckDiscardedEnabled());
}
public static Joiner<?, ?, ?> newEmptyJoinerFor(DexReference reference) {
@@ -164,23 +155,6 @@
}
/**
- * True if an item may be repackaged.
- *
- * <p>This method requires knowledge of the global configuration as that can override the concrete
- * value on a given item.
- */
- public abstract boolean isRepackagingAllowed(
- ProgramDefinition definition, GlobalKeepInfoConfiguration configuration);
-
- boolean internalIsRepackagingAllowed() {
- return allowRepackaging;
- }
-
- boolean internalIsAccessModificationRequiredForRepackaging() {
- return requireAccessModificationForRepackaging;
- }
-
- /**
* True if an item may have its access flags modified.
*
* <p>This method requires knowledge of the global access modification as that will override the
@@ -242,7 +216,6 @@
&& (allowAnnotationRemoval || !other.internalIsAnnotationRemovalAllowed())
&& (allowMinification || !other.internalIsMinificationAllowed())
&& (allowOptimization || !other.internalIsOptimizationAllowed())
- && (allowRepackaging || !other.internalIsRepackagingAllowed())
&& (allowShrinking || !other.internalIsShrinkingAllowed())
&& (allowSignatureRemoval || !other.internalIsSignatureRemovalAllowed())
&& (!checkDiscarded || other.internalIsCheckDiscardedEnabled());
@@ -265,12 +238,10 @@
private boolean allowAccessModification;
private boolean allowAnnotationRemoval;
private boolean allowMinification;
- private boolean allowRepackaging;
private boolean allowOptimization;
private boolean allowShrinking;
private boolean allowSignatureRemoval;
private boolean checkDiscarded;
- private boolean requireAccessModificationForRepackaging;
Builder() {
// Default initialized. Use should be followed by makeTop/makeBottom.
@@ -282,12 +253,9 @@
allowAnnotationRemoval = original.internalIsAnnotationRemovalAllowed();
allowMinification = original.internalIsMinificationAllowed();
allowOptimization = original.internalIsOptimizationAllowed();
- allowRepackaging = original.internalIsRepackagingAllowed();
allowShrinking = original.internalIsShrinkingAllowed();
allowSignatureRemoval = original.internalIsSignatureRemovalAllowed();
checkDiscarded = original.internalIsCheckDiscardedEnabled();
- requireAccessModificationForRepackaging =
- original.internalIsAccessModificationRequiredForRepackaging();
}
B makeTop() {
@@ -295,11 +263,9 @@
disallowAnnotationRemoval();
disallowMinification();
disallowOptimization();
- disallowRepackaging();
disallowShrinking();
disallowSignatureRemoval();
unsetCheckDiscarded();
- requireAccessModificationForRepackaging();
return self();
}
@@ -308,11 +274,9 @@
allowAnnotationRemoval();
allowMinification();
allowOptimization();
- allowRepackaging();
allowShrinking();
allowSignatureRemoval();
unsetCheckDiscarded();
- unsetRequireAccessModificationForRepackaging();
return self();
}
@@ -336,16 +300,9 @@
&& isAnnotationRemovalAllowed() == other.internalIsAnnotationRemovalAllowed()
&& isMinificationAllowed() == other.internalIsMinificationAllowed()
&& isOptimizationAllowed() == other.internalIsOptimizationAllowed()
- && isRepackagingAllowed() == other.internalIsRepackagingAllowed()
&& isShrinkingAllowed() == other.internalIsShrinkingAllowed()
&& isSignatureRemovalAllowed() == other.internalIsSignatureRemovalAllowed()
- && isCheckDiscardedEnabled() == other.internalIsCheckDiscardedEnabled()
- && isAccessModificationRequiredForRepackaging()
- == other.internalIsAccessModificationRequiredForRepackaging();
- }
-
- public boolean isAccessModificationRequiredForRepackaging() {
- return requireAccessModificationForRepackaging;
+ && isCheckDiscardedEnabled() == other.internalIsCheckDiscardedEnabled();
}
public boolean isAccessModificationAllowed() {
@@ -368,10 +325,6 @@
return allowOptimization;
}
- public boolean isRepackagingAllowed() {
- return allowRepackaging;
- }
-
public boolean isShrinkingAllowed() {
return allowShrinking;
}
@@ -393,19 +346,6 @@
return setAllowMinification(false);
}
- public B setAllowRepackaging(boolean allowRepackaging) {
- this.allowRepackaging = allowRepackaging;
- return self();
- }
-
- public B allowRepackaging() {
- return setAllowRepackaging(true);
- }
-
- public B disallowRepackaging() {
- return setAllowRepackaging(false);
- }
-
public B setAllowOptimization(boolean allowOptimization) {
this.allowOptimization = allowOptimization;
return self();
@@ -445,20 +385,6 @@
return setCheckDiscarded(false);
}
- public B setRequireAccessModificationForRepackaging(
- boolean requireAccessModificationForRepackaging) {
- this.requireAccessModificationForRepackaging = requireAccessModificationForRepackaging;
- return self();
- }
-
- public B requireAccessModificationForRepackaging() {
- return setRequireAccessModificationForRepackaging(true);
- }
-
- public B unsetRequireAccessModificationForRepackaging() {
- return setRequireAccessModificationForRepackaging(false);
- }
-
public B setAllowAccessModification(boolean allowAccessModification) {
this.allowAccessModification = allowAccessModification;
return self();
@@ -608,11 +534,6 @@
return self();
}
- public J disallowRepackaging() {
- builder.disallowRepackaging();
- return self();
- }
-
public J disallowOptimization() {
builder.disallowOptimization();
return self();
@@ -633,24 +554,15 @@
return self();
}
- public J requireAccessModificationForRepackaging() {
- builder.requireAccessModificationForRepackaging();
- return self();
- }
-
public J merge(J joiner) {
Builder<B, K> builder = joiner.builder;
applyIf(!builder.isAccessModificationAllowed(), Joiner::disallowAccessModification);
applyIf(!builder.isAnnotationRemovalAllowed(), Joiner::disallowAnnotationRemoval);
applyIf(!builder.isMinificationAllowed(), Joiner::disallowMinification);
applyIf(!builder.isOptimizationAllowed(), Joiner::disallowOptimization);
- applyIf(!builder.isRepackagingAllowed(), Joiner::disallowRepackaging);
applyIf(!builder.isShrinkingAllowed(), Joiner::disallowShrinking);
applyIf(!builder.isSignatureRemovalAllowed(), Joiner::disallowSignatureRemoval);
applyIf(builder.isCheckDiscardedEnabled(), Joiner::setCheckDiscarded);
- applyIf(
- builder.isAccessModificationRequiredForRepackaging(),
- Joiner::requireAccessModificationForRepackaging);
reasons.addAll(joiner.reasons);
rules.addAll(joiner.rules);
return self();
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 62529ed..c78e39a 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -140,6 +140,10 @@
throw new Unreachable();
}
+ public final KeepClassInfo getInfo(DexProgramClass clazz) {
+ return getClassInfo(clazz);
+ }
+
public final KeepInfo<?, ?> getInfo(ProgramDefinition definition) {
if (definition.isProgramClass()) {
return getClassInfo(definition.asProgramClass());
@@ -279,8 +283,7 @@
assert newType == type
|| !info.isPinned(options)
|| info.isMinificationAllowed(options)
- || info.isRepackagingAllowed(
- definitions.definitionFor(newType).asProgramClass(), options);
+ || info.isRepackagingAllowed(options);
KeepClassInfo previous = newClassInfo.put(newType, info);
assert previous == null;
});
@@ -461,29 +464,6 @@
joinMethod(method, KeepInfo.Joiner::top);
}
- public void unsetRequireAllowAccessModificationForRepackaging(ProgramDefinition definition) {
- if (definition.isProgramClass()) {
- DexProgramClass clazz = definition.asProgramClass();
- KeepClassInfo info = getClassInfo(clazz);
- keepClassInfo.put(
- clazz.getType(), info.builder().unsetRequireAccessModificationForRepackaging().build());
- } else if (definition.isProgramMethod()) {
- ProgramMethod method = definition.asProgramMethod();
- KeepMethodInfo info = getMethodInfo(method);
- keepMethodInfo.put(
- method.getReference(),
- info.builder().unsetRequireAccessModificationForRepackaging().build());
- } else if (definition.isProgramField()) {
- ProgramField field = definition.asProgramField();
- KeepFieldInfo info = getFieldInfo(field);
- keepFieldInfo.put(
- field.getReference(),
- info.builder().unsetRequireAccessModificationForRepackaging().build());
- } else {
- throw new Unreachable();
- }
- }
-
public void joinField(ProgramField field, Consumer<? super KeepFieldInfo.Joiner> fn) {
KeepFieldInfo info = getFieldInfo(field);
if (info.isTop()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
index 2225e14..e77c27e 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.shaking.KeepInfo.Builder;
/** Immutable keep requirements for a member. */
@@ -15,14 +14,6 @@
super(builder);
}
- @Override
- public boolean isRepackagingAllowed(
- ProgramDefinition definition, GlobalKeepInfoConfiguration configuration) {
- return configuration.isRepackagingEnabled()
- && (definition.getAccessFlags().isPublic()
- || !internalIsAccessModificationRequiredForRepackaging());
- }
-
public boolean isKotlinMetadataRemovalAllowed(
DexProgramClass holder, GlobalKeepInfoConfiguration configuration) {
// Checking the holder for missing kotlin information relies on the holder being processed
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index cc5d00b..5f5ad5f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -295,7 +295,7 @@
@Override
public boolean matches(DexType type) {
- return classNames.stream().anyMatch(name -> name.matches(type));
+ return Iterables.any(classNames, name -> name.matches(type));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 36bd397..b4e83f7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -42,8 +42,8 @@
private boolean verbose;
private String renameSourceFileAttribute;
private final List<String> keepAttributePatterns = new ArrayList<>();
- private final ProguardPackageNameList.Builder keepPackageNamesPatterns =
- ProguardPackageNameList.builder();
+ private final ProguardClassFilter.Builder keepPackageNamesPatterns =
+ ProguardClassFilter.builder();
private final ProguardClassFilter.Builder dontWarnPatterns = ProguardClassFilter.builder();
private final ProguardClassFilter.Builder dontNotePatterns = ProguardClassFilter.builder();
protected final Set<ProguardConfigurationRule> rules = Sets.newLinkedHashSet();
@@ -193,8 +193,8 @@
this.rules.add(rule);
}
- public void addKeepPackageNamesPattern(boolean isNegated, ProguardPackageMatcher pattern) {
- keepPackageNamesPatterns.addPackageName(isNegated, pattern);
+ public void addKeepPackageNamesPattern(ProguardClassNameList pattern) {
+ keepPackageNamesPatterns.addPattern(pattern);
}
public void addDontWarnPattern(ProguardClassNameList pattern) {
@@ -395,7 +395,7 @@
private final boolean verbose;
private final String renameSourceFileAttribute;
private final ProguardKeepAttributes keepAttributes;
- private final ProguardPackageNameList keepPackageNamesPatterns;
+ private final ProguardClassFilter keepPackageNamesPatterns;
private final ProguardClassFilter dontWarnPatterns;
private final ProguardClassFilter dontNotePatterns;
protected final ImmutableList<ProguardConfigurationRule> rules;
@@ -437,7 +437,7 @@
boolean verbose,
String renameSourceFileAttribute,
ProguardKeepAttributes keepAttributes,
- ProguardPackageNameList keepPackageNamesPatterns,
+ ProguardClassFilter keepPackageNamesPatterns,
ProguardClassFilter dontWarnPatterns,
ProguardClassFilter dontNotePatterns,
Set<ProguardConfigurationRule> rules,
@@ -594,7 +594,7 @@
return keepAttributes;
}
- public ProguardPackageNameList getKeepPackageNamesPatterns() {
+ public ProguardClassFilter getKeepPackageNamesPatterns() {
return keepPackageNamesPatterns;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 526fc5f..803c78d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -316,7 +316,7 @@
} else if (acceptString("keepattributes")) {
parseKeepAttributes();
} else if (acceptString("keeppackagenames")) {
- parsePackageFilter(configurationBuilder::addKeepPackageNamesPattern);
+ parseClassFilter(configurationBuilder::addKeepPackageNamesPattern);
} else if (acceptString("keepparameternames")) {
configurationBuilder.setKeepParameterNames(true, origin, getPosition(optionStart));
} else if (acceptString("checkdiscard")) {
@@ -1146,6 +1146,8 @@
builder.getModifiersBuilder().setAllowsObfuscation(true);
} else if (acceptString("accessmodification")) {
builder.getModifiersBuilder().setAllowsAccessModification(true);
+ } else if (acceptString("repackage")) {
+ builder.getModifiersBuilder().setAllowsRepackaging(true);
} else if (options.isTestingOptionsEnabled()) {
if (acceptString("annotationremoval")) {
builder.getModifiersBuilder().setAllowsAnnotationRemoval(true);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
index 7535a3e..a2619f7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -3,11 +3,14 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import java.util.Objects;
+
public class ProguardKeepRuleModifiers {
public static class Builder {
private boolean allowsAccessModification = false;
private boolean allowsAnnotationRemoval = false;
+ private boolean allowsRepackaging = false;
private boolean allowsShrinking = false;
private boolean allowsOptimization = false;
private boolean allowsObfuscation = false;
@@ -37,6 +40,14 @@
public Builder setAllowsObfuscation(boolean allowsObfuscation) {
this.allowsObfuscation = allowsObfuscation;
+ if (allowsObfuscation) {
+ this.allowsRepackaging = true;
+ }
+ return this;
+ }
+
+ public Builder setAllowsRepackaging(boolean allowsRepackaging) {
+ this.allowsRepackaging = allowsRepackaging;
return this;
}
@@ -48,6 +59,7 @@
return new ProguardKeepRuleModifiers(
allowsAccessModification,
allowsAnnotationRemoval,
+ allowsRepackaging,
allowsShrinking,
allowsOptimization,
allowsObfuscation,
@@ -57,6 +69,7 @@
public final boolean allowsAccessModification;
public final boolean allowsAnnotationRemoval;
+ public final boolean allowsRepackaging;
public final boolean allowsShrinking;
public final boolean allowsOptimization;
public final boolean allowsObfuscation;
@@ -65,12 +78,14 @@
private ProguardKeepRuleModifiers(
boolean allowsAccessModification,
boolean allowsAnnotationRemoval,
+ boolean allowsRepackaging,
boolean allowsShrinking,
boolean allowsOptimization,
boolean allowsObfuscation,
boolean includeDescriptorClasses) {
this.allowsAccessModification = allowsAccessModification;
this.allowsAnnotationRemoval = allowsAnnotationRemoval;
+ this.allowsRepackaging = allowsRepackaging;
this.allowsShrinking = allowsShrinking;
this.allowsOptimization = allowsOptimization;
this.allowsObfuscation = allowsObfuscation;
@@ -87,6 +102,7 @@
public boolean isBottom() {
return allowsAccessModification
&& allowsAnnotationRemoval
+ && allowsRepackaging
&& allowsObfuscation
&& allowsOptimization
&& allowsShrinking
@@ -101,6 +117,7 @@
ProguardKeepRuleModifiers that = (ProguardKeepRuleModifiers) o;
return allowsAccessModification == that.allowsAccessModification
&& allowsAnnotationRemoval == that.allowsAnnotationRemoval
+ && allowsRepackaging == that.allowsRepackaging
&& allowsShrinking == that.allowsShrinking
&& allowsOptimization == that.allowsOptimization
&& allowsObfuscation == that.allowsObfuscation
@@ -109,12 +126,14 @@
@Override
public int hashCode() {
- return (allowsAccessModification ? 1 : 0)
- | (allowsAnnotationRemoval ? 2 : 0)
- | (allowsShrinking ? 4 : 0)
- | (allowsOptimization ? 8 : 0)
- | (allowsObfuscation ? 16 : 0)
- | (includeDescriptorClasses ? 32 : 0);
+ return Objects.hash(
+ allowsAccessModification,
+ allowsAnnotationRemoval,
+ allowsRepackaging,
+ allowsShrinking,
+ allowsOptimization,
+ allowsObfuscation,
+ includeDescriptorClasses);
}
@Override
@@ -122,6 +141,7 @@
StringBuilder builder = new StringBuilder();
appendWithComma(builder, allowsAccessModification, "allowaccessmodification");
appendWithComma(builder, allowsAnnotationRemoval, "allowannotationremoval");
+ appendWithComma(builder, allowsRepackaging, "allowrepackaging");
appendWithComma(builder, allowsObfuscation, "allowobfuscation");
appendWithComma(builder, allowsShrinking, "allowshrinking");
appendWithComma(builder, allowsOptimization, "allowoptimization");
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 3b46564..9e1f28f 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -54,6 +54,7 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.repackaging.RepackagingUtils;
import com.android.tools.r8.shaking.AnnotationMatchResult.AnnotationsIgnoredMatchResult;
import com.android.tools.r8.shaking.AnnotationMatchResult.ConcreteAnnotationMatchResult;
import com.android.tools.r8.shaking.AnnotationMatchResult.MatchedAnnotation;
@@ -1135,6 +1136,7 @@
dependentMinimumKeepInfo
.getOrCreateMinimumKeepInfoFor(preconditionEvent, clazz.getReference())
.disallowMinification()
+ .asClassJoiner()
.disallowRepackaging();
}
}
@@ -1637,15 +1639,14 @@
if (appView.options().isMinificationEnabled() && !modifiers.allowsObfuscation) {
dependentMinimumKeepInfo
.getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
- .disallowMinification()
- .disallowRepackaging();
+ .disallowMinification();
context.markAsUsed();
}
- if (appView.options().isRepackagingEnabled() && !modifiers.allowsObfuscation) {
+ if (appView.options().isRepackagingEnabled() && isRepackagingDisallowed(item, modifiers)) {
dependentMinimumKeepInfo
.getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
- .disallowRepackaging();
+ .applyIf(item.isProgramClass(), joiner -> joiner.asClassJoiner().disallowRepackaging());
context.markAsUsed();
}
@@ -1664,10 +1665,6 @@
.addRule(keepRule)
.disallowShrinking();
context.markAsUsed();
-
- if (item.getAccessFlags().isPackagePrivateOrProtected()) {
- minimumKeepInfoForItem.requireAccessModificationForRepackaging();
- }
}
if (modifiers.includeDescriptorClasses) {
@@ -1676,6 +1673,14 @@
}
}
+ private boolean isRepackagingDisallowed(
+ ProgramDefinition item, ProguardKeepRuleModifiers modifiers) {
+ if (!modifiers.allowsRepackaging) {
+ return true;
+ }
+ return RepackagingUtils.isPackageNameKept(item.getContextClass(), options);
+ }
+
private void evaluateIdentifierNameStringRule(
Definition item, ProguardConfigurationRule context, ProguardIfRule ifRule) {
// Main dex rules should not contain -identifiernamestring rules.
@@ -1981,7 +1986,8 @@
getDependentMinimumKeepInfo()
.getOrCreateUnconditionalMinimumKeepInfoFor(definition.getReference())
.disallowMinification()
- .disallowRepackaging();
+ .applyIf(
+ definition.isProgramClass(), joiner -> joiner.asClassJoiner().disallowRepackaging());
}
public boolean verifyKeptFieldsAreAccessedAndLive(AppView<AppInfoWithLiveness> appView) {
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 68c055f..9af4f06 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -804,6 +804,7 @@
public boolean reportMissingClassesInEnclosingMethodAttribute = false;
public boolean reportMissingClassesInInnerClassAttributes = false;
public boolean disableGenericSignatureValidation = false;
+ public boolean disableInnerClassSeparatorValidationWhenRepackaging = false;
// EXPERIMENTAL flag to get behaviour as close to Proguard as possible.
public boolean forceProguardCompatibility = false;
diff --git a/src/main/java/com/android/tools/r8/utils/OneShotCollectionConsumer.java b/src/main/java/com/android/tools/r8/utils/OneShotCollectionConsumer.java
new file mode 100644
index 0000000..89090e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/OneShotCollectionConsumer.java
@@ -0,0 +1,37 @@
+// 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.utils;
+
+import java.util.Collection;
+import java.util.function.Consumer;
+
+public class OneShotCollectionConsumer<T> {
+
+ private final Collection<T> collection;
+ private boolean hasBeenConsumed = false;
+
+ private OneShotCollectionConsumer(Collection<T> collection) {
+ this.collection = collection;
+ }
+
+ public void add(T t) {
+ assert !hasBeenConsumed;
+ this.collection.add(t);
+ }
+
+ public void consume(Consumer<T> consumer) {
+ hasBeenConsumed = true;
+ collection.forEach(consumer);
+ collection.clear();
+ }
+
+ public static <T> OneShotCollectionConsumer<T> wrap(Collection<T> collection) {
+ return new OneShotCollectionConsumer<>(collection);
+ }
+
+ public boolean isEmpty() {
+ return collection.isEmpty();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
index cd6efb7..dd60a3e 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
@@ -11,8 +11,10 @@
import com.android.tools.r8.cf.code.CfPosition;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.utils.Pair;
import java.util.ArrayList;
import java.util.List;
@@ -34,7 +36,7 @@
int pcEncodingCutoff) {
return appView.options().getTestingOptions().usePcEncodingInCfForTesting
? getPcEncodedPositions(method, positionRemapper)
- : getMappedPositionsRemapped(method, positionRemapper);
+ : getMappedPositionsRemapped(method, positionRemapper, hasOverloads);
}
@Override
@@ -43,15 +45,17 @@
}
private List<MappedPosition> getMappedPositionsRemapped(
- ProgramMethod method, PositionRemapper positionRemapper) {
+ ProgramMethod method, PositionRemapper positionRemapper, boolean hasOverloads) {
List<MappedPosition> mappedPositions = new ArrayList<>();
// Do the actual processing for each method.
CfCode oldCode = method.getDefinition().getCode().asCfCode();
List<CfInstruction> oldInstructions = oldCode.getInstructions();
List<CfInstruction> newInstructions = new ArrayList<>(oldInstructions.size());
+ boolean seenPosition = false;
for (CfInstruction oldInstruction : oldInstructions) {
CfInstruction newInstruction;
if (oldInstruction.isPosition()) {
+ seenPosition = true;
CfPosition cfPosition = oldInstruction.asPosition();
newInstruction =
new CfPosition(
@@ -62,6 +66,24 @@
}
newInstructions.add(newInstruction);
}
+ if (!seenPosition && hasOverloads) {
+ // If a method with overloads does not have an actual position then map it to the implicit
+ // preamble position.
+ DexMethod reference = method.getReference();
+ DexMethod original = appView.graphLens().getOriginalMethodSignature(reference);
+ CfPosition preamblePositionForOverload =
+ new CfPosition(
+ new CfLabel(),
+ remapAndAdd(
+ SyntheticPosition.builder().setMethod(original).setLine(0).build(),
+ positionRemapper,
+ mappedPositions));
+ List<CfInstruction> shiftedPositions = new ArrayList<>(oldInstructions.size() + 2);
+ shiftedPositions.add(preamblePositionForOverload);
+ shiftedPositions.add(preamblePositionForOverload.getLabel());
+ shiftedPositions.addAll(newInstructions);
+ newInstructions = shiftedPositions;
+ }
method.setCode(
new CfCode(
method.getHolderType(),
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 993ca5f..c1e6850 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
@@ -118,13 +118,21 @@
for (ProgramMethod method : methods) {
DexEncodedMethod definition = method.getDefinition();
+ DexMethod methodReference = method.getReference();
+ if (methodName == method.getName()
+ && appView.graphLens().getOriginalMethodSignature(methodReference) == methodReference
+ && !mustHaveResidualDebugInfo(appView.options(), definition)
+ && !definition.isD8R8Synthesized()
+ && methods.size() <= 1) {
+ continue;
+ }
positionRemapper.setCurrentMethod(definition);
List<MappedPosition> mappedPositions;
int pcEncodingCutoff =
methods.size() == 1 ? representation.getDexPcEncodingCutoff(method) : -1;
boolean canUseDexPc = pcEncodingCutoff > 0;
if (definition.getCode() != null
- && mustHaveResidualDebugInfo(appView.options(), definition)
+ && (definition.getCode().isCfCode() || definition.getCode().isDexCode())
&& !appView.isCfByteCodePassThrough(definition)) {
mappedPositions =
positionToMappedRangeMapper.getMappedPositions(
@@ -229,14 +237,9 @@
DexEncodedMethod definition = programMethod.getDefinition();
DexMethod method = programMethod.getReference();
DexString renamedName = appView.getNamingLens().lookupName(method);
- if (renamedName != method.name
- || appView.graphLens().getOriginalMethodSignature(method) != method
- || mustHaveResidualDebugInfo(appView.options(), definition)
- || definition.isD8R8Synthesized()) {
- methodsByRenamedName
- .computeIfAbsent(renamedName, key -> new ArrayList<>())
- .add(programMethod);
- }
+ methodsByRenamedName
+ .computeIfAbsent(renamedName, key -> new ArrayList<>())
+ .add(programMethod);
}
return methodsByRenamedName;
}
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 646e09e..ae0eb0e 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
@@ -45,6 +45,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.OneShotCollectionConsumer;
import com.android.tools.r8.utils.OriginalSourceFiles;
import com.android.tools.r8.utils.Pair;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
@@ -196,9 +197,11 @@
MethodSignature originalSignature =
MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != originalType);
- List<MappingInformation> methodMappingInfo = new ArrayList<>();
+ OneShotCollectionConsumer<MappingInformation> methodSpecificMappingInformation =
+ OneShotCollectionConsumer.wrap(new ArrayList<>());
if (method.getDefinition().isD8R8Synthesized()) {
- methodMappingInfo.add(CompilerSynthesizedMappingInformation.builder().build());
+ methodSpecificMappingInformation.add(
+ CompilerSynthesizedMappingInformation.builder().build());
}
DexMethod residualMethod =
@@ -208,7 +211,7 @@
if (isIdentityMapping(
mapFileVersion,
mappedPositions,
- methodMappingInfo,
+ methodSpecificMappingInformation,
method,
residualMethod.getName(),
originalMethod,
@@ -221,7 +224,7 @@
if (mapFileVersion.isGreaterThan(MapVersion.MAP_VERSION_2_1)
&& originalMethod != method.getReference()
&& !appView.graphLens().isSimpleRenaming(residualMethod)) {
- methodMappingInfo.add(
+ methodSpecificMappingInformation.add(
ResidualMethodSignatureMappingInformation.fromDexMethod(residualMethod));
}
MethodSignature residualSignature = MethodSignature.fromDexMethod(residualMethod);
@@ -233,7 +236,8 @@
if (mappedPositions.isEmpty()) {
MappedRange range =
getBuilder().addMappedRange(null, originalSignature, null, residualSignature.getName());
- methodMappingInfo.forEach(info -> range.addMappingInformation(info, Unreachable::raise));
+ methodSpecificMappingInformation.consume(
+ info -> range.addMappingInformation(info, Unreachable::raise));
return this;
}
@@ -252,7 +256,7 @@
outlineMethod,
outline -> new OutlineFixupBuilder(computeMappedMethod(outline, appView)))
.setMappedPositionsOutline(mappedPositions);
- methodMappingInfo.add(OutlineMappingInformation.builder().build());
+ methodSpecificMappingInformation.add(OutlineMappingInformation.builder().build());
}
// Update memberNaming with the collected positions, merging multiple positions into a
@@ -311,9 +315,8 @@
firstPosition.getCaller(),
prunedInlinedClasses,
cardinalRangeCache);
- for (MappingInformation info : methodMappingInfo) {
- lastMappedRange.addMappingInformation(info, Unreachable::raise);
- }
+ methodSpecificMappingInformation.consume(
+ info -> lastMappedRange.addMappingInformation(info, Unreachable::raise));
// firstPosition will contain a potential outline caller.
if (firstPosition.getOutlineCallee() != null) {
Int2IntMap positionMap = new Int2IntArrayMap();
@@ -427,7 +430,7 @@
private boolean isIdentityMapping(
MapVersion mapFileVersion,
List<MappedPosition> mappedPositions,
- List<MappingInformation> methodMappingInfo,
+ OneShotCollectionConsumer<MappingInformation> methodMappingInfo,
ProgramMethod method,
DexString obfuscatedNameDexString,
DexMethod originalMethod,
diff --git a/src/test/examples/shaking1/print-mapping-dex.ref b/src/test/examples/shaking1/print-mapping-dex.ref
index 3635115..bb72965 100644
--- a/src/test/examples/shaking1/print-mapping-dex.ref
+++ b/src/test/examples/shaking1/print-mapping-dex.ref
@@ -1,6 +1,7 @@
shaking1.Shaking -> shaking1.Shaking:
shaking1.Used -> a.a:
0:2:void <init>(java.lang.String):12:12 -> <init>
+ 0:2:java.lang.String aMethodThatIsNotUsedButKept():0:0 -> aMethodThatIsNotUsedButKept
3:5:void <init>(java.lang.String):13:13 -> <init>
0:6:void main(java.lang.String[]):8:8 -> main
7:21:void main(java.lang.String[]):9:9 -> main
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
index 301cebb..fe94b93 100644
--- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
+++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -101,7 +101,7 @@
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics.assertWarningsMatch(
- // TODO(b/251482856): Unexpected unverifiable code.
+ // The open-interfaces analysis will issue unverifiable code on missing class.
diagnosticType(UnverifiableCfCodeDiagnostic.class),
// TODO(b/175659048): This should likely be a desugar diagnostic.
allOf(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineSubTypeStaticReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineSubTypeStaticReferenceTest.java
new file mode 100644
index 0000000..3e56179
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineSubTypeStaticReferenceTest.java
@@ -0,0 +1,174 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.lang.reflect.Method;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelOutlineSubTypeStaticReferenceTest extends TestBase {
+
+ private static final AndroidApiLevel libraryApiLevel = AndroidApiLevel.M;
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private boolean willInvokeLibraryMethods() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(libraryApiLevel);
+ }
+
+ @Test
+ public void testD8BootClassPath() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ compileOnD8()
+ .addBootClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
+ @Test
+ public void testD8RuntimeClasspath() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ compileOnD8()
+ .addRunClasspathClasses(LibraryClass.class)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
+ private D8TestCompileResult compileOnD8() throws Exception {
+ return testForD8(parameters.getBackend())
+ .addLibraryClasses(LibraryClass.class)
+ .addProgramClasses(Main.class, Sub.class)
+ .addAndroidBuildVersion()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class, Sub.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, libraryApiLevel))
+ .apply(
+ setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), libraryApiLevel))
+ // TODO(b/213552119): Remove when enabled by default.
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(Sub.class)
+ .compile()
+ .inspect(inspector -> inspect(inspector, true))
+ .applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkResultOnBootClassPath);
+ }
+
+ private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
+ Method otherMethod = Sub.class.getMethod("otherMethod");
+ Method libraryMethod = LibraryClass.class.getMethod("foo");
+ // TODO(b/254510678): R8 should not member-rebind to a potential non-existing method.
+ if (isR8) {
+ MethodSubject method = inspector.method(otherMethod);
+ assertThat(method, isPresent());
+ assertThat(method, invokesMethod(Reference.methodFromMethod(libraryMethod)));
+ }
+ // TODO(b/254510678): We should outline this up until library api level.
+ verifyThat(inspector, parameters, libraryMethod).isNotOutlinedFrom(otherMethod);
+ }
+
+ private void checkResultOnBootClassPath(SingleTestRunResult<?> runResult) {
+ runResult
+ .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods(), "Not calling API")
+ .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods(), "Base::foo");
+ }
+
+ public static class LibraryClass {
+
+ public void foo() {
+ System.out.println("Base::foo");
+ }
+ }
+
+ public static class Sub extends LibraryClass {
+
+ public void otherMethod() {
+ foo();
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ new Sub().otherMethod();
+ } else {
+ System.out.println("Not calling API");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index c0f7ea3..50a8842 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -151,11 +151,6 @@
@Rule
public TestDescriptionWatcher watcher = new TestDescriptionWatcher();
- protected static final boolean supportsDefaultMethod(DebugTestConfig config) {
- return config.isCfRuntime()
- || ToolHelper.getMinApiLevelForDexVm().getLevel() >= AndroidApiLevel.N.getLevel();
- }
-
protected final void runDebugTest(
DebugTestConfig config, Class<?> debuggeeClass, JUnit3Wrapper.Command... commands)
throws Throwable {
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index e603d08..51e8ab2 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -9,16 +9,18 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
@@ -29,33 +31,40 @@
private static final String INTERFACE_SOURCE_FILE = "InterfaceWithDefaultAndStaticMethods.java";
@Parameters(name = "{0}")
- public static Collection configs() {
- return parameters()
- .add("CF", new CfDebugTestConfig(JAR))
- .add("D8", temp -> new D8DebugTestConfig().compileAndAdd(temp, JAR))
- .build();
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- private final DebugTestConfig config;
+ @Parameter public TestParameters parameters;
+ String debuggeeClass = "DebugInterfaceMethod";
- public InterfaceMethodTest(String name, DelayedDebugTestConfig delayedConfig) {
- this.config = delayedConfig.getConfig(temp);
+ private boolean supportsDefaultMethods() {
+ return parameters.isCfRuntime()
+ || parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport());
}
@Test
public void testDefaultMethod() throws Throwable {
// TODO(b/244683447): This test fails on ART 13 when checking current method in doSomething.
assumeTrue(
- config.getRuntime().isCf()
- || !config.getRuntime().asDex().getVersion().isEqualTo(Version.V13_0_0));
- String debuggeeClass = "DebugInterfaceMethod";
+ parameters.isCfRuntime() || !parameters.getDexRuntimeVersion().isEqualTo(Version.V13_0_0));
+ testForRuntime(parameters)
+ .addProgramFiles(JAR)
+ .run(parameters.getRuntime(), debuggeeClass)
+ .debugger(this::runDefaultMethod);
+ }
+
+ private void runDefaultMethod(DebugTestConfig config) throws Throwable {
String parameterName = "msg";
String localVariableName = "name";
final String defaultMethodContainerClass;
final String defaultMethodName;
final String defaultMethodThisName;
- if (supportsDefaultMethod(config)) {
+
+ if (supportsDefaultMethods()) {
defaultMethodContainerClass = "InterfaceWithDefaultAndStaticMethods";
defaultMethodName = "doSomething";
defaultMethodThisName = "this";
@@ -69,7 +78,6 @@
defaultMethodThisName = "_this";
}
-
List<Command> commands = new ArrayList<>();
commands.add(breakpoint(debuggeeClass, "testDefaultMethod"));
commands.add(run());
@@ -96,7 +104,13 @@
@Test
public void testOverrideDefaultMethod() throws Throwable {
- String debuggeeClass = "DebugInterfaceMethod";
+ testForRuntime(parameters)
+ .addProgramFiles(JAR)
+ .run(parameters.getRuntime(), debuggeeClass)
+ .debugger(this::runOverrideDefaultMethod);
+ }
+
+ private void runOverrideDefaultMethod(DebugTestConfig config) throws Throwable {
String parameterName = "msg";
String localVariableName = "newMsg";
@@ -121,12 +135,18 @@
@Test
public void testStaticMethod() throws Throwable {
- String debuggeeClass = "DebugInterfaceMethod";
+ testForRuntime(parameters)
+ .addProgramFiles(JAR)
+ .run(parameters.getRuntime(), debuggeeClass)
+ .debugger(this::runStaticMethod);
+ }
+
+ private void runStaticMethod(DebugTestConfig config) throws Throwable {
String parameterName = "msg";
final String staticMethodContainerClass;
final String staticMethodName = "printString";
- if (supportsDefaultMethod(config)) {
+ if (supportsDefaultMethods()) {
staticMethodContainerClass = "InterfaceWithDefaultAndStaticMethods";
} else {
staticMethodContainerClass =
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 cf69ff4..abe31f8 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -48,12 +48,18 @@
testForD8()
.addProgramFiles(kotlinTestParameters.getCompiler().getKotlinStdlibJar())
.setMinApi(parameters.getApiLevel())
- // TODO(b/248244467): Remove if fixed.
.compileWithExpectedDiagnostics(
- diagnostics ->
+ diagnostics -> {
+ if (kotlinTestParameters.isKotlinDev()
+ && parameters.isDexRuntime()
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)) {
+ diagnostics.assertWarningsCount(2);
diagnostics.assertAllWarningsMatch(
- DiagnosticsMatcher.diagnosticType(
- InterfaceDesugarMissingTypeDiagnostic.class)));
+ DiagnosticsMatcher.diagnosticType(InterfaceDesugarMissingTypeDiagnostic.class));
+ } else {
+ diagnostics.assertNoMessages();
+ }
+ });
}
@Test
diff --git a/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java b/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
index 47f6e4b..7a519a6 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/OverloadWithNoLineNumberTest.java
@@ -4,17 +4,20 @@
package com.android.tools.r8.debuginfo;
-import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.debuginfo.testclasses.SimpleCallChainClassWithOverloads;
-import com.android.tools.r8.naming.retrace.StackTrace;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceCommand;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -35,68 +38,43 @@
@Test
public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClassFileData(
- transformer(SimpleCallChainClassWithOverloads.class)
- .removeLineNumberTable(MethodPredicate.onName("test"))
- .transform())
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(SimpleCallChainClassWithOverloads.class)
- .addKeepClassAndMembersRules(SimpleCallChainClassWithOverloads.class)
- .addKeepAttributeLineNumberTable()
- .run(parameters.getRuntime(), SimpleCallChainClassWithOverloads.class)
- .assertFailureWithErrorThatThrows(RuntimeException.class)
- .inspectStackTrace(
- (stackTrace, inspector) -> {
- StackTraceLine mainLine =
- StackTraceLine.builder()
- .setClassName(typeName(SimpleCallChainClassWithOverloads.class))
- .setMethodName("main")
- .setFileName(SOURCE_FILE_NAME)
- .setLineNumber(10)
- .build();
- if (parameters.isCfRuntime()
- || parameters.getDexRuntimeVersion().isOlderThan(Version.V8_1_0)) {
- StackTraceLine testStackTraceLine =
- StackTraceLine.builder()
- .setClassName(typeName(SimpleCallChainClassWithOverloads.class))
- .setMethodName("test")
- .setFileName(SOURCE_FILE_NAME)
- .build();
- assertThat(
- stackTrace,
- isSame(
- StackTrace.builder()
- // TODO(b/251677184): Stack trace lines should still be distinguishable
- // even if there are no original line numbers to map back two.
- .add(testStackTraceLine)
- .add(testStackTraceLine)
- .add(mainLine)
- .build()));
- } else {
- assertThat(
- stackTrace,
- isSame(
- StackTrace.builder()
- // TODO(b/251677184): Strange that we are able to distinguish when using
- // pc mapping.
- .add(
- StackTraceLine.builder()
- .setClassName(typeName(SimpleCallChainClassWithOverloads.class))
- .setMethodName("test")
- .setFileName(SOURCE_FILE_NAME)
- .setLineNumber(11)
- .build())
- .add(
- StackTraceLine.builder()
- .setClassName(typeName(SimpleCallChainClassWithOverloads.class))
- .setMethodName("test")
- .setFileName(SOURCE_FILE_NAME)
- .setLineNumber(4)
- .build())
- .add(mainLine)
- .build()));
- }
- });
+ R8TestRunResult result =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(SimpleCallChainClassWithOverloads.class)
+ .removeLineNumberTable(MethodPredicate.onName("test"))
+ .transform())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(SimpleCallChainClassWithOverloads.class)
+ .addKeepClassAndMembersRules(SimpleCallChainClassWithOverloads.class)
+ .addKeepAttributeLineNumberTable()
+ .run(parameters.getRuntime(), SimpleCallChainClassWithOverloads.class)
+ .assertFailureWithErrorThatThrows(RuntimeException.class);
+ Retrace.run(
+ RetraceCommand.builder()
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromString(result.proguardMap()))
+ .build())
+ .setStackTrace(
+ result.getOriginalStackTrace().getStackTraceLines().stream()
+ .map(line -> line.originalLine)
+ .collect(Collectors.toList()))
+ .setRetracedStackTraceConsumer(
+ retraced -> {
+ String className = typeName(SimpleCallChainClassWithOverloads.class);
+ assertEquals(
+ StringUtils.joinLines(
+ "\tat " + className + ".void test(long)(" + SOURCE_FILE_NAME + ":0)",
+ "\tat " + className + ".void test()(" + SOURCE_FILE_NAME + ":0)",
+ "\tat "
+ + className
+ + ".void main(java.lang.String[])("
+ + SOURCE_FILE_NAME
+ + ":10)"),
+ StringUtils.joinLines(retraced));
+ })
+ .setVerbose(true)
+ .build());
}
}
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 b5ca98b..a68f3ce 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
@@ -123,13 +123,7 @@
// TODO(b/238179854): Investigate how to fix these.
private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_PATH =
- ImmutableSet.of(
- "java.lang.Iterable java.nio.file.FileSystem.getFileStores()",
- "java.lang.Iterable java.nio.file.FileSystem.getRootDirectories()",
- "java.util.Iterator java.nio.file.Path.iterator()",
- "java.nio.file.DirectoryStream"
- + " java.nio.file.spi.FileSystemProvider.newDirectoryStream(java.nio.file.Path,"
- + " java.nio.file.DirectoryStream$Filter)");
+ ImmutableSet.of("java.lang.Iterable java.nio.file.FileSystem.getFileStores()");
// TODO(b/238179854): Investigate how to fix these.
private static final Set<String> MISSING_GENERIC_TYPE_CONVERSION_FLOW =
@@ -245,6 +239,18 @@
.collect(Collectors.toSet());
Set<String> maintainTypeInSet =
specification.getMaintainType().stream().map(DexType::toString).collect(Collectors.toSet());
+ Map<String, boolean[]> genericConversionsInSpec = new HashMap<>();
+ specification
+ .getApiGenericConversion()
+ .forEach(
+ (method, generics) -> {
+ boolean[] indexes = new boolean[generics.length];
+ for (int i = 0; i < generics.length; i++) {
+ indexes[i] = generics[i] != null;
+ }
+ genericConversionsInSpec.put(method.toString(), indexes);
+ });
+
assertEquals(
Collections.emptySet(), Sets.intersection(wrappersInSpec, customConversionsInSpec));
@@ -257,6 +263,7 @@
nonDesugaredJar,
customConversionsInSpec,
maintainTypeInSet,
+ genericConversionsInSpec,
genericDependencies);
Map<ClassReference, Set<ClassReference>> indirectWrappers =
getIndirectlyReferencedWrapperTypes(
@@ -266,6 +273,7 @@
customConversionsInSpec,
maintainTypeInSet,
specification.getWrappers(),
+ genericConversionsInSpec,
genericDependencies);
{
Set<String> missingGenericDependency = new HashSet<>();
@@ -339,6 +347,7 @@
CodeInspector nonDesugaredJar,
Set<String> customConversions,
Set<String> maintainType,
+ Map<String, boolean[]> genericConversionsInSpec,
Set<DexEncodedMethod> genericDependencies) {
Map<ClassReference, Set<MethodReference>> directWrappers = new HashMap<>();
nonDesugaredJar.forAllClasses(
@@ -364,6 +373,7 @@
forEachType(
method,
t -> addType(adder, t, preDesugarTypes, customConversions, maintainType),
+ genericConversionsInSpec,
genericDependencies);
});
});
@@ -373,12 +383,20 @@
private void forEachType(
FoundMethodSubject subject,
Function<String, Boolean> process,
+ Map<String, boolean[]> genericConversionsInSpec,
Set<DexEncodedMethod> generics) {
+ boolean[] genericConversions = genericConversionsInSpec.get(subject.toString());
MethodSignature signature = subject.getFinalSignature().asMethodSignature();
- process.apply(signature.type);
- for (String parameter : signature.parameters) {
- process.apply(parameter);
+ if (genericConversions == null || !genericConversions[genericConversions.length - 1]) {
+ process.apply(signature.type);
}
+ for (int i = 0; i < signature.parameters.length; i++) {
+ if (genericConversions == null || !genericConversions[i]) {
+ process.apply(signature.parameters[i]);
+ }
+ }
+ // Even if the genericConversions are present, we check the generic types since conversions
+ // on such types will happen through the hand written custom wrappers.
MethodTypeSignature genericSignature = subject.getMethod().getGenericSignature();
if (genericSignature != null) {
TypeSignature[] typeSignatures = new TypeSignature[signature.parameters.length + 1];
@@ -387,15 +405,17 @@
}
typeSignatures[signature.parameters.length] = genericSignature.returnType().typeSignature();
for (TypeSignature typeSignature : typeSignatures) {
- if ((typeSignature instanceof ClassTypeSignature)) {
- for (FieldTypeSignature typeArgument :
- ((ClassTypeSignature) typeSignature).typeArguments()) {
- if (typeArgument instanceof ClassTypeSignature) {
- String type = descriptorToJavaType(typeArgument.toString()).split("<")[0];
- if (!GENERIC_NOT_NEEDED.contains(type)) {
- boolean added = process.apply(type);
- if (added) {
- generics.add(subject.getMethod());
+ if (typeSignature != null) {
+ if ((typeSignature instanceof ClassTypeSignature)) {
+ for (FieldTypeSignature typeArgument :
+ ((ClassTypeSignature) typeSignature).typeArguments()) {
+ if (typeArgument instanceof ClassTypeSignature) {
+ String type = descriptorToJavaType(typeArgument.toString()).split("<")[0];
+ if (!GENERIC_NOT_NEEDED.contains(type)) {
+ boolean added = process.apply(type);
+ if (added) {
+ generics.add(subject.getMethod());
+ }
}
}
}
@@ -412,6 +432,7 @@
Set<String> customConversions,
Set<String> maintainType,
Map<DexType, WrapperDescriptor> wrapperDescriptorMap,
+ Map<String, boolean[]> genericConversionsInSpec,
Set<DexEncodedMethod> genericDependencies) {
Map<ClassReference, Set<ClassReference>> indirectWrappers = new HashMap<>();
WorkList<ClassReference> worklist = WorkList.newEqualityWorkList(directWrappers.keySet());
@@ -435,6 +456,7 @@
forEachType(
method,
t -> addType(adder, t, existing, customConversions, maintainType),
+ genericConversionsInSpec,
genericDependencies);
});
WrapperDescriptor descriptor = wrapperDescriptorMap.get(clazz.getDexProgramClass().getType());
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterateTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterateTest.java
index d69ebe8..4ae0d62 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/IterateTest.java
@@ -5,23 +5,45 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.SPECIFICATIONS_WITH_CF2CF;
-import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+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.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.transformers.MethodTransformer;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
import java.util.List;
+import java.util.function.DoublePredicate;
+import java.util.function.DoubleUnaryOperator;
+import java.util.function.IntPredicate;
+import java.util.function.IntUnaryOperator;
+import java.util.function.LongPredicate;
+import java.util.function.LongUnaryOperator;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
+import org.objectweb.asm.Opcodes;
@RunWith(Parameterized.class)
public class IterateTest extends DesugaredLibraryTestBase {
- private static final String EXPECTED_OUTPUT = StringUtils.lines("1");
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("1", "2", "1.0", "3", "1", "2", "1.0", "3");
private final TestParameters parameters;
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -30,8 +52,12 @@
@Parameters(name = "{0}, spec: {1}, {2}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(),
- getJdk8Jdk11(),
+ getTestParameters()
+ .withCfRuntime(CfVm.JDK11)
+ .withDexRuntimes()
+ .withAllApiLevelsAlsoForCf()
+ .build(),
+ ImmutableList.of(JDK11, JDK11_PATH),
SPECIFICATIONS_WITH_CF2CF);
}
@@ -47,17 +73,107 @@
@Test
public void testIterable() throws Throwable {
testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
- .addInnerClasses(getClass())
+ .addProgramClassFileData(getProgramClassFileData())
+ .addProgramFiles(getOtherProgramClasses())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
+ private Collection<Path> getOtherProgramClasses() throws IOException {
+ Collection<Path> files = ToolHelper.getClassFilesForInnerClasses(getClass());
+ files.removeIf(p -> p.toString().endsWith("Main.class"));
+ return files;
+ }
+
+ private Collection<byte[]> getProgramClassFileData() throws IOException {
+ ImmutableMap<String, String> mapping =
+ ImmutableMap.of(
+ "com/android/tools/r8/desugar/desugaredlibrary/IterateTest$IntStreamStub",
+ "java/util/stream/IntStream",
+ "com/android/tools/r8/desugar/desugaredlibrary/IterateTest$StreamStub",
+ "java/util/stream/Stream",
+ "com/android/tools/r8/desugar/desugaredlibrary/IterateTest$LongStreamStub",
+ "java/util/stream/LongStream",
+ "com/android/tools/r8/desugar/desugaredlibrary/IterateTest$DoubleStreamStub",
+ "java/util/stream/DoubleStream");
+ return ImmutableList.of(
+ transformer(Main.class)
+ .addMethodTransformer(
+ new MethodTransformer() {
+ @Override
+ public void visitMethodInsn(
+ int opcode,
+ String owner,
+ String name,
+ String descriptor,
+ boolean isInterface) {
+ if (opcode == Opcodes.INVOKESTATIC && mapping.containsKey(owner)) {
+ super.visitMethodInsn(
+ opcode, mapping.get(owner), name, descriptor, isInterface);
+ return;
+ }
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform());
+ }
+
static class Main {
public static void main(String[] args) {
- IntStream iterate = IntStream.iterate(1, x -> x + 3);
- System.out.println(iterate.findFirst().getAsInt());
+ noPredicate();
+ predicate();
+ }
+
+ private static void noPredicate() {
+ IntStream iterateInt = IntStream.iterate(1, x -> x + 3);
+ System.out.println(iterateInt.findFirst().getAsInt());
+ LongStream iterateLong = LongStream.iterate(2L, x -> x + 3);
+ System.out.println(iterateLong.findFirst().getAsLong());
+ DoubleStream iterateDouble = DoubleStream.iterate(1.0, x -> x + 3);
+ System.out.println(iterateDouble.findFirst().getAsDouble());
+ Stream<Integer> iterateObject = Stream.iterate(3, x -> x + 3);
+ System.out.println(iterateObject.findFirst().get());
+ }
+
+ private static void predicate() {
+ IntStream iterateInt = IntStreamStub.iterate(1, x -> x != 0, x -> x + 3);
+ System.out.println(iterateInt.findFirst().getAsInt());
+ LongStream iterateLong = LongStreamStub.iterate(2L, x -> x != 0, x -> x + 3);
+ System.out.println(iterateLong.findFirst().getAsLong());
+ DoubleStream iterateDouble = DoubleStreamStub.iterate(1.0, x -> x != 0, x -> x + 3);
+ System.out.println(iterateDouble.findFirst().getAsDouble());
+ Stream<Integer> iterateObject = StreamStub.iterate(3, x -> x != 0, x -> x + 3);
+ System.out.println(iterateObject.findFirst().get());
+ }
+ }
+
+ private interface IntStreamStub {
+
+ static IntStream iterate(int i, IntPredicate predicate, IntUnaryOperator operator) {
+ return null;
+ }
+ }
+
+ private interface LongStreamStub {
+
+ static LongStream iterate(long i, LongPredicate predicate, LongUnaryOperator operator) {
+ return null;
+ }
+ }
+
+ private interface DoubleStreamStub {
+
+ static DoubleStream iterate(double i, DoublePredicate predicate, DoubleUnaryOperator operator) {
+ return null;
+ }
+ }
+
+ private interface StreamStub<T> {
+
+ static <T> Stream<T> iterate(T o, Predicate<T> predicate, UnaryOperator<T> operator) {
+ return null;
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MultiDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MultiDexTest.java
new file mode 100644
index 0000000..f5d5449
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/MultiDexTest.java
@@ -0,0 +1,106 @@
+// 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 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_PATH;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+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 MultiDexTest extends DesugaredLibraryTestBase {
+
+ private static final String[] JAR_NAMES =
+ new String[] {
+ "multidex-1.0.3.jar",
+ "multidex-instrumentation-1.0.3.jar",
+ "multidex-2.0.1.jar",
+ "multidex-instrumentation-2.0.0.jar"
+ };
+ private static final List<Path> MULTIDEX_JARS =
+ Arrays.stream(JAR_NAMES)
+ .map(jar -> Paths.get(ToolHelper.THIRD_PARTY_DIR + "multidex/" + jar))
+ .collect(Collectors.toList());
+
+ private final TestParameters parameters;
+ private final CompilationSpecification compilationSpecification;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final Path multidexJar;
+
+ @Parameters(name = "{0}, spec: {1}, {2}, {3}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ ImmutableList.of(JDK8, JDK11, JDK11_PATH),
+ ImmutableList.of(D8_L8DEBUG),
+ MULTIDEX_JARS);
+ }
+
+ public MultiDexTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification,
+ Path multidexJar) {
+ this.parameters = parameters;
+ this.compilationSpecification = compilationSpecification;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.multidexJar = multidexJar;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(multidexJar)
+ .compile()
+ .inspect(this::assertNoJ$Reference);
+ }
+
+ private void assertNoJ$Reference(CodeInspector inspector) {
+ String prefix = "j$";
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ clazz.forAllFields(f -> assertFalse(f.type().toString().startsWith(prefix)));
+ clazz.forAllMethods(
+ m -> {
+ if (m.hasCode()) {
+ for (InstructionSubject instruction : m.instructions()) {
+ if (instruction.isInvoke()) {
+ DexMethod method = instruction.getMethod();
+ for (DexType referencedType : method.getReferencedTypes()) {
+ assertFalse(referencedType.toString().startsWith(prefix));
+ }
+ assertFalse(method.getHolderType().toString().startsWith(prefix));
+ }
+ if (instruction.isFieldAccess()) {
+ DexField field = instruction.getField();
+ assertFalse(field.getType().toString().startsWith(prefix));
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesMoveTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesMoveTest.java
new file mode 100644
index 0000000..97feb33
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesMoveTest.java
@@ -0,0 +1,80 @@
+// 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_PATH;
+
+import com.android.tools.r8.TestParameters;
+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.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+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 FilesMoveTest extends DesugaredLibraryTestBase {
+
+ private static final String EXPECTED_RESULT = StringUtils.lines("Hello");
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ // Skip Android 4.4.4 due to missing libjavacrypto.
+ getTestParameters()
+ .withDexRuntime(Version.V4_0_4)
+ .withDexRuntimesStartingFromIncluding(Version.V5_1_1)
+ .withAllApiLevels()
+ .build(),
+ ImmutableList.of(JDK11_PATH),
+ DEFAULT_SPECIFICATIONS);
+ }
+
+ public FilesMoveTest(
+ 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)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .compile()
+ .withArt6Plus64BitsLib()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) throws Throwable {
+ Path src = Files.createTempFile("src", ".txt");
+ Path dest = src.getParent().resolve("dest");
+ Files.write(src, "Hello".getBytes(StandardCharsets.UTF_8), StandardOpenOption.WRITE);
+ Files.move(src, dest, StandardCopyOption.ATOMIC_MOVE);
+ System.out.println(Files.readAllLines(dest).get(0));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
index 9508e8c..cef3785 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/jdk11/FilesTest.java
@@ -18,6 +18,7 @@
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
+import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.OpenOption;
@@ -28,6 +29,7 @@
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -50,7 +52,10 @@
"null",
"true",
"unsupported",
- "j$.nio.file.attribute");
+ "j$.nio.file.attribute",
+ "tmp",
+ "/",
+ "true");
private static final String EXPECTED_RESULT_DESUGARING_FILE_SYSTEM_PLATFORM_CHANNEL =
StringUtils.lines(
"bytes written: 11",
@@ -62,7 +67,10 @@
"null",
"true",
"unsupported",
- "j$.nio.file.attribute");
+ "j$.nio.file.attribute",
+ "tmp",
+ "/",
+ "true");
private static final String EXPECTED_RESULT_PLATFORM_FILE_SYSTEM_DESUGARING =
StringUtils.lines(
"bytes written: 11",
@@ -74,7 +82,10 @@
"true",
"true",
"true",
- "j$.nio.file.attribute");
+ "j$.nio.file.attribute",
+ "tmp",
+ "/",
+ "true");
private static final String EXPECTED_RESULT_PLATFORM_FILE_SYSTEM =
StringUtils.lines(
"bytes written: 11",
@@ -86,7 +97,10 @@
"true",
"true",
"true",
- "java.nio.file.attribute");
+ "java.nio.file.attribute",
+ "tmp",
+ "/",
+ "true");
private final TestParameters parameters;
private final LibraryDesugaringSpecification libraryDesugaringSpecification;
@@ -144,6 +158,18 @@
readThroughFileChannelAPI(path);
attributeAccess(path);
fspMethodsWithGeneric(path);
+ pathGeneric();
+ }
+
+ private static void pathGeneric() throws IOException {
+ Path tmpDict = Files.createTempDirectory("tmpDict");
+ Path tmpFile = Files.createFile(tmpDict.resolve("tmpFile"));
+ Iterator<Path> iterator = tmpDict.iterator();
+ System.out.println(iterator.next());
+ Iterable<Path> rootDirectories = tmpFile.getFileSystem().getRootDirectories();
+ System.out.println(rootDirectories.iterator().next());
+ DirectoryStream<Path> paths = Files.newDirectoryStream(tmpDict);
+ System.out.println(paths.iterator().hasNext());
}
private static void fspMethodsWithGeneric(Path path) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
index 468494f..2a07cfb 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/specification/ConvertExportReadTest.java
@@ -126,11 +126,11 @@
private void testMultiLevelUsingMain(LibraryDesugaringSpecification spec) throws IOException {
Assume.assumeTrue(ToolHelper.isLocalDevelopment());
- Path output = temp.newFile().toPath();
- DesugaredLibraryConverter.convertMultiLevelAnythingToMachineSpecification(
- spec.getSpecification(), spec.getDesugarJdkLibs(), spec.getLibraryFiles(), output);
-
InternalOptions options = new InternalOptions();
+
+ Path output = temp.newFile().toPath();
+ convertMultiLevelAnythingToMachineSpecification(spec, output, options);
+
MachineDesugaredLibrarySpecification machineSpecParsed =
new MachineDesugaredLibrarySpecificationParser(
options.dexItemFactory(),
@@ -142,6 +142,33 @@
assertFalse(machineSpecParsed.getRewriteType().isEmpty());
}
+ public void convertMultiLevelAnythingToMachineSpecification(
+ LibraryDesugaringSpecification spec, Path output, InternalOptions options)
+ throws IOException {
+
+ MultiAPILevelHumanDesugaredLibrarySpecification humanSpec =
+ DesugaredLibraryConverter.convertMultiLevelAnythingToMachineSpecification(
+ spec.getSpecification(),
+ spec.getDesugarJdkLibs(),
+ spec.getLibraryFiles(),
+ output,
+ options);
+
+ if (humanSpec == null) {
+ return;
+ }
+
+ // Validate the written spec is the read spec.
+ Box<String> json = new Box<>();
+ MultiAPILevelHumanDesugaredLibrarySpecificationJsonExporter.export(
+ humanSpec, ((string, handler) -> json.set(string)));
+ MultiAPILevelHumanDesugaredLibrarySpecification writtenHumanSpec =
+ new MultiAPILevelHumanDesugaredLibrarySpecificationParser(
+ options.dexItemFactory(), options.reporter)
+ .parseMultiLevelConfiguration(StringResource.fromString(json.get(), Origin.unknown()));
+ assertSpecEquals(humanSpec, writtenHumanSpec);
+ }
+
@Test
public void testSingleLevel() throws IOException {
Assume.assumeTrue(ToolHelper.isLocalDevelopment());
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 8085bf6..6245fec 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,9 +34,9 @@
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, 32, -1);
+ 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, 32, -1);
+ public static Descriptor JDK11_PATH_DESCRIPTOR = new Descriptor(24, 26, 26, 10000, -1);
public static Descriptor JDK11_LEGACY_DESCRIPTOR = new Descriptor(10000, 10000, -1, 10000, 24);
private static class Descriptor {
diff --git a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
index 78b83da..333ab92 100644
--- a/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
+++ b/src/test/java/com/android/tools/r8/graph/MissingClassThrowingTest.java
@@ -76,9 +76,7 @@
.compileWithExpectedDiagnostics(
diagnostics ->
diagnostics
- .assertNoInfos()
- // TODO(b/251482856): When missing class is not an error this throws
- // for unverifiable code. Is that expected?
+ .assertOnlyErrors()
.inspectErrors(
diagnostic ->
diagnostic
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
index 8b33acb..374fb98 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/InnerClassNameTestRunner.java
@@ -225,12 +225,16 @@
R8TestCompileResult r8CompileResult =
testForR8(parameters.getBackend())
.addKeepMainRule(MAIN_CLASS)
- .addKeepRules("-keep,allowobfuscation class * { *; }")
- .addKeepRules("-keepattributes InnerClasses,EnclosingMethod")
+ .addKeepAllClassesRuleWithAllowObfuscation()
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
.addProgramClassFileData(InnerClassNameTestDump.dump(config, parameters))
.allowDiagnosticInfoMessages(hasMalformedInnerClassAttribute())
.minification(minify)
- .addOptionsModification(InternalOptions::disableNameReflectionOptimization)
+ .addOptionsModification(
+ options -> {
+ options.disableInnerClassSeparatorValidationWhenRepackaging = true;
+ options.disableNameReflectionOptimization();
+ })
.setMinApi(parameters.getApiLevel())
.compile()
.apply(this::checkWarningsAboutMalformedAttribute);
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingFrontierTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingFrontierTest.java
new file mode 100644
index 0000000..2584946
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingFrontierTest.java
@@ -0,0 +1,136 @@
+// 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.memberrebinding;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MemberRebindingFrontierTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addProgramClasses(Main.class, ProgramClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addLibraryClasses(Base.class, I.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(this::setupRunclasspath)
+ .run(parameters.getRuntime(), Main.class)
+ .apply(result -> checkOutput(result, false));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, ProgramClass.class)
+ .addProgramClassFileData()
+ .addDefaultRuntimeLibrary(parameters)
+ .addLibraryClasses(I.class)
+ .addLibraryClassFileData(removeFooMethod(Base.class))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassRules(ProgramClass.class)
+ .addKeepMainRule(Main.class)
+ .compile()
+ .apply(this::setupRunclasspath)
+ .inspect(
+ inspector -> {
+ // TODO(b/254510678): We should not rebind to I.foo
+ ClassSubject mainClass = inspector.clazz(Main.class);
+ assertThat(mainClass, isPresent());
+ MethodSubject foo = mainClass.mainMethod();
+ assertThat(
+ foo, CodeMatchers.invokesMethodWithHolderAndName(typeName(I.class), "foo"));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .apply(result -> checkOutput(result, true));
+ }
+
+ private byte[] removeFooMethod(Class<?> clazz) throws Exception {
+ return transformer(clazz).removeMethodsWithName("foo").transform();
+ }
+
+ private void setupRunclasspath(TestCompileResult<?, ?> compileResult) {
+ compileResult
+ .applyIf(
+ parameters.canUseDefaultAndStaticInterfaceMethods(),
+ result ->
+ result
+ .addRunClasspathClasses(I.class)
+ .addRunClasspathClassFileData(removeFooMethod(Base.class)))
+ .applyIf(
+ !parameters.canUseDefaultAndStaticInterfaceMethods(),
+ result ->
+ result
+ .addRunClasspathClasses(Base.class)
+ .addRunClasspathClassFileData(removeFooMethod(I.class)));
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult, boolean r8) {
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ runResult.assertSuccessWithOutputLines("I::foo");
+ return;
+ }
+ // TODO(b/254510678): We should not rebind to I.foo
+ if (r8) {
+ runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+ } else {
+ runResult.assertSuccessWithOutputLines("Base::foo");
+ }
+ }
+
+ private interface I {
+
+ // Introduced at a later api level.
+ default void foo() {
+ System.out.println("I::foo");
+ }
+ }
+
+ public abstract static class Base {
+
+ // Was present until moved into I at some api level.
+ public void foo() {
+ System.out.println("Base::foo");
+ }
+ }
+
+ public static class ProgramClass extends Base implements I {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ callFoo(new ProgramClass());
+ }
+
+ private static void callFoo(ProgramClass clazz) {
+ clazz.foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
index bb73c3f..5eb7783 100644
--- a/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/KeepPackageNamesTest.java
@@ -99,4 +99,14 @@
.compile()
.inspect(config::inspect);
}
+
+ @Test
+ public void testR8Compat() throws Exception {
+ testForR8Compat(Backend.DEX)
+ .addProgramClasses(CLASSES)
+ .addKeepAllClassesRuleWithAllowObfuscation()
+ .addKeepRules(config.getKeepRule())
+ .compile()
+ .inspect(config::inspect);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
index 487498f..fa9de18 100644
--- a/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/PackageNamingTest.java
@@ -356,13 +356,11 @@
ba.getDexProgramClass().getType().getPackageName(),
bb.getDexProgramClass().getType().getPackageName());
- // We cannot repackage c or d since these have package-private members and a
- // keep,allowobfuscation. For us to be able to repackage them, we have to use
- // -allowaccesmodification.
+ // We can repackage c and d since these have no external package-private references.
List<String> klasses = ImmutableList.of("naming101.c", "naming101.d");
for (String klass : klasses) {
ClassSubject k = inspector.clazz(klass);
- assertNotEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
+ assertEquals("naming101.a", k.getDexProgramClass().getType().getPackageName());
}
// All other classes can be repackaged to naming101.a, but naming101.a.a exists to make a name
diff --git a/src/test/java/com/android/tools/r8/naming/keeppackagenames/KeepPackageNameRootTest.java b/src/test/java/com/android/tools/r8/naming/keeppackagenames/KeepPackageNameRootTest.java
index 309ab55..e7c14ff 100644
--- a/src/test/java/com/android/tools/r8/naming/keeppackagenames/KeepPackageNameRootTest.java
+++ b/src/test/java/com/android/tools/r8/naming/keeppackagenames/KeepPackageNameRootTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.ProguardVersion;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestShrinkerBuilder;
@@ -34,22 +33,22 @@
@Test
public void testR8Compat() throws Exception {
- run(testForR8Compat(Backend.CF));
+ run(testForR8Compat(Backend.CF), false);
}
@Test
public void testR8Full() throws Exception {
- run(testForR8(Backend.CF));
+ run(testForR8(Backend.CF), true);
}
@Test
public void testR8PG() throws Exception {
- run(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"));
+ run(testForProguard(ProguardVersion.V7_0_0).addKeepRules("-dontwarn"), false);
}
- private TestCompileResult<?, ?> run(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder)
+ private void run(TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder, boolean isFullMode)
throws Exception {
- return testBuilder
+ testBuilder
.addProgramClassFileData(
transformer(Main.class)
.setClassDescriptor("Lfoo/Main;")
@@ -64,7 +63,11 @@
assertEquals(1, inspector.allClasses().size());
inspector.forAllClasses(
clazz -> {
- assertNotEquals("foo", clazz.getDexProgramClass().getType().getPackageName());
+ if (isFullMode) {
+ assertEquals("foo", clazz.getDexProgramClass().getType().getPackageName());
+ } else {
+ assertNotEquals("foo", clazz.getDexProgramClass().getType().getPackageName());
+ }
});
});
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 1619359..87698d9 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -163,6 +163,16 @@
return this;
}
+ public Builder applyIf(
+ boolean condition, Consumer<Builder> trueConsumer, Consumer<Builder> elseConsumer) {
+ if (condition) {
+ trueConsumer.accept(this);
+ } else {
+ elseConsumer.accept(this);
+ }
+ return this;
+ }
+
public StackTraceLine build() {
String lineNumberPart = lineNumber >= 0 ? ":" + lineNumber : "";
String originalLine = className + '.' + methodName + '(' + fileName + lineNumberPart + ')';
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index fc1e432..9e361e6 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -130,7 +130,6 @@
// at com.android.tools.r8.naming.retraceproguard.MainApp.main(MainApp.java:7)
//
// We should instead translate to:
- // at com.android.tools.r8.naming.retraceproguard.ResourceWrapper.foo(ResourceWrapper.java:1)
// at com.android.tools.r8.naming.retraceproguard.ResourceWrapper.foo(ResourceWrapper.java:0)
// at com.android.tools.r8.naming.retraceproguard.MainApp.main(MainApp.java:7)
// since the synthetic bridge belongs to ResourceWrapper.foo.
@@ -140,12 +139,7 @@
runTest(
ImmutableList.of(),
(StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
- StackTrace reprocessedStackTrace =
- mode == CompilationMode.DEBUG
- ? retracedStackTrace
- : retracedStackTrace.filter(this::filterSynthesizedBridgeMethod);
- assertThat(
- reprocessedStackTrace, isSameExceptForFileNameAndLineNumber(expectedStackTrace));
+ assertThat(retracedStackTrace, isSameExceptForFileNameAndLineNumber(expectedStackTrace));
assertEquals(
expectedActualStackTraceHeight(), actualStackTrace.getStackTraceLines().size());
});
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageAllowRepackagingTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageAllowRepackagingTest.java
new file mode 100644
index 0000000..686be1d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageAllowRepackagingTest.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageAllowRepackagingTest extends RepackageTestBase {
+
+ public RepackageAllowRepackagingTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .apply(this::configureRepackaging)
+ .addKeepRules(
+ "-keep,allowrepackage class " + typeName(ShouldStayInPackage.class) + " { *; }")
+ .addKeepRules(
+ "-keep,allowrepackage class " + typeName(ShouldBeRepackaged.class) + " { *; }")
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(ShouldStayInPackage.class, isNotRepackaged(inspector));
+ assertThat(ShouldBeRepackaged.class, isRepackaged(inspector));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("ShouldStayInPackage::foo", "ShouldBeRepackaged::bar");
+ }
+
+ public static class ShouldStayInPackage {
+
+ static void foo() {
+ System.out.println("ShouldStayInPackage::foo");
+ }
+ }
+
+ public static class ShouldBeRepackaged {
+
+ public static void bar() {
+ System.out.println("ShouldBeRepackaged::bar");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ ShouldStayInPackage.foo();
+ ShouldBeRepackaged.bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageKeepPackageNameTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageKeepPackageNameTest.java
new file mode 100644
index 0000000..bcffb5f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageKeepPackageNameTest.java
@@ -0,0 +1,73 @@
+// 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.repackage;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RepackageKeepPackageNameTest extends RepackageTestBase {
+
+ public RepackageKeepPackageNameTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ super(flattenPackageHierarchyOrRepackageClasses, parameters);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ R8TestCompileResult compileLib =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepClassAndMembersRulesWithAllowObfuscation(
+ ShouldStayInPackage.class, ShouldBeRepackaged.class)
+ .addKeepPackageNamesRule(typeName(ShouldStayInPackage.class))
+ .apply(this::configureRepackaging)
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(ShouldStayInPackage.class, isNotRepackaged(inspector));
+ assertThat(ShouldBeRepackaged.class, isRepackaged(inspector));
+ });
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Runner.class)
+ .addClasspathClasses(ShouldStayInPackage.class, ShouldBeRepackaged.class)
+ .addApplyMapping(compileLib.getProguardMap())
+ .addKeepMainRule(Runner.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(compileLib.writeToZip())
+ .run(parameters.getRuntime(), Runner.class)
+ .assertSuccessWithOutputLines("ShouldStayInPackage::foo", "ShouldBeRepackaged::bar");
+ }
+
+ public static class ShouldStayInPackage {
+
+ static void foo() {
+ System.out.println("ShouldStayInPackage::foo");
+ }
+ }
+
+ public static class ShouldBeRepackaged {
+
+ public static void bar() {
+ System.out.println("ShouldBeRepackaged::bar");
+ }
+ }
+
+ public static class Runner {
+
+ public static void main(String[] args) {
+ ShouldStayInPackage.foo();
+ ShouldBeRepackaged.bar();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java
index a6c9ea3..8152688 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSamePackageTest.java
@@ -45,7 +45,7 @@
.addKeepMainRule(Main.class)
.addDontWarn(RepackageProtectedInSamePackageTest.class, NeverClassInline.class)
.run(parameters.getRuntime(), Main.class, typeName(RepackageForKeepClassMembers.class))
- .inspect(inspector -> inspect(inspector, true))
+ .inspect(this::inspect)
.assertSuccessWithOutputLines(EXPECTED);
}
@@ -61,7 +61,7 @@
.addKeepMainRule(Main.class)
.enableNeverClassInliningAnnotations()
.run(parameters.getRuntime(), Main.class)
- .inspect(inspector -> inspect(inspector, false))
+ .inspect(this::inspect)
.assertSuccessWithOutputLines(EXPECTED);
}
@@ -82,16 +82,10 @@
.transform();
}
- private void inspect(CodeInspector inspector, boolean isProguard) {
+ private void inspect(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(RepackageForKeepClassMembers.class);
assertThat(clazz, isPresent());
- // TODO(b/250671873): We should be able to repackage the Sub class since the only reference
- // to Sub.class is in the same package and we have allowobfuscation.
- if (isProguard) {
- assertThat(RepackageForKeepClassMembers.class, isRepackaged(inspector));
- } else {
- assertThat(RepackageForKeepClassMembers.class, isNotRepackaged(inspector));
- }
+ assertThat(RepackageForKeepClassMembers.class, isRepackaged(inspector));
assertThat(clazz.uniqueFieldWithOriginalName("hashCodeCache"), isPresentAndNotRenamed());
assertThat(clazz.uniqueMethodWithOriginalName("calculateHashCode"), isPresentAndNotRenamed());
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
index 179845d..9742b6a 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageTest.java
@@ -163,18 +163,16 @@
// 3.A, 3.B, 3.C) Accessing a kept method is OK.
markShouldAlwaysBeEligible.accept(AccessPublicKeptMethodOnReachableClass.class);
- markEligibleWithAllowAccessModification.accept(
- AccessPackagePrivateKeptMethodOnReachableClassDirect.class);
- markEligibleWithAllowAccessModification.accept(
- AccessPackagePrivateKeptMethodOnReachableClassIndirect.class);
+ markShouldAlwaysBeEligible.accept(AccessPackagePrivateKeptMethodOnReachableClassDirect.class);
+ markShouldAlwaysBeEligible.accept(AccessPackagePrivateKeptMethodOnReachableClassIndirect.class);
// 4) -keepclassmembers,allowobfuscation class ReachableClassWithKeptMethod { <methods>; }
// 4.A, 4.B, 4.C) Accessing a kept method is OK.
markShouldAlwaysBeEligible.accept(AccessPublicKeptMethodAllowRenamingOnReachableClass.class);
- markEligibleWithAllowAccessModification.accept(
+ markShouldAlwaysBeEligible.accept(
AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassDirect.class);
- markEligibleWithAllowAccessModification.accept(
+ markShouldAlwaysBeEligible.accept(
AccessPackagePrivateKeptMethodAllowRenamingOnReachableClassIndirect.class);
// 5) No keep rule.
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithKeepPackagePrivateTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithKeepPackagePrivateTest.java
index 8020e78..6f79b12 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithKeepPackagePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithKeepPackagePrivateTest.java
@@ -76,8 +76,8 @@
}
private void inspect(CodeInspector inspector) {
- assertThat(A.class, isRepackagedIf(inspector, allowAccessModification));
- assertThat(B.class, isRepackagedIf(inspector, allowAccessModification));
+ assertThat(A.class, isNotRepackaged(inspector));
+ assertThat(B.class, isNotRepackaged(inspector));
}
public static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
index 3aa2f49..2ba06ba 100644
--- a/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/OverloadsWithoutLineNumberTest.java
@@ -63,21 +63,11 @@
.setRetracedStackTraceConsumer(box::set)
.setVerbose(true);
Retrace.run(builder.build());
- // TODO(b/221015863): This should ideally be:
- // at " + typeName(ClassWithOverload.class) + ".test(int)(OverloadsWithoutLineNumberTest.java)
- if (parameters.canUseNativeDexPC()) {
- assertEquals(
- "\tat "
- + typeName(ClassWithOverload.class)
- + ".test(OverloadsWithoutLineNumberTest.java:0)",
- box.get().get(1));
- } else {
- assertEquals(
- "\tat "
- + typeName(ClassWithOverload.class)
- + ".test(OverloadsWithoutLineNumberTest.java)",
- box.get().get(1));
- }
+ assertEquals(
+ "\tat "
+ + typeName(ClassWithOverload.class)
+ + ".void test(int)(OverloadsWithoutLineNumberTest.java:0)",
+ box.get().get(1));
}
public static class ClassWithOverload {
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 9d9d7a4..7513419 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceStackTraceFunctionalCompositionTest.java
@@ -157,7 +157,7 @@
return rewrittenR8Jar;
}
- @Ignore("b/251677184: Failing since update to target 11")
+ @Ignore("b/255292908")
@Test
public void testR8RetraceAndComposition() throws Exception {
Path rewrittenR8Jar = getRewrittenR8Jar();
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceSynthesizedInnerFrameTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceSynthesizedInnerFrameTest.java
new file mode 100644
index 0000000..b1ce233
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceSynthesizedInnerFrameTest.java
@@ -0,0 +1,73 @@
+// 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.retrace;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
+import java.util.List;
+import java.util.OptionalInt;
+import java.util.stream.Collectors;
+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 RetraceSynthesizedInnerFrameTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetraceSynthesizedInnerFrameTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ private static final String mapping =
+ "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\n"
+ + "some.Class -> a:\n"
+ + " 3:3:int other.strawberry(int):101:101 -> a\n"
+ + " # { id: 'com.android.tools.r8.synthesized' }\n"
+ + " 3:3:int mango(float):28 -> a";
+
+ @Test
+ public void testSyntheticClass() {
+ List<RetraceFrameElement> frameResults =
+ Retracer.createDefault(ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
+ .retraceClass(Reference.classFromTypeName("a"))
+ .stream()
+ .flatMap(
+ element ->
+ element
+ .lookupFrame(RetraceStackTraceContext.empty(), OptionalInt.of(3), "a")
+ .stream())
+ .collect(Collectors.toList());
+ assertEquals(1, frameResults.size());
+ RetraceFrameElement retraceFrameElement = frameResults.get(0);
+ List<RetracedMethodReference> allFrames =
+ retraceFrameElement.stream()
+ .map(RetracedSingleFrame::getMethodReference)
+ .collect(Collectors.toList());
+ assertEquals(2, allFrames.size());
+ List<RetracedMethodReference> nonSyntheticFrames =
+ retraceFrameElement
+ .streamRewritten(RetraceStackTraceContext.empty())
+ .map(RetracedSingleFrame::getMethodReference)
+ .collect(Collectors.toList());
+ // The input should never have a synthesized inline frame - rater it should have been removed
+ // from the mapping output. Because of the incorrect input we now attribute the synthesized
+ // information to the entire inline block.
+ assertEquals(1, nonSyntheticFrames.size());
+ assertEquals(
+ Reference.methodFromDescriptor("Lother;", "strawberry", "(I)I"),
+ nonSyntheticFrames.get(0).asKnown().getMethodReference());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
index 6783fa7..47c1d91 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceWithPcAndNoLineTableTestRunner.java
@@ -61,24 +61,12 @@
assertEquals(
"SourceFile",
inspector.clazz(getTestClass()).getDexProgramClass().sourceFile.toString());
- // The stack-trace should not have line numbers unless on a VM with DEX PC support.
+ // TODO(b/202919530): The stack-trace should have line positions since they are
+ // essentially free when compiling for dex.
if (parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0)) {
- if (parameters
- .getApiLevel()
- .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())) {
- // TODO(b/202919530): With PC support it should retrace.
- assertThat(
- stacktrace,
- StackTrace.isSameExceptForLineNumbers(getExpectedStackTrace(true)));
- } else {
- // Compiling for an API level before PC support it may be unfeasible to include
- // the mapping information due to mapping size increase. That is up for
- // discussion.
- assertThat(
- stacktrace,
- StackTrace.isSameExceptForLineNumbers(getExpectedStackTrace(true)));
- }
+ assertThat(
+ stacktrace, StackTrace.isSameExceptForLineNumbers(getExpectedStackTrace(true)));
} else {
// Having stripped the line-number table, the raw stack will not have line info.
assertThat(stacktrace, StackTrace.isSame(getExpectedStackTrace(false)));
@@ -95,14 +83,30 @@
.setClassName(className)
.setFileName(sourceFile)
.setMethodName("foo")
- .applyIf(withLines, b -> b.setLineNumber(10))
+ .applyIf(
+ withLines,
+ b -> b.setLineNumber(10),
+ b -> {
+ if (parameters.isDexRuntime()) {
+ // TODO(b/255705077): Should not have position 0.
+ b.setLineNumber(0);
+ }
+ })
.build())
.add(
StackTraceLine.builder()
.setClassName(className)
.setFileName(sourceFile)
.setMethodName("bar")
- .applyIf(withLines, b -> b.setLineNumber(15))
+ .applyIf(
+ withLines,
+ b -> b.setLineNumber(15),
+ b -> {
+ if (parameters.isDexRuntime()) {
+ // TODO(b/255705077): Should not have position 0.
+ b.setLineNumber(0);
+ }
+ })
.build())
.add(
StackTraceLine.builder()
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
deleted file mode 100644
index 1b10739..0000000
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiSynthesizedInnerFrameTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2021, 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.retrace.api;
-
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.DiagnosticsHandler;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.RetraceFrameElement;
-import com.android.tools.r8.retrace.RetraceStackTraceContext;
-import com.android.tools.r8.retrace.RetracedMethodReference;
-import com.android.tools.r8.retrace.RetracedSingleFrame;
-import com.android.tools.r8.retrace.Retracer;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.Collectors;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class RetraceApiSynthesizedInnerFrameTest extends RetraceApiTestBase {
-
- public RetraceApiSynthesizedInnerFrameTest(TestParameters parameters) {
- super(parameters);
- }
-
- @Override
- protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
- return ApiTest.class;
- }
-
- public static class ApiTest implements RetraceApiBinaryTest {
-
- private final String mapping =
- "# { id: 'com.android.tools.r8.mapping', version: '1.0' }\n"
- + "some.Class -> a:\n"
- + " 3:3:int other.strawberry(int):101:101 -> a\n"
- + " # { id: 'com.android.tools.r8.synthesized' }\n"
- + " 3:3:int mango(float):28 -> a";
-
- @Test
- public void testSyntheticClass() {
- List<RetraceFrameElement> frameResults =
- Retracer.createDefault(
- ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {})
- .retraceClass(Reference.classFromTypeName("a"))
- .stream()
- .flatMap(
- element ->
- element
- .lookupFrame(RetraceStackTraceContext.empty(), OptionalInt.of(3), "a")
- .stream())
- .collect(Collectors.toList());
- assertEquals(1, frameResults.size());
- RetraceFrameElement retraceFrameElement = frameResults.get(0);
- List<RetracedMethodReference> allFrames =
- retraceFrameElement.stream()
- .map(RetracedSingleFrame::getMethodReference)
- .collect(Collectors.toList());
- assertEquals(2, allFrames.size());
- List<RetracedMethodReference> nonSyntheticFrames =
- retraceFrameElement
- .streamRewritten(RetraceStackTraceContext.empty())
- .map(RetracedSingleFrame::getMethodReference)
- .collect(Collectors.toList());
- assertEquals(allFrames, nonSyntheticFrames);
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
index 5b20d00..c233d49 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
@@ -28,7 +28,6 @@
RetraceApiSynthesizedFieldTest.ApiTest.class,
RetraceApiSynthesizedMethodTest.ApiTest.class,
RetraceApiSynthesizedFrameTest.ApiTest.class,
- RetraceApiSynthesizedInnerFrameTest.ApiTest.class,
RetraceApiUnknownJsonTest.ApiTest.class,
RetraceApiRewriteFrameInlineNpeTest.ApiTest.class,
RetraceApiAmbiguousOriginalRangeTest.ApiTest.class,
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 2b8d68b..8149c5b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -1513,48 +1513,30 @@
testKeepattributes(expected, config);
}
- private void testKeeppackagenames(ProguardPackageNameList expected, String config) {
+ private void testKeeppackagenames(String config) {
ProguardConfigurationParser parser =
new ProguardConfigurationParser(new DexItemFactory(), reporter);
parser.parse(createConfigurationForTesting(ImmutableList.of(config)));
verifyParserEndsCleanly();
- assertEquals(expected, parser.getConfigRawForTesting().getKeepPackageNamesPatterns());
}
@Test
public void parseKeeppackagenames() {
- ProguardPackageNameList xxxYYY =
- ProguardPackageNameList.builder()
- .addPackageName(false, new ProguardPackageMatcher("xxx"))
- .addPackageName(false, new ProguardPackageMatcher("yyy"))
- .build();
- testKeeppackagenames(xxxYYY, "-keeppackagenames xxx,yyy");
- testKeeppackagenames(xxxYYY, "-keeppackagenames xxx, yyy");
- testKeeppackagenames(xxxYYY, "-keeppackagenames xxx ,yyy");
- testKeeppackagenames(xxxYYY, "-keeppackagenames xxx , yyy");
- testKeeppackagenames(xxxYYY, "-keeppackagenames xxx , yyy ");
- testKeeppackagenames(xxxYYY, "-keeppackagenames xxx , yyy \n");
- testKeeppackagenames(xxxYYY, "-keeppackagenames \"xxx\",\"yyy\"");
+ testKeeppackagenames("-keeppackagenames xxx,yyy");
+ testKeeppackagenames("-keeppackagenames xxx, yyy");
+ testKeeppackagenames("-keeppackagenames xxx ,yyy");
+ testKeeppackagenames("-keeppackagenames xxx , yyy");
+ testKeeppackagenames("-keeppackagenames xxx , yyy ");
+ testKeeppackagenames("-keeppackagenames xxx , yyy \n");
+ testKeeppackagenames("-keeppackagenames \"xxx\",\"yyy\"");
testKeeppackagenames(
- ProguardPackageNameList.builder()
- .addPackageName(false, new ProguardPackageMatcher("com.**"))
- .addPackageName(false, new ProguardPackageMatcher("org.*"))
- .build(),
"-keeppackagenames com.**, org.*");
testKeeppackagenames(
- ProguardPackageNameList.builder()
- .addPackageName(false, new ProguardPackageMatcher("c?m.**"))
- .addPackageName(false, new ProguardPackageMatcher("?r?.*"))
- .build(),
"-keeppackagenames c?m.**, ?r?.*");
testKeeppackagenames(
- ProguardPackageNameList.builder()
- .addPackageName(true, new ProguardPackageMatcher("c?m.**"))
- .addPackageName(true, new ProguardPackageMatcher("?r?.*"))
- .build(),
"-keeppackagenames !c?m.**, !?r?.*");
}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 47c49fc..63efc58 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -1282,12 +1282,17 @@
public ClassFileTransformer transformMethodInsnInMethod(
String methodName, MethodInsnTransform transform) {
+ return transformMethodInsnInMethod(MethodPredicate.onName(methodName), transform);
+ }
+
+ public ClassFileTransformer transformMethodInsnInMethod(
+ MethodPredicate predicate, MethodInsnTransform transform) {
return addMethodTransformer(
new MethodTransformer() {
@Override
public void visitMethodInsn(
int opcode, String owner, String name, String descriptor, boolean isInterface) {
- if (getContext().method.getMethodName().equals(methodName)) {
+ if (MethodPredicate.testContext(predicate, getContext())) {
transform.visitMethodInsn(
opcode,
owner,
diff --git a/third_party/multidex.tar.gz.sha1 b/third_party/multidex.tar.gz.sha1
new file mode 100644
index 0000000..7d22fb6
--- /dev/null
+++ b/third_party/multidex.tar.gz.sha1
@@ -0,0 +1 @@
+f0ea06be076aa1fc45ea5451188121d8e8b992e2
\ No newline at end of file
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index 5d61558..023f1c9 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-189692be996b34e4ba4a069c148b20d7ff99abe7
\ No newline at end of file
+ec4f1b68e126b5b5e8a53078c74c79c65e190cf2
\ No newline at end of file
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 4d19948..0f90ab1 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -1045,7 +1045,7 @@
print('// AUTOGENERATED FILE from tools/run_on_app_dump.py in R8 repo')
print('part of r8_config;')
print('')
- print('final Suite dumpsSuite = new Suite("OpenSourceAppDumps");')
+ print('final Suite dumpsSuite = Suite("OpenSourceAppDumps");')
print('')
print('createOpenSourceAppBenchmarks() {')
print_indented('final cpus = ["Lenovo M90"];', 2)