Merge commit '5260d7981c1849f8a6f3f6ac9e198c196c1aa46c' into dev-release
diff --git a/build.gradle b/build.gradle index 360f21a..3032180 100644 --- a/build.gradle +++ b/build.gradle
@@ -68,6 +68,14 @@ // Custom source set for example tests and generated tests. sourceSets { + main11 { + java { + srcDirs = ['src/main/java'] + } + resources { + srcDirs = ['src/main/resources'] + } + } test { java { srcDirs = [ @@ -240,6 +248,27 @@ implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion implementation files('third_party/android_jar/api-database/api-database.jar') + + main11Implementation "net.sf.jopt-simple:jopt-simple:$joptSimpleVersion" + main11Implementation "com.google.code.gson:gson:$gsonVersion" + // Include all of guava when compiling the code, but exclude annotations that we don't + // need from the packaging. + main11CompileOnly("com.google.guava:guava:$guavaVersion") + main11Implementation("com.google.guava:guava:$guavaVersion", { + exclude group: 'com.google.errorprone' + exclude group: 'com.google.code.findbugs' + exclude group: 'com.google.j2objc' + exclude group: 'org.codehaus.mojo' + }) + main11Implementation group: 'it.unimi.dsi', name: 'fastutil', version: fastutilVersion + main11Implementation "org.jetbrains.kotlinx:kotlinx-metadata-jvm:$kotlinExtMetadataJVMVersion" + main11Implementation group: 'org.ow2.asm', name: 'asm', version: asmVersion + main11Implementation group: 'org.ow2.asm', name: 'asm-commons', version: asmVersion + main11Implementation group: 'org.ow2.asm', name: 'asm-tree', version: asmVersion + main11Implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion + main11Implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion + main11Implementation files('third_party/android_jar/api-database/api-database.jar') + examplesTestNGRunnerCompile group: 'org.testng', name: 'testng', version: testngVersion testCompile sourceSets.examples.output testCompile "junit:junit:$junitVersion" @@ -272,7 +301,6 @@ def r8DesugaredPath = "$buildDir/libs/r8desugared.jar" def r8LibGeneratedKeepRulesPath = "$buildDir/generated/keep.txt" def r8LibTestPath = "$buildDir/classes/r8libtest" -def java11ClassFiles = "$buildDir/classes/java/mainJava11" def osString = OperatingSystem.current().isLinux() ? "linux" : OperatingSystem.current().isMacOsX() ? "mac" : "windows" @@ -593,6 +621,11 @@ JavaVersion.VERSION_1_10, false) setJdkCompilationWithCompatibility( + sourceSets.main11.compileJavaTaskName, + 'jdk-11', + JavaVersion.VERSION_11, + false) +setJdkCompilationWithCompatibility( sourceSets.examplesJava11.compileJavaTaskName, 'jdk-11', JavaVersion.VERSION_11, @@ -608,25 +641,6 @@ JavaVersion.VERSION_17, false) -task compileMainWithJava11 (type: JavaCompile) { - dependsOn downloadDeps - def jdkDir = 'third_party/openjdk/jdk-11/' - options.fork = true - options.forkOptions.jvmArgs = [] - if (OperatingSystem.current().isLinux()) { - options.forkOptions.javaHome = file(jdkDir + 'linux') - } else if (OperatingSystem.current().isMacOsX()) { - options.forkOptions.javaHome = file(jdkDir + 'osx/Contents/Home') - } else { - options.forkOptions.javaHome = file(jdkDir + 'windows') - } - source = sourceSets.main.allSource - destinationDir = file(java11ClassFiles) - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - classpath = sourceSets.main.compileClasspath -} - task provideJdk11TestsDependencies(type: org.gradle.api.tasks.Copy) { from sourceSets.examplesTestNGRunner.compileClasspath include "**/**.jar" @@ -762,8 +776,7 @@ } task repackageSources11(type: ShadowJar) { - dependsOn compileMainWithJava11 - from file(java11ClassFiles) + from sourceSets.main11.output mergeServiceFiles(it) baseName 'sources_main_11' }
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java index c292f33..51c1d2f 100644 --- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java +++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -50,6 +50,7 @@ private final List<Consumer<Inspector>> outputInspections; private final int threadCount; private final DumpInputFlags dumpInputFlags; + private final MapIdProvider mapIdProvider; BaseCompilerCommand(boolean printHelp, boolean printVersion) { super(printHelp, printVersion); @@ -66,6 +67,7 @@ outputInspections = null; threadCount = ThreadUtils.NOT_SPECIFIED; dumpInputFlags = DumpInputFlags.noDump(); + mapIdProvider = null; } BaseCompilerCommand( @@ -82,7 +84,8 @@ List<AssertionsConfiguration> assertionsConfiguration, List<Consumer<Inspector>> outputInspections, int threadCount, - DumpInputFlags dumpInputFlags) { + DumpInputFlags dumpInputFlags, + MapIdProvider mapIdProvider) { super(app); assert minApiLevel > 0; assert mode != null; @@ -99,6 +102,7 @@ this.outputInspections = outputInspections; this.threadCount = threadCount; this.dumpInputFlags = dumpInputFlags; + this.mapIdProvider = mapIdProvider; } /** @@ -148,6 +152,10 @@ return desugarState; } + public MapIdProvider getMapIdProvider() { + return mapIdProvider; + } + /** True if the output dex files has checksum information encoded in it. False otherwise. */ public boolean getIncludeClassesChecksum() { return includeClassesChecksum; @@ -217,6 +225,7 @@ private List<Consumer<Inspector>> outputInspections = new ArrayList<>(); protected StringConsumer proguardMapConsumer = null; private DumpInputFlags dumpInputFlags = DumpInputFlags.noDump(); + private MapIdProvider mapIdProvider = null; abstract CompilationMode defaultCompilationMode(); @@ -516,6 +525,16 @@ return desugarState; } + /** Set a custom provider for defining the map id for the build. */ + public B setMapIdProvider(MapIdProvider mapIdProvider) { + this.mapIdProvider = mapIdProvider; + return self(); + } + + public MapIdProvider getMapIdProvider() { + return mapIdProvider; + } + @Deprecated public B addSpecialLibraryConfiguration(String configuration) { return addDesugaredLibraryConfiguration(configuration);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index 671fb6b..0a7bcd1 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -318,6 +318,7 @@ mainDexKeepRules, getThreadCount(), getDumpInputFlags(), + getMapIdProvider(), factory); } } @@ -399,6 +400,7 @@ ImmutableList<ProguardConfigurationRule> mainDexKeepRules, int threadCount, DumpInputFlags dumpInputFlags, + MapIdProvider mapIdProvider, DexItemFactory factory) { super( inputApp, @@ -414,7 +416,8 @@ assertionsConfiguration, outputInspections, threadCount, - dumpInputFlags); + dumpInputFlags, + mapIdProvider); this.intermediate = intermediate; this.desugarGraphConsumer = desugarGraphConsumer; this.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
diff --git a/src/main/java/com/android/tools/r8/Disassemble.java b/src/main/java/com/android/tools/r8/Disassemble.java index 7c4ac49..c36a796 100644 --- a/src/main/java/com/android/tools/r8/Disassemble.java +++ b/src/main/java/com/android/tools/r8/Disassemble.java
@@ -213,6 +213,7 @@ InternalOptions getInternalOptions() { InternalOptions internal = new InternalOptions(); internal.useSmaliSyntax = useSmali; + internal.readDebugSetFileEvent = true; return internal; } }
diff --git a/src/main/java/com/android/tools/r8/DumpOptions.java b/src/main/java/com/android/tools/r8/DumpOptions.java index 1c0e170..e8c6a49 100644 --- a/src/main/java/com/android/tools/r8/DumpOptions.java +++ b/src/main/java/com/android/tools/r8/DumpOptions.java
@@ -34,6 +34,7 @@ private static final String TREE_SHAKING_KEY = "tree-shaking"; private static final String MINIFICATION_KEY = "minification"; private static final String FORCE_PROGUARD_COMPATIBILITY_KEY = "force-proguard-compatibility"; + private static final String SYSTEM_PROPERTY_PREFIX = "system-property-"; private final Tool tool; private final CompilationMode compilationMode; @@ -110,6 +111,15 @@ addOptionalDumpEntry(builder, TREE_SHAKING_KEY, treeShaking); addOptionalDumpEntry(builder, MINIFICATION_KEY, minification); addOptionalDumpEntry(builder, FORCE_PROGUARD_COMPATIBILITY_KEY, forceProguardCompatibility); + System.getProperties() + .stringPropertyNames() + .forEach( + name -> { + if (name.startsWith("com.android.tools.r8.")) { + String value = System.getProperty(name); + addDumpEntry(builder, SYSTEM_PROPERTY_PREFIX + name, value); + } + }); return builder.toString(); }
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java index 7cd95e5..fc11da4 100644 --- a/src/main/java/com/android/tools/r8/L8Command.java +++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -100,6 +100,7 @@ List<Consumer<Inspector>> outputInspections, int threadCount, DumpInputFlags dumpInputFlags, + MapIdProvider mapIdProvider, DexItemFactory factory) { super( inputApp, @@ -115,7 +116,8 @@ assertionsConfiguration, outputInspections, threadCount, - dumpInputFlags); + dumpInputFlags, + mapIdProvider); this.d8Command = d8Command; this.r8Command = r8Command; this.libraryConfiguration = libraryConfiguration; @@ -409,6 +411,7 @@ getOutputInspections(), getThreadCount(), getDumpInputFlags(), + getMapIdProvider(), factory); } }
diff --git a/src/main/java/com/android/tools/r8/MapIdEnvironment.java b/src/main/java/com/android/tools/r8/MapIdEnvironment.java new file mode 100644 index 0000000..f1e4260 --- /dev/null +++ b/src/main/java/com/android/tools/r8/MapIdEnvironment.java
@@ -0,0 +1,12 @@ +// 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; + +/** Environment made available when defining a custom map id for a build. */ +@Keep +public interface MapIdEnvironment { + + /** Get the computed hash for the mapping file content. */ + String getMapHash(); +}
diff --git a/src/main/java/com/android/tools/r8/MapIdProvider.java b/src/main/java/com/android/tools/r8/MapIdProvider.java new file mode 100644 index 0000000..9af775f --- /dev/null +++ b/src/main/java/com/android/tools/r8/MapIdProvider.java
@@ -0,0 +1,25 @@ +// 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; + +/** + * Interface for providing a custom map-id to the compiler. + * + * <p>The map-id is inserted in the mapping file output and included in the compiler marker + * information. The map-id can be used to provide an key/identifier to automate the lookup of + * mapping file information for builds. For example, by including it in the source-file part of + * program stacktraces. See {@code SourceFileProvider}. + */ +@Keep +@FunctionalInterface +public interface MapIdProvider { + + /** + * Return the map-id content. + * + * @param environment An environment of values available for use when defining the map id. + * @return A non-null string that will be used as the map id. + */ + String get(MapIdEnvironment environment); +}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index fdf35d6..bb22da1 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -585,7 +585,8 @@ synthesizedClassPrefix, skipDump, getThreadCount(), - getDumpInputFlags()); + getDumpInputFlags(), + getMapIdProvider()); return command; } @@ -746,7 +747,8 @@ String synthesizedClassPrefix, boolean skipDump, int threadCount, - DumpInputFlags dumpInputFlags) { + DumpInputFlags dumpInputFlags, + MapIdProvider mapIdProvider) { super( inputApp, mode, @@ -761,7 +763,8 @@ assertionsConfiguration, outputInspections, threadCount, - dumpInputFlags); + dumpInputFlags, + mapIdProvider); assert proguardConfiguration != null; assert mainDexKeepRules != null; this.mainDexKeepRules = mainDexKeepRules; @@ -874,6 +877,8 @@ internal.enableVerticalClassMerging = false; } + internal.mapIdProvider = getMapIdProvider(); + // Amend the proguard-map consumer with options from the proguard configuration. internal.proguardMapConsumer = wrapStringConsumer(
diff --git a/src/main/java/com/android/tools/r8/StringConsumer.java b/src/main/java/com/android/tools/r8/StringConsumer.java index 843d145..d0e05cc 100644 --- a/src/main/java/com/android/tools/r8/StringConsumer.java +++ b/src/main/java/com/android/tools/r8/StringConsumer.java
@@ -48,6 +48,7 @@ } /** Empty consumer to request the production of the resource but ignore its value. */ + @Keep class EmptyConsumer implements StringConsumer { private static final EmptyConsumer EMPTY_CONSUMER = new EmptyConsumer(); @@ -65,7 +66,7 @@ /** Forwarding consumer to delegate to an optional existing consumer. */ @Keep - public class ForwardingConsumer implements StringConsumer { + class ForwardingConsumer implements StringConsumer { private final StringConsumer consumer;
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java new file mode 100644 index 0000000..058201f --- /dev/null +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java
@@ -0,0 +1,26 @@ +// 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.androidapi; + +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.AndroidApiLevel; +import java.util.function.BiConsumer; + +/** + * This is an interface for all generated classes from api-versions.xml for building a database from + * a serialized hashed format. + */ +public interface AndroidApiForHashingClass { + + DexType getType(); + + AndroidApiLevel getApiLevel(); + + void visitMethodsWithApiLevels(BiConsumer<DexMethod, AndroidApiLevel> consumer); + + void visitFieldsWithApiLevels(BiConsumer<DexField, AndroidApiLevel> consumer); +}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java new file mode 100644 index 0000000..551085a --- /dev/null +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -0,0 +1,159 @@ +// 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.androidapi; + +import static com.android.tools.r8.utils.AndroidApiLevel.NOT_SET; +import static com.android.tools.r8.utils.AndroidApiLevel.UNKNOWN; + +import com.android.tools.r8.errors.CompilationError; +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.DexReference; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.structural.DefaultHashingVisitor; +import com.android.tools.r8.utils.structural.HasherWrapper; +import com.android.tools.r8.utils.structural.StructuralItem; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class AndroidApiLevelHashingDatabaseImpl implements AndroidApiLevelDatabase { + + public static HasherWrapper getDefaultHasher() { + return HasherWrapper.murmur3128Hasher(); + } + + private final Int2ReferenceMap<AndroidApiLevel> lookupNonAmbiguousCache = + new Int2ReferenceOpenHashMap<AndroidApiLevel>(); + private final Map<String, AndroidApiLevel> ambiguousHashesWithApiLevel = new HashMap<>(); + private final Map<DexReference, AndroidApiLevel> ambiguousCache = new IdentityHashMap<>(); + private final DexItemFactory factory; + + public AndroidApiLevelHashingDatabaseImpl( + DexItemFactory factory, List<AndroidApiForHashingClass> predefinedApiTypeLookup) { + this.factory = factory; + loadData(); + predefinedApiTypeLookup.forEach( + apiClass -> { + DexType type = apiClass.getType(); + lookupNonAmbiguousCache.put(type.hashCode(), NOT_SET); + ambiguousCache.put(type, apiClass.getApiLevel()); + apiClass.visitMethodsWithApiLevels( + (method, apiLevel) -> { + lookupNonAmbiguousCache.put(method.hashCode(), NOT_SET); + ambiguousCache.put(method, apiLevel); + }); + apiClass.visitFieldsWithApiLevels( + (field, apiLevel) -> { + lookupNonAmbiguousCache.put(field.hashCode(), NOT_SET); + ambiguousCache.put(field, apiLevel); + }); + }); + } + + private void loadData() { + int[] hashIndices; + byte[] apiLevels; + List<String> ambiguous; + try (InputStream indicesInputStream = + getClass() + .getClassLoader() + .getResourceAsStream("api_database/api_database_hash_lookup.ser"); + ObjectInputStream indicesObjectStream = new ObjectInputStream(indicesInputStream); + InputStream apiInputStream = + getClass() + .getClassLoader() + .getResourceAsStream("api_database/api_database_api_level.ser"); + ObjectInputStream apiObjectStream = new ObjectInputStream(apiInputStream); + InputStream ambiguousInputStream = + getClass() + .getClassLoader() + .getResourceAsStream("api_database/api_database_ambiguous.txt")) { + hashIndices = (int[]) indicesObjectStream.readObject(); + apiLevels = (byte[]) apiObjectStream.readObject(); + ambiguous = + new BufferedReader(new InputStreamReader(ambiguousInputStream, StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.toList()); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException("Could not build api database"); + } + assert hashIndices.length == apiLevels.length; + for (int i = 0; i < hashIndices.length; i++) { + byte apiLevel = apiLevels[i]; + lookupNonAmbiguousCache.put( + hashIndices[i], apiLevel == -1 ? NOT_SET : AndroidApiLevel.getAndroidApiLevel(apiLevel)); + } + ambiguous.forEach(this::parseAmbiguous); + } + + /** + * All elements in the ambiguous map are on the form <key>:<api-level>. The reason for this + * additional map is that the keys collide for the items using the ordinary hashing function. + */ + private void parseAmbiguous(String ambiguous) { + String[] split = ambiguous.split(":"); + if (split.length != 2) { + throw new CompilationError("Expected two entries in ambiguous map"); + } + ambiguousHashesWithApiLevel.put( + split[0], AndroidApiLevel.getAndroidApiLevel(Integer.parseInt(split[1]))); + } + + @Override + public AndroidApiLevel getTypeApiLevel(DexType type) { + return lookupApiLevel(type); + } + + @Override + public AndroidApiLevel getMethodApiLevel(DexMethod method) { + return lookupApiLevel(method); + } + + @Override + public AndroidApiLevel getFieldApiLevel(DexField field) { + return lookupApiLevel(field); + } + + private AndroidApiLevel lookupApiLevel(DexReference reference) { + AndroidApiLevel result = lookupNonAmbiguousCache.getOrDefault(reference.hashCode(), UNKNOWN); + if (result != NOT_SET) { + return result; + } + return ambiguousCache.computeIfAbsent( + reference, + ignored -> { + HasherWrapper defaultHasher = getDefaultHasher(); + reference.accept( + type -> DefaultHashingVisitor.run(type, defaultHasher, DexType::acceptHashing), + field -> + DefaultHashingVisitor.run(field, defaultHasher, StructuralItem::acceptHashing), + method -> + DefaultHashingVisitor.run(method, defaultHasher, StructuralItem::acceptHashing)); + String existingHash = defaultHasher.hash().toString(); + AndroidApiLevel androidApiLevel = ambiguousHashesWithApiLevel.get(existingHash); + if (androidApiLevel == null) { + throw new CompilationError( + "Failed to find api level for reference: " + + reference.toSourceString() + + " with hash value: " + + existingHash); + } + return androidApiLevel; + }); + } +}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelObjectDatabaseImpl.java similarity index 61% rename from src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java rename to src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelObjectDatabaseImpl.java index d98013e..e48b221 100644 --- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelObjectDatabaseImpl.java
@@ -16,11 +16,13 @@ import com.android.tools.r8.utils.Box; import com.android.tools.r8.utils.TraversalContinuation; import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.BiFunction; -public class AndroidApiLevelDatabaseImpl implements AndroidApiLevelDatabase { +public class AndroidApiLevelObjectDatabaseImpl implements AndroidApiLevelDatabase { - private final HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup; + private final Map<DexType, AndroidApiClass> predefinedApiTypeLookup; private final AndroidApiClass SENTINEL = new AndroidApiClass(null) { @@ -52,8 +54,57 @@ } }; - public AndroidApiLevelDatabaseImpl(HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup) { - this.predefinedApiTypeLookup = predefinedApiTypeLookup; + public AndroidApiLevelObjectDatabaseImpl( + List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) { + Map<DexType, AndroidApiClass> predefinedMap = new HashMap<>(); + for (AndroidApiForHashingClass androidApiClass : predefinedApiTypeLookupForHashing) { + predefinedMap.put( + androidApiClass.getType(), + new AndroidApiClass(androidApiClass.getType().asClassReference()) { + @Override + public AndroidApiLevel getApiLevel() { + return androidApiClass.getApiLevel(); + } + + @Override + public int getMemberCount() { + return 0; + } + + @Override + protected TraversalContinuation visitFields( + BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor, + ClassReference holder, + int minApiClass) { + Box<TraversalContinuation> continuationBox = + new Box<>(TraversalContinuation.CONTINUE); + androidApiClass.visitFieldsWithApiLevels( + (field, apiLevel) -> { + if (continuationBox.get().shouldContinue()) { + continuationBox.set(visitor.apply(field.asFieldReference(), apiLevel)); + } + }); + return continuationBox.get(); + } + + @Override + protected TraversalContinuation visitMethods( + BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor, + ClassReference holder, + int minApiClass) { + Box<TraversalContinuation> continuationBox = + new Box<>(TraversalContinuation.CONTINUE); + androidApiClass.visitMethodsWithApiLevels( + (method, apiLevel) -> { + if (continuationBox.get().shouldContinue()) { + continuationBox.set(visitor.apply(method.asMethodReference(), apiLevel)); + } + }); + return continuationBox.get(); + } + }); + } + this.predefinedApiTypeLookup = predefinedMap; } @Override
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java index 8ca1b59..df4cefd 100644 --- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java +++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -10,7 +10,8 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration; import com.android.tools.r8.utils.AndroidApiLevel; -import java.util.HashMap; +import com.google.common.collect.ImmutableList; +import java.util.List; public class AndroidApiReferenceLevelCache { @@ -19,13 +20,21 @@ private final AppView<?> appView; private AndroidApiReferenceLevelCache(AppView<?> appView) { - this(appView, new HashMap<>()); + this(appView, ImmutableList.of()); } private AndroidApiReferenceLevelCache( - AppView<?> appView, HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup) { + AppView<?> appView, List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) { this.appView = appView; - androidApiLevelDatabase = new AndroidApiLevelDatabaseImpl(predefinedApiTypeLookup); + // TODO(b/199934316): When the implementation has been decided on, remove the others. + if (appView.options().apiModelingOptions().useHashingDatabase) { + androidApiLevelDatabase = + new AndroidApiLevelHashingDatabaseImpl( + appView.dexItemFactory(), predefinedApiTypeLookupForHashing); + } else { + androidApiLevelDatabase = + new AndroidApiLevelObjectDatabaseImpl(predefinedApiTypeLookupForHashing); + } desugaredLibraryConfiguration = appView.options().desugaredLibraryConfiguration; } @@ -40,18 +49,12 @@ } }; } - // The apiTypeLookup is build lazily except for the mocked api types that we define in tests - // externally. - HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup = new HashMap<>(); + ImmutableList.Builder<AndroidApiForHashingClass> builder = ImmutableList.builder(); appView .options() .apiModelingOptions() - .visitMockedApiReferences( - (classReference, androidApiClass) -> - predefinedApiTypeLookup.put( - appView.dexItemFactory().createType(classReference.getDescriptor()), - androidApiClass)); - return new AndroidApiReferenceLevelCache(appView, predefinedApiTypeLookup); + .visitMockedApiLevelsForReferences(appView.dexItemFactory(), builder::add); + return new AndroidApiReferenceLevelCache(appView, builder.build()); } public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) { @@ -61,10 +64,14 @@ public AndroidApiLevel lookup(DexReference reference) { DexType contextType = reference.getContextType(); if (contextType.isArrayType()) { + if (reference.isDexMethod() + && reference.asDexMethod().match(appView.dexItemFactory().objectMembers.clone)) { + return appView.options().minApiLevel; + } return lookup(contextType.toBaseType(appView.dexItemFactory())); } if (contextType.isPrimitiveType() || contextType.isVoidType()) { - return AndroidApiLevel.B; + return appView.options().minApiLevel; } DexClass clazz = appView.definitionFor(contextType); if (clazz == null) { @@ -73,6 +80,9 @@ if (!clazz.isLibraryClass()) { return appView.options().minApiLevel; } + if (isReferenceToJavaLangObject(reference)) { + return appView.options().minApiLevel; + } if (desugaredLibraryConfiguration.isSupported(reference, appView)) { // If we end up desugaring the reference, the library classes is bridged by j$ which is part // of the program. @@ -83,4 +93,12 @@ androidApiLevelDatabase::getFieldApiLevel, androidApiLevelDatabase::getMethodApiLevel); } + + private boolean isReferenceToJavaLangObject(DexReference reference) { + if (reference.getContextType() == appView.dexItemFactory().objectType) { + return true; + } + return reference.isDexMethod() + && appView.dexItemFactory().objectMembers.isObjectMember(reference.asDexMethod()); + } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java index cfad75e..2220c7d 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfCheckCast.java
@@ -87,7 +87,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerCheckCast(type); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java index b7524be..78fbcd2 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstClass.java
@@ -118,7 +118,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerConstClass(type, iterator); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java index e6e1f0d..f86c213 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -200,7 +200,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerTypeReference(reference.getType()); registry.registerMethodHandle( reference.getBootstrapMethod(), NOT_ARGUMENT_TO_LAMBDA_METAFACTORY);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java index 7391677..b495441 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodHandle.java
@@ -73,7 +73,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerMethodHandle(handle, MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java index e85db92..3578a70 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstMethodType.java
@@ -71,7 +71,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerProto(type); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java index 924fa7e..1958b5c 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfDexItemBasedConstString.java
@@ -94,7 +94,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { if (nameComputationInfo.needsToRegisterReference()) { assert item.isDexType(); registry.registerTypeReference(item.asDexType());
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java index cd8d6b7..a2f4e80 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -111,7 +111,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { switch (opcode) { case Opcodes.GETFIELD: registry.registerInstanceFieldRead(field);
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java index 8eb0db7..929a010 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInitClass.java
@@ -87,7 +87,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerInitClass(clazz); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java index 8eab765..8b82246 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstanceOf.java
@@ -96,7 +96,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerInstanceOf(type); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java index 188c575..ab73479 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -92,7 +92,7 @@ } void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { // Intentionally empty. }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java index 625430b..e9cd796 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -5,7 +5,6 @@ import com.android.tools.r8.cf.CfPrinter; import com.android.tools.r8.dex.Constants; -import com.android.tools.r8.errors.CompilationError; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.CfCode; @@ -15,7 +14,6 @@ import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; -import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLens; @@ -34,7 +32,6 @@ import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.naming.NamingLens; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.structural.CompareToVisitor; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.Arrays; @@ -106,7 +103,8 @@ LensCodeRewriterUtils rewriter, MethodVisitor visitor) { MethodLookupResult lookup = - graphLens.lookupMethod(method, context.getReference(), getInvokeType(context)); + graphLens.lookupMethod( + method, context.getReference(), getInvokeType(context, dexItemFactory)); DexMethod rewrittenMethod = lookup.getReference(); String owner = namingLens.lookupInternalName(rewrittenMethod.holder); String name = namingLens.lookupName(rewrittenMethod).toString(); @@ -121,8 +119,8 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { - Type invokeType = getInvokeType(context); + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + Type invokeType = getInvokeType(context, registry.dexItemFactory()); switch (invokeType) { case DIRECT: registry.registerInvokeDirect(method); @@ -146,7 +144,7 @@ // We should avoid interpreting a CF invoke using DEX semantics. @Deprecated - public Invoke.Type getInvokeType(DexClassAndMethod context) { + private Invoke.Type getInvokeType(DexClassAndMethod context, DexItemFactory dexItemFactory) { switch (opcode) { case Opcodes.INVOKEINTERFACE: return Type.INTERFACE; @@ -155,8 +153,8 @@ return Type.VIRTUAL; case Opcodes.INVOKESPECIAL: - if (method.name.toString().equals(Constants.INSTANCE_INITIALIZER_NAME) - || method.holder == context.getHolderType()) { + if (method.isInstanceInitializer(dexItemFactory) + || method.getHolderType() == context.getHolderType()) { return Type.DIRECT; } return Type.SUPER; @@ -247,7 +245,7 @@ canonicalMethod = method; type = computeInvokeTypeForInvokeSpecial( - builder.appView, method, code.getOriginalHolder(), code.getOrigin()); + builder.appView, method, builder.getProgramMethod(), code.getOriginalHolder()); break; } case Opcodes.INVOKESTATIC: @@ -296,8 +294,7 @@ case Opcodes.INVOKESPECIAL: { Type actualInvokeType = - computeInvokeTypeForInvokeSpecial( - appView, method, code.getOriginalHolder(), context.getOrigin()); + computeInvokeTypeForInvokeSpecial(appView, method, context, code.getOriginalHolder()); type = graphLens.lookupMethod(target, context.getReference(), actualInvokeType).getType(); } break; @@ -362,55 +359,38 @@ } private Type computeInvokeTypeForInvokeSpecial( - AppView<?> appView, DexMethod method, DexType originalHolder, Origin origin) { + AppView<?> appView, DexMethod method, ProgramMethod context, DexType originalHolder) { if (appView.dexItemFactory().isConstructor(method)) { return Type.DIRECT; } - if (originalHolder == method.holder) { - return invokeTypeForInvokeSpecialToNonInitMethodOnHolder(appView, origin); + if (originalHolder != method.getHolderType()) { + return Type.SUPER; } - return Type.SUPER; + return invokeTypeForInvokeSpecialToNonInitMethodOnHolder(context, appView.graphLens()); } private Type invokeTypeForInvokeSpecialToNonInitMethodOnHolder( - AppView<?> appView, Origin origin) { - boolean desugaringEnabled = appView.options().isInterfaceMethodDesugaringEnabled(); - MethodLookupResult lookupResult = appView.graphLens().lookupMethod(method, method, Type.DIRECT); - if (lookupResult.getType() == Type.VIRTUAL) { - // The method has been publicized. We can't always expect private methods that have been - // publicized to be final. For example, if a private method A.m() is publicized, and A is - // subsequently merged with a class B, with declares a public non-final method B.m(), then the - // horizontal class merger will merge A.m() and B.m() into a new non-final public method. - return Type.VIRTUAL; - } - DexMethod rewrittenMethod = lookupResult.getReference(); - DexEncodedMethod definition = lookupMethodOnHolder(appView, rewrittenMethod); + ProgramMethod context, GraphLens graphLens) { + MethodLookupResult lookupResult = + graphLens.lookupMethod(method, context.getReference(), Type.DIRECT); + DexEncodedMethod definition = context.getHolder().lookupMethod(lookupResult.getReference()); if (definition == null) { - // The method is not defined on the class, we can use super to target. When desugaring - // default interface methods, it is expected they are targeted with invoke-direct. - return this.itf && desugaringEnabled ? Type.DIRECT : Type.SUPER; + return Type.SUPER; } - if (definition.isPrivateMethod() || !definition.isVirtualMethod()) { - return Type.DIRECT; - } - if (definition.isFinal()) { - // This method is final which indicates no subtype will overwrite it, we can use - // invoke-virtual. - return Type.VIRTUAL; - } - if (itf && definition.isDefaultMethod()) { - return desugaringEnabled ? Type.DIRECT : Type.SUPER; - } - // We cannot emulate the semantics of invoke-special in this case and should throw a compilation - // error. - throw new CompilationError("Failed to compile unsupported use of invokespecial", origin); - } - private DexEncodedMethod lookupMethodOnHolder(AppView<?> appView, DexMethod method) { - // Directly lookup the program type for holder. This bypasses lookup order as well as looks - // directly on the application data, which bypasses and indirection or validation. - DexProgramClass clazz = appView.appInfo().unsafeDirectProgramTypeLookup(method.getHolderType()); - assert clazz != null; - return clazz.lookupMethod(method); + if (context.getHolder().isInterface()) { + // On interfaces invoke-special should be mapped to invoke-super if the invoke-special + // instruction is used to target a default interface method. + if (definition.belongsToVirtualPool()) { + return Type.SUPER; + } + } else { + // Due to desugaring of invoke-special instructions that target virtual methods, this invoke + // should only target a virtual method if the method has been publicized in R8 (in which case + // the invoke instruction has a pending rewrite to invoke-virtual). + assert definition.isPrivate() || lookupResult.getType().isVirtual(); + } + + return Type.DIRECT; } }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java index 3138b27..644ed7d 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -127,7 +127,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerCallSite(callSite); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java index e83b18d..b798257 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -98,7 +98,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerTypeReference(type); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNew.java b/src/main/java/com/android/tools/r8/cf/code/CfNew.java index 0fe8c96..ff87575 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNew.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNew.java
@@ -87,7 +87,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerNewInstance(type); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java index 559f3ba..769fe22 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNewArray.java
@@ -133,7 +133,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { if (!type.isPrimitiveArrayType()) { registry.registerTypeReference(type); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java index 89f8686..335cce5 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
@@ -90,7 +90,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerNewUnboxedEnumInstance(type); }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSafeCheckCast.java b/src/main/java/com/android/tools/r8/cf/code/CfSafeCheckCast.java index e1473d3..9fe64c6 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfSafeCheckCast.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfSafeCheckCast.java
@@ -24,7 +24,7 @@ @Override void internalRegisterUse( - UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + UseRegistry<?> registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { registry.registerSafeCheckCast(getType()); }
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java index 9f3739a..cb21b8c 100644 --- a/src/main/java/com/android/tools/r8/code/CheckCast.java +++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -82,7 +82,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerCheckCast(getType()); }
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java index b96f4aa..f57556a 100644 --- a/src/main/java/com/android/tools/r8/code/ConstClass.java +++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -72,7 +72,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerConstClass(getType(), null); }
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java index 2f05b0b..0df963f 100644 --- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java +++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -67,7 +67,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerMethodHandle( getMethodHandle(), MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY); }
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java index 7662fe4..478d3b5 100644 --- a/src/main/java/com/android/tools/r8/code/ConstMethodType.java +++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -66,7 +66,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerProto(getMethodType()); }
diff --git a/src/main/java/com/android/tools/r8/code/DexInitClass.java b/src/main/java/com/android/tools/r8/code/DexInitClass.java index 8e85bd0..545c24c 100644 --- a/src/main/java/com/android/tools/r8/code/DexInitClass.java +++ b/src/main/java/com/android/tools/r8/code/DexInitClass.java
@@ -107,7 +107,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInitClass(clazz); }
diff --git a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java index 1a434fd..70e9c4f 100644 --- a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java +++ b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
@@ -107,7 +107,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { if (nameComputationInfo.needsToRegisterReference()) { assert getItem().isDexType(); registry.registerTypeReference(getItem().asDexType());
diff --git a/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java index f9c26e6..9909ac6 100644 --- a/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java +++ b/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java
@@ -69,7 +69,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerNewUnboxedEnumInstance(getType()); }
diff --git a/src/main/java/com/android/tools/r8/code/Iget.java b/src/main/java/com/android/tools/r8/code/Iget.java index 3625037..71c11c3 100644 --- a/src/main/java/com/android/tools/r8/code/Iget.java +++ b/src/main/java/com/android/tools/r8/code/Iget.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IgetBoolean.java b/src/main/java/com/android/tools/r8/code/IgetBoolean.java index 0df2731..36c00b6 100644 --- a/src/main/java/com/android/tools/r8/code/IgetBoolean.java +++ b/src/main/java/com/android/tools/r8/code/IgetBoolean.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IgetByte.java b/src/main/java/com/android/tools/r8/code/IgetByte.java index f3cb157..8d57bb5 100644 --- a/src/main/java/com/android/tools/r8/code/IgetByte.java +++ b/src/main/java/com/android/tools/r8/code/IgetByte.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IgetChar.java b/src/main/java/com/android/tools/r8/code/IgetChar.java index 2c0793c..2ba0e26 100644 --- a/src/main/java/com/android/tools/r8/code/IgetChar.java +++ b/src/main/java/com/android/tools/r8/code/IgetChar.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IgetObject.java b/src/main/java/com/android/tools/r8/code/IgetObject.java index 802409e..c685229 100644 --- a/src/main/java/com/android/tools/r8/code/IgetObject.java +++ b/src/main/java/com/android/tools/r8/code/IgetObject.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IgetShort.java b/src/main/java/com/android/tools/r8/code/IgetShort.java index a8ca9a7..77667ba 100644 --- a/src/main/java/com/android/tools/r8/code/IgetShort.java +++ b/src/main/java/com/android/tools/r8/code/IgetShort.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IgetWide.java b/src/main/java/com/android/tools/r8/code/IgetWide.java index bd63727..a71b95a 100644 --- a/src/main/java/com/android/tools/r8/code/IgetWide.java +++ b/src/main/java/com/android/tools/r8/code/IgetWide.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/InstanceOf.java b/src/main/java/com/android/tools/r8/code/InstanceOf.java index 6c9d960..c1b8293 100644 --- a/src/main/java/com/android/tools/r8/code/InstanceOf.java +++ b/src/main/java/com/android/tools/r8/code/InstanceOf.java
@@ -68,7 +68,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceOf(getType()); }
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java index ccfd713..96e754e 100644 --- a/src/main/java/com/android/tools/r8/code/Instruction.java +++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -385,7 +385,7 @@ return this.equals(other); } - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { // Intentionally empty }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java index 1355f13..ea07822 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeCustom.java +++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -54,7 +54,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerCallSite(getCallSite()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java index 2d09e4f..c057c94 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
@@ -59,7 +59,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerCallSite(getCallSite()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/code/InvokeDirect.java index 6786de8..4f5938f 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeDirect.java +++ b/src/main/java/com/android/tools/r8/code/InvokeDirect.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeDirect(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java index 843d968..6f232c0 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeDirectRange.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeDirect(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/code/InvokeInterface.java index 89b8a16..2e20c9c 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeInterface.java +++ b/src/main/java/com/android/tools/r8/code/InvokeInterface.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeInterface(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java index e710b8c..7592e3d 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeInterfaceRange.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeInterface(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java index 45eb6e8..07cf3a0 100644 --- a/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokePolymorphicRange.java
@@ -47,7 +47,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeDirect(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/code/InvokeStatic.java index 796b183..fb198e4 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeStatic.java +++ b/src/main/java/com/android/tools/r8/code/InvokeStatic.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeStatic(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java index 20809c9..de84a90 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeStaticRange.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeStatic(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/code/InvokeSuper.java index d2d6e28..24ab80e 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeSuper.java +++ b/src/main/java/com/android/tools/r8/code/InvokeSuper.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeSuper(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java index 70fd726..ac820c9 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeSuperRange.java
@@ -44,7 +44,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeSuper(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java index 9b9e064..27e37c5 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeVirtual.java +++ b/src/main/java/com/android/tools/r8/code/InvokeVirtual.java
@@ -54,7 +54,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeVirtual(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java index a4f2e98..e93733f 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeVirtualRange.java
@@ -54,7 +54,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInvokeVirtual(getMethod()); }
diff --git a/src/main/java/com/android/tools/r8/code/Iput.java b/src/main/java/com/android/tools/r8/code/Iput.java index ea170c7..76f90af 100644 --- a/src/main/java/com/android/tools/r8/code/Iput.java +++ b/src/main/java/com/android/tools/r8/code/Iput.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IputBoolean.java b/src/main/java/com/android/tools/r8/code/IputBoolean.java index 8a992c7..634657d 100644 --- a/src/main/java/com/android/tools/r8/code/IputBoolean.java +++ b/src/main/java/com/android/tools/r8/code/IputBoolean.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IputByte.java b/src/main/java/com/android/tools/r8/code/IputByte.java index 4005875..b579bec 100644 --- a/src/main/java/com/android/tools/r8/code/IputByte.java +++ b/src/main/java/com/android/tools/r8/code/IputByte.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IputChar.java b/src/main/java/com/android/tools/r8/code/IputChar.java index 55f72fe..1b0d42d 100644 --- a/src/main/java/com/android/tools/r8/code/IputChar.java +++ b/src/main/java/com/android/tools/r8/code/IputChar.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IputObject.java b/src/main/java/com/android/tools/r8/code/IputObject.java index 5ca2f90..816f81f 100644 --- a/src/main/java/com/android/tools/r8/code/IputObject.java +++ b/src/main/java/com/android/tools/r8/code/IputObject.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IputShort.java b/src/main/java/com/android/tools/r8/code/IputShort.java index bf41801..dfa9ff7 100644 --- a/src/main/java/com/android/tools/r8/code/IputShort.java +++ b/src/main/java/com/android/tools/r8/code/IputShort.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/IputWide.java b/src/main/java/com/android/tools/r8/code/IputWide.java index 4462875..261dc26 100644 --- a/src/main/java/com/android/tools/r8/code/IputWide.java +++ b/src/main/java/com/android/tools/r8/code/IputWide.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerInstanceFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/NewArray.java b/src/main/java/com/android/tools/r8/code/NewArray.java index a0104a2..35b3187 100644 --- a/src/main/java/com/android/tools/r8/code/NewArray.java +++ b/src/main/java/com/android/tools/r8/code/NewArray.java
@@ -54,7 +54,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerTypeReference(getType()); }
diff --git a/src/main/java/com/android/tools/r8/code/NewInstance.java b/src/main/java/com/android/tools/r8/code/NewInstance.java index 44d2613..1aed86e 100644 --- a/src/main/java/com/android/tools/r8/code/NewInstance.java +++ b/src/main/java/com/android/tools/r8/code/NewInstance.java
@@ -72,7 +72,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerNewInstance(getType()); }
diff --git a/src/main/java/com/android/tools/r8/code/SafeCheckCast.java b/src/main/java/com/android/tools/r8/code/SafeCheckCast.java index e4af2a0..7fff773 100644 --- a/src/main/java/com/android/tools/r8/code/SafeCheckCast.java +++ b/src/main/java/com/android/tools/r8/code/SafeCheckCast.java
@@ -25,7 +25,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerSafeCheckCast(getType()); } }
diff --git a/src/main/java/com/android/tools/r8/code/Sget.java b/src/main/java/com/android/tools/r8/code/Sget.java index 0c727ac..c2d93df 100644 --- a/src/main/java/com/android/tools/r8/code/Sget.java +++ b/src/main/java/com/android/tools/r8/code/Sget.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SgetBoolean.java b/src/main/java/com/android/tools/r8/code/SgetBoolean.java index 3efb6b1..3d79934 100644 --- a/src/main/java/com/android/tools/r8/code/SgetBoolean.java +++ b/src/main/java/com/android/tools/r8/code/SgetBoolean.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SgetByte.java b/src/main/java/com/android/tools/r8/code/SgetByte.java index 9a40297..08f2b97 100644 --- a/src/main/java/com/android/tools/r8/code/SgetByte.java +++ b/src/main/java/com/android/tools/r8/code/SgetByte.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SgetChar.java b/src/main/java/com/android/tools/r8/code/SgetChar.java index 429eaa6..5ce7f8c 100644 --- a/src/main/java/com/android/tools/r8/code/SgetChar.java +++ b/src/main/java/com/android/tools/r8/code/SgetChar.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SgetObject.java b/src/main/java/com/android/tools/r8/code/SgetObject.java index 6b1130d..e4ae240 100644 --- a/src/main/java/com/android/tools/r8/code/SgetObject.java +++ b/src/main/java/com/android/tools/r8/code/SgetObject.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SgetShort.java b/src/main/java/com/android/tools/r8/code/SgetShort.java index 37f331e..62e2de7 100644 --- a/src/main/java/com/android/tools/r8/code/SgetShort.java +++ b/src/main/java/com/android/tools/r8/code/SgetShort.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SgetWide.java b/src/main/java/com/android/tools/r8/code/SgetWide.java index 638ca0c..962d0c1 100644 --- a/src/main/java/com/android/tools/r8/code/SgetWide.java +++ b/src/main/java/com/android/tools/r8/code/SgetWide.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldRead(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/Sput.java b/src/main/java/com/android/tools/r8/code/Sput.java index e6f3b4e..e9e7f56 100644 --- a/src/main/java/com/android/tools/r8/code/Sput.java +++ b/src/main/java/com/android/tools/r8/code/Sput.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SputBoolean.java b/src/main/java/com/android/tools/r8/code/SputBoolean.java index 6dae9b2..d3ba949 100644 --- a/src/main/java/com/android/tools/r8/code/SputBoolean.java +++ b/src/main/java/com/android/tools/r8/code/SputBoolean.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SputByte.java b/src/main/java/com/android/tools/r8/code/SputByte.java index c6cb521..e1c09f5 100644 --- a/src/main/java/com/android/tools/r8/code/SputByte.java +++ b/src/main/java/com/android/tools/r8/code/SputByte.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SputChar.java b/src/main/java/com/android/tools/r8/code/SputChar.java index 727ba25..2497f76 100644 --- a/src/main/java/com/android/tools/r8/code/SputChar.java +++ b/src/main/java/com/android/tools/r8/code/SputChar.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SputObject.java b/src/main/java/com/android/tools/r8/code/SputObject.java index 4850664..43a92b6 100644 --- a/src/main/java/com/android/tools/r8/code/SputObject.java +++ b/src/main/java/com/android/tools/r8/code/SputObject.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SputShort.java b/src/main/java/com/android/tools/r8/code/SputShort.java index eb33e33..2335606 100644 --- a/src/main/java/com/android/tools/r8/code/SputShort.java +++ b/src/main/java/com/android/tools/r8/code/SputShort.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/code/SputWide.java b/src/main/java/com/android/tools/r8/code/SputWide.java index 8104f75..278418a 100644 --- a/src/main/java/com/android/tools/r8/code/SputWide.java +++ b/src/main/java/com/android/tools/r8/code/SputWide.java
@@ -38,7 +38,7 @@ } @Override - public void registerUse(UseRegistry registry) { + public void registerUse(UseRegistry<?> registry) { registry.registerStaticFieldWrite(getField()); }
diff --git a/src/main/java/com/android/tools/r8/contexts/CompilationContext.java b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java index f037883..d3db39a 100644 --- a/src/main/java/com/android/tools/r8/contexts/CompilationContext.java +++ b/src/main/java/com/android/tools/r8/contexts/CompilationContext.java
@@ -6,8 +6,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.utils.InternalOptions; -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; +import com.android.tools.r8.utils.structural.HasherWrapper; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; @@ -143,9 +142,9 @@ private StringBuilder buildSuffix(StringBuilder builder) { // TODO(b/172194101): Sanitize the method descriptor instead of hashing. - Hasher hasher = Hashing.sha256().newHasher(); + HasherWrapper hasher = HasherWrapper.sha256Hasher(); method.getReference().hash(hasher); - return builder.append('$').append(hasher.hash().toString()); + return builder.append('$').append(hasher.hashCodeAsString()); } @Override
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 7595a40..0e1edc9 100644 --- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java +++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -8,12 +8,15 @@ import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.naming.NamingLens; +import com.android.tools.r8.utils.CollectionUtils; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.Sets; +import java.util.Comparator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -155,7 +158,14 @@ // TODO(b/134734081): Stream the consumer instead of building the String. StringBuilder sb = new StringBuilder(); String cr = System.lineSeparator(); - for (DexType type : toKeep.keySet()) { + Comparator<DexReference> comparator = + new Comparator<DexReference>() { + @Override + public int compare(DexReference o1, DexReference o2) { + return o1.compareTo(o2); + } + }; + for (DexType type : CollectionUtils.sort(toKeep.keySet(), getComparator())) { KeepStruct keepStruct = toKeep.get(type); sb.append("-keep class ").append(convertType(type)); if (keepStruct.all) { @@ -167,7 +177,7 @@ continue; } sb.append(" {").append(cr); - for (DexField field : keepStruct.fields) { + for (DexField field : CollectionUtils.sort(keepStruct.fields, getComparator())) { sb.append(" ") .append(convertType(field.type)) .append(" ") @@ -175,7 +185,7 @@ .append(";") .append(cr); } - for (DexMethod method : keepStruct.methods) { + for (DexMethod method : CollectionUtils.sort(keepStruct.methods, getComparator())) { sb.append(" ") .append(convertType(method.proto.returnType)) .append(" ") @@ -194,6 +204,15 @@ options.desugaredLibraryKeepRuleConsumer.accept(sb.toString(), options.reporter); options.desugaredLibraryKeepRuleConsumer.finished(options.reporter); } + + private static <T extends DexReference> Comparator<T> getComparator() { + return new Comparator<T>() { + @Override + public int compare(T o1, T o2) { + return o1.compareTo(o2); + } + }; + } } public static class NopCodeToKeep extends CodeToKeep {
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java index 03e0e31..0601b5c 100644 --- a/src/main/java/com/android/tools/r8/dex/DexParser.java +++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -581,7 +581,9 @@ case Constants.DBG_SET_FILE: { int nameIdx = dexReader.getUleb128p1(); DexString sourceFile = nameIdx == NO_INDEX ? null : indexedItems.getString(nameIdx); - events.add(dexItemFactory.createSetFile(sourceFile)); + if (options.readDebugSetFileEvent) { + events.add(dexItemFactory.createSetFile(sourceFile)); + } break; } default: {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java index 1bbd88a..dfd4f1c 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.dex.DebugBytecodeWriter; import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.dex.MixedSectionCollection; +import com.android.tools.r8.errors.InternalCompilerError; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.ir.code.Position; import com.android.tools.r8.utils.structural.CompareToVisitor; @@ -457,6 +458,13 @@ } } + /** + * Unused/unsupported set-file event. + * + * <p>The set-file event is unused by all DEX VMs and incorrect on some older VMs. It is + * represented in the type of events for completeness, but should never be emitted as part of + * writing DEX code. + */ public static class SetFile extends DexDebugEvent { DexString fileName; @@ -468,8 +476,7 @@ @Override public void writeOn( DebugBytecodeWriter writer, ObjectToOffsetMapping mapping, GraphLens graphLens) { - writer.putByte(Constants.DBG_SET_FILE); - writer.putString(fileName); + throw new InternalCompilerError("Unused/unsupported SetFile event should never be written"); } @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java index fd2c110..1f86536 100644 --- a/src/main/java/com/android/tools/r8/graph/DexField.java +++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.references.FieldReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralMapping; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.Collections; @@ -49,6 +50,13 @@ } @Override + public void acceptHashing(HashingVisitor visitor) { + visitor.visitDexType(holder); + visitor.visitDexString(name); + getReferencedTypes().forEach(visitor::visitDexType); + } + + @Override public DexField self() { return this; } @@ -192,6 +200,7 @@ return type.toSourceString() + " " + holder.toSourceString() + "." + name.toSourceString(); } + @Override public DexField withHolder(DexType holder, DexItemFactory dexItemFactory) { return dexItemFactory.createField(holder, type, name); }
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 d31d8b7..24c2a3a 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1370,6 +1370,11 @@ public final DexMethod constructor; public final DexMethod finalize; public final DexMethod toString; + public final DexMethod notify; + public final DexMethod notifyAll; + public final DexMethod wait; + public final DexMethod waitLong; + public final DexMethod waitLongInt; private ObjectMembers() { // The clone method is installed on each array, so one has to use method.match(clone). @@ -1382,6 +1387,36 @@ finalizeMethodName, voidType.descriptor, DexString.EMPTY_ARRAY); toString = createMethod(objectDescriptor, toStringMethodName, stringDescriptor, DexString.EMPTY_ARRAY); + notify = + createMethod(objectDescriptor, notifyMethodName, voidDescriptor, DexString.EMPTY_ARRAY); + notifyAll = + createMethod( + objectDescriptor, notifyAllMethodName, voidDescriptor, DexString.EMPTY_ARRAY); + wait = createMethod(objectDescriptor, waitMethodName, voidDescriptor, DexString.EMPTY_ARRAY); + waitLong = + createMethod( + objectDescriptor, waitMethodName, voidDescriptor, new DexString[] {longDescriptor}); + waitLongInt = + createMethod( + objectDescriptor, + waitMethodName, + voidDescriptor, + new DexString[] {longDescriptor, intDescriptor}); + } + + public boolean isObjectMember(DexMethod method) { + return method.match(clone) + || method.match(getClass) + || method.match(constructor) + || method.match(finalize) + || method.match(toString) + || method.match(hashCode) + || method.match(equals) + || method.match(notify) + || method.match(notifyAll) + || method.match(wait) + || method.match(waitLong) + || method.match(waitLongInt); } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java index 9b62855..1f5566c 100644 --- a/src/main/java/com/android/tools/r8/graph/DexMember.java +++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -60,4 +60,6 @@ public Iterable<DexType> getReferencedBaseTypes(DexItemFactory dexItemFactory) { return Iterables.transform(getReferencedTypes(), type -> type.toBaseType(dexItemFactory)); } + + public abstract DexMember<D, R> withHolder(DexType holder, DexItemFactory dexItemFactory); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java index d7f282f..4f23975 100644 --- a/src/main/java/com/android/tools/r8/graph/DexMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.references.TypeReference; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.HashingVisitor; import com.android.tools.r8.utils.structural.StructuralMapping; import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.ArrayList; @@ -60,6 +61,13 @@ return visitor.visitDexMethod(this, other); } + @Override + public void acceptHashing(HashingVisitor visitor) { + visitor.visitDexType(holder); + visitor.visitDexString(name); + getReferencedTypes().forEach(visitor::visitDexType); + } + public DexType getArgumentType(int argumentIndex, boolean isStatic) { if (isStatic) { return getParameter(argumentIndex); @@ -207,9 +215,7 @@ @Override public int computeHashCode() { - return holder.hashCode() - + proto.hashCode() * 7 - + name.hashCode() * 31; + return holder.hashCode() * 7 + proto.hashCode() * 29 + name.hashCode() * 31; } @Override @@ -225,7 +231,7 @@ @Override public boolean match(DexMethod method) { - return match(method.getProto(), method.getName()); + return method == this || match(method.getProto(), method.getName()); } public boolean match(DexMethodSignature method) { @@ -300,10 +306,11 @@ } public DexMethod withHolder(DexDefinition definition, DexItemFactory dexItemFactory) { - return withHolder(definition.getReference(), dexItemFactory); + return withHolder(definition.getContextType(), dexItemFactory); } - public DexMethod withHolder(DexReference reference, DexItemFactory dexItemFactory) { + @Override + public DexMethod withHolder(DexType reference, DexItemFactory dexItemFactory) { return dexItemFactory.createMethod(reference.getContextType(), proto, name); }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProto.java b/src/main/java/com/android/tools/r8/graph/DexProto.java index dc30560..990eccc 100644 --- a/src/main/java/com/android/tools/r8/graph/DexProto.java +++ b/src/main/java/com/android/tools/r8/graph/DexProto.java
@@ -55,7 +55,7 @@ @Override public int computeHashCode() { - return shorty.hashCode() + returnType.hashCode() * 7 + parameters.hashCode() * 31; + return shorty.hashCode() * 7 + returnType.hashCode() * 13 + parameters.hashCode() * 31; } public DexType getReturnType() {
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java index 4d1790e..7e1cd24 100644 --- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java +++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -52,7 +52,7 @@ definition.parameterAnnotationsList.collectIndexedItems(indexedItems); } - public void registerCodeReferences(UseRegistry registry) { + public void registerCodeReferences(UseRegistry<?> registry) { Code code = getDefinition().getCode(); if (code != null) { if (Log.ENABLED) { @@ -62,7 +62,7 @@ } } - public <T> T registerCodeReferencesWithResult(UseRegistryWithResult<T> registry) { + public <R> R registerCodeReferencesWithResult(UseRegistryWithResult<R, ?> registry) { registerCodeReferences(registry); return registry.getResult(); }
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java index 3913383..396fa40 100644 --- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java +++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -7,9 +7,11 @@ import com.android.tools.r8.utils.TraversalContinuation; import java.util.ListIterator; -public abstract class UseRegistry { +public abstract class UseRegistry<T extends Definition> { - private DexItemFactory factory; + private final T context; + private final DexItemFactory factory; + private TraversalContinuation continuation = TraversalContinuation.CONTINUE; public enum MethodHandleUse { @@ -17,7 +19,8 @@ NOT_ARGUMENT_TO_LAMBDA_METAFACTORY } - public UseRegistry(DexItemFactory factory) { + public UseRegistry(T context, DexItemFactory factory) { + this.context = context; this.factory = factory; } @@ -25,11 +28,19 @@ method.registerCodeReferences(this); } + public DexItemFactory dexItemFactory() { + return factory; + } + public void doBreak() { assert continuation.shouldContinue(); continuation = TraversalContinuation.BREAK; } + public final T getContext() { + return context; + } + public TraversalContinuation getTraversalContinuation() { return continuation; }
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistryWithResult.java b/src/main/java/com/android/tools/r8/graph/UseRegistryWithResult.java index 916a2f9..ee31347 100644 --- a/src/main/java/com/android/tools/r8/graph/UseRegistryWithResult.java +++ b/src/main/java/com/android/tools/r8/graph/UseRegistryWithResult.java
@@ -4,24 +4,24 @@ package com.android.tools.r8.graph; -public abstract class UseRegistryWithResult<T> extends UseRegistry { +public abstract class UseRegistryWithResult<R, T extends Definition> extends UseRegistry<T> { - private T result; + private R result; - public UseRegistryWithResult(DexItemFactory factory) { - super(factory); + public UseRegistryWithResult(T context, DexItemFactory factory) { + super(context, factory); } - public UseRegistryWithResult(DexItemFactory factory, T defaultResult) { - super(factory); + public UseRegistryWithResult(T context, DexItemFactory factory, R defaultResult) { + super(context, factory); this.result = defaultResult; } - public T getResult() { + public R getResult() { return result; } - public void setResult(T result) { + public void setResult(R result) { this.result = result; doBreak(); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java index 036a381..3a9f744 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.horizontalclassmerging.code; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.UseRegistry; @@ -37,7 +38,7 @@ } @Override - public Consumer<UseRegistry> getRegistryCallback() { + public Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method) { return this::registerReachableDefinitions; }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java index c258558..7cfd3dc 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/VirtualMethodEntryPointSynthesizedCode.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.horizontalclassmerging.code; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; @@ -15,7 +16,10 @@ import java.util.function.Consumer; public class VirtualMethodEntryPointSynthesizedCode extends SynthesizedCode { + + private final DexItemFactory dexItemFactory; private final Int2ReferenceSortedMap<DexMethod> mappedMethods; + private final DexMethod superMethod; public VirtualMethodEntryPointSynthesizedCode( Int2ReferenceSortedMap<DexMethod> mappedMethods, @@ -33,7 +37,9 @@ method, position, originalMethod)); + this.dexItemFactory = factory; this.mappedMethods = mappedMethods; + this.superMethod = superMethod; } private static DexMethod computeSuperMethodTarget( @@ -47,11 +53,11 @@ } @Override - public Consumer<UseRegistry> getRegistryCallback() { - return this::registerReachableDefinitions; + public Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method) { + return registry -> registerReachableDefinitions(method, registry); } - private void registerReachableDefinitions(UseRegistry registry) { + private void registerReachableDefinitions(DexClassAndMethod method, UseRegistry registry) { assert registry.getTraversalContinuation().shouldContinue(); for (DexMethod mappedMethod : mappedMethods.values()) { registry.registerInvokeDirect(mappedMethod); @@ -59,6 +65,11 @@ return; } } + DexMethod superMethodTarget = + computeSuperMethodTarget(superMethod, method.asProgramMethod(), dexItemFactory); + if (superMethodTarget != null) { + registry.registerInvokeSuper(superMethodTarget); + } } @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java index 6e9e19f..9336e2b 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -65,13 +65,13 @@ // Check that all accesses from [clazz] to classes or members from the current package of // [clazz] will continue to work. This is guaranteed if the methods of [clazz] do not access // any private or protected classes or members from the current package of [clazz]. - IllegalAccessDetector registry = new IllegalAccessDetector(appView, clazz); TraversalContinuation result = clazz.traverseProgramMethods( method -> { - registry.setContext(method); - method.registerCodeReferences(registry); - if (registry.foundIllegalAccess()) { + boolean foundIllegalAccess = + method.registerCodeReferencesWithResult( + new IllegalAccessDetector(appView, method)); + if (foundIllegalAccess) { return TraversalContinuation.BREAK; } return TraversalContinuation.CONTINUE;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java index e98e0d2..0aa7237 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -287,13 +287,10 @@ return true; } - class TrivialFieldAccessUseRegistry extends UseRegistry { - - private final ProgramMethod method; + class TrivialFieldAccessUseRegistry extends UseRegistry<ProgramMethod> { TrivialFieldAccessUseRegistry(ProgramMethod method) { - super(appView.dexItemFactory()); - this.method = method; + super(method, appView.dexItemFactory()); } private void registerFieldAccess(DexField reference, boolean isStatic, boolean isWrite) { @@ -307,8 +304,8 @@ DexEncodedField definition = field.getDefinition(); if (definition.isStatic() != isStatic - || appView.isCfByteCodePassThrough(method.getDefinition()) - || resolutionResult.isAccessibleFrom(method, appView).isPossiblyFalse()) { + || appView.isCfByteCodePassThrough(getContext().getDefinition()) + || resolutionResult.isAccessibleFrom(getContext(), appView).isPossiblyFalse()) { recordAccessThatCannotBeOptimized(field, definition); return; } @@ -328,7 +325,7 @@ if (constantFields.contains(definition) || (!isWrite && nonConstantFields.contains(definition))) { - methodsToReprocess.add(method); + methodsToReprocess.add(getContext()); } } @@ -352,7 +349,7 @@ AbstractAccessContexts accessContexts = fieldAccesses.computeIfAbsent(field, ignore -> new ConcreteAccessContexts()); assert accessContexts.isConcrete(); - accessContexts.asConcrete().recordAccess(field.getReference(), method); + accessContexts.asConcrete().recordAccess(field.getReference(), getContext()); } else if (!otherAccessContexts.isTop()) { // Now both read and written. fieldAccesses.put(field, AbstractAccessContexts.unknown());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java index e8770c7..8c71936 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoInliningReasonStrategy.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.ir.code.Instruction; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.optimize.Inliner.Reason; import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy; @@ -34,7 +35,10 @@ @Override public Reason computeInliningReason( - InvokeMethod invoke, ProgramMethod target, ProgramMethod context) { + InvokeMethod invoke, + ProgramMethod target, + ProgramMethod context, + MethodProcessor methodProcessor) { if (references.isAbstractGeneratedMessageLiteBuilder(context.getHolder()) && invoke.isInvokeSuper()) { // Aggressively inline invoke-super calls inside the GeneratedMessageLite builders. Such @@ -44,7 +48,7 @@ } return references.isDynamicMethod(target) || references.isDynamicMethodBridge(target) ? computeInliningReasonForDynamicMethod(invoke, target, context) - : parent.computeInliningReason(invoke, target, context); + : parent.computeInliningReason(invoke, target, context, methodProcessor); } private Reason computeInliningReasonForDynamicMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java index 2be47ca..f6f7d45 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -106,13 +106,13 @@ abstract boolean verifyAllMethodsWithCodeExists(); - class InvokeExtractor extends UseRegistry { + class InvokeExtractor extends UseRegistry<ProgramMethod> { private final Node currentMethod; private final Predicate<ProgramMethod> targetTester; InvokeExtractor(Node currentMethod, Predicate<ProgramMethod> targetTester) { - super(appView.dexItemFactory()); + super(currentMethod.getProgramMethod(), appView.dexItemFactory()); this.currentMethod = currentMethod; this.targetTester = targetTester; }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java index 993315b..162e0bb 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/CallSiteInformation.java
@@ -18,13 +18,13 @@ * * <p>For pinned methods (methods kept through Proguard keep rules) this will always answer <code> * false</code>. - * - * @param method */ public abstract boolean hasSingleCallSite(ProgramMethod method); public abstract boolean hasDoubleCallSite(ProgramMethod method); + public abstract void unsetCallSiteInformation(ProgramMethod method); + public static CallSiteInformation empty() { return EmptyCallSiteInformation.EMPTY_INFO; } @@ -42,6 +42,11 @@ public boolean hasDoubleCallSite(ProgramMethod method) { return false; } + + @Override + public void unsetCallSiteInformation(ProgramMethod method) { + // Intentionally empty. + } } static class CallGraphBasedCallSiteInformation extends CallSiteInformation { @@ -95,5 +100,11 @@ public boolean hasDoubleCallSite(ProgramMethod method) { return doubleCallSite.contains(method.getReference()); } + + @Override + public void unsetCallSiteInformation(ProgramMethod method) { + singleCallSite.remove(method.getReference()); + doubleCallSite.remove(method.getReference()); + } } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java index 6359283..1d1a698 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -677,8 +677,7 @@ { timing.begin("Build primary method processor"); PrimaryMethodProcessor primaryMethodProcessor = - PrimaryMethodProcessor.create( - appView.withLiveness(), postMethodProcessorBuilder, executorService, timing); + PrimaryMethodProcessor.create(appView.withLiveness(), executorService, timing); timing.end(); timing.begin("IR conversion phase 1"); assert appView.graphLens() == graphLensForPrimaryOptimizationPass; @@ -1977,4 +1976,16 @@ } return previous; } + + /** + * Called when a method is pruned as a result of optimizations during IR processing in R8, to + * allow optimizations that track sets of methods to fixup their state. + */ + public void pruneMethod(ProgramMethod method) { + assert appView.enableWholeProgramOptimizations(); + assert method.getHolder().lookupMethod(method.getReference()) == null; + if (inliner != null) { + inliner.pruneMethod(method); + } + } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LibraryDesugaredChecker.java b/src/main/java/com/android/tools/r8/ir/conversion/LibraryDesugaredChecker.java index 7707d9e..4533478 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/LibraryDesugaredChecker.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/LibraryDesugaredChecker.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; 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.DexProgramClass; import com.android.tools.r8.graph.DexString; @@ -16,7 +17,8 @@ import com.android.tools.r8.graph.DexValue; import com.android.tools.r8.graph.DexValue.DexValueArray; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.graph.UseRegistryWithResult; +import com.google.common.collect.Iterables; public class LibraryDesugaredChecker { private final AppView<?> appView; @@ -34,7 +36,7 @@ return tracer.isLibraryDesugared(); } - private static class IsLibraryDesugaredTracer extends UseRegistry { + private static class IsLibraryDesugaredTracer { private final DexString jDollarDescriptorPrefix; private final AppView<?> appView; @@ -43,7 +45,6 @@ public IsLibraryDesugaredTracer( AppView<?> appView, DexString jDollarDescriptorPrefix, DexProgramClass clazz) { - super(appView.dexItemFactory()); this.jDollarDescriptorPrefix = jDollarDescriptorPrefix; this.appView = appView; this.clazz = clazz; @@ -59,10 +60,10 @@ private void registerClass(DexProgramClass clazz) { if (clazz.superType != null) { - registerTypeReference(clazz.superType); + registerType(clazz.superType); } for (DexType implementsType : clazz.interfaces.values) { - registerTypeReference(implementsType); + registerType(implementsType); } if (isLibraryDesugared) { return; @@ -87,10 +88,10 @@ } private void registerMethod(DexMethod method) { - for (DexType type : method.getParameters().values) { - registerTypeReference(type); + for (DexType type : method.getParameters()) { + registerType(type); } - registerTypeReference(method.getReturnType()); + registerType(method.getReturnType()); } private void registerField(DexEncodedField field) { @@ -107,77 +108,107 @@ } } } - method.registerCodeReferences(this); + + if (!isLibraryDesugared) { + isLibraryDesugared = + method.registerCodeReferencesWithResult( + new IsLibraryDesugaredUseRegistry(method, appView.dexItemFactory())); + } } - @Override - public void registerInitClass(DexType type) { - registerType(type); - } + private class IsLibraryDesugaredUseRegistry + extends UseRegistryWithResult<Boolean, ProgramMethod> { - @Override - public void registerInvokeVirtual(DexMethod method) { - registerMethod(method); - } + public IsLibraryDesugaredUseRegistry(ProgramMethod context, DexItemFactory factory) { + super(context, factory, false); + } - @Override - public void registerInvokeDirect(DexMethod method) { - registerMethod(method); - } + private boolean registerField(DexField field) { + return registerType(field.getHolderType()) || registerType(field.getType()); + } - @Override - public void registerInvokeStatic(DexMethod method) { - registerMethod(method); - } + private boolean registerMethod(DexMethod method) { + return registerType(method.getReturnType()) + || Iterables.any(method.getParameters(), this::registerType); + } - @Override - public void registerInvokeInterface(DexMethod method) { - registerMethod(method); - } + private boolean registerType(DexType type) { + if (type.descriptor.startsWith(jDollarDescriptorPrefix)) { + setResult(true); + return true; + } + return false; + } - @Override - public void registerInvokeStatic(DexMethod method, boolean itf) { - registerMethod(method); - } + @Override + public void registerInitClass(DexType type) { + registerType(type); + } - @Override - public void registerInvokeSuper(DexMethod method) { - registerMethod(method); - } + @Override + public void registerInvokeVirtual(DexMethod method) { + registerMethod(method); + } - @Override - public void registerInstanceFieldRead(DexField field) { - registerField(field); - } + @Override + public void registerInvokeDirect(DexMethod method) { + registerMethod(method); + } - @Override - public void registerInstanceFieldWrite(DexField field) { - registerField(field); - } + @Override + public void registerInvokeStatic(DexMethod method) { + registerMethod(method); + } - @Override - public void registerNewInstance(DexType type) { - registerType(type); - } + @Override + public void registerInvokeInterface(DexMethod method) { + registerMethod(method); + } - @Override - public void registerStaticFieldRead(DexField field) { - registerField(field); - } + @Override + public void registerInvokeStatic(DexMethod method, boolean itf) { + registerMethod(method); + } - @Override - public void registerStaticFieldWrite(DexField field) { - registerField(field); - } + @Override + public void registerInvokeSuper(DexMethod method) { + registerMethod(method); + } - @Override - public void registerTypeReference(DexType type) { - registerType(type); - } + @Override + public void registerInstanceFieldRead(DexField field) { + registerField(field); + } - @Override - public void registerInstanceOf(DexType type) { - registerType(type); + @Override + public void registerInstanceFieldWrite(DexField field) { + registerField(field); + } + + @Override + public void registerNewInstance(DexType type) { + registerType(type); + } + + @Override + public void registerStaticFieldRead(DexField field) { + registerField(field); + } + + @Override + public void registerStaticFieldWrite(DexField field) { + registerField(field); + } + + @Override + public void registerTypeReference(DexType type) { + registerType(type); + } + + @Override + public void registerInstanceOf(DexType type) { + registerType(type); + } } } }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java index 5219a50..80b9cbd 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodOptimizationFeedback.java
@@ -37,7 +37,7 @@ void methodReturnsAbstractValue( DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue); - void unsetAbstractReturnValue(DexEncodedMethod method); + void unsetAbstractReturnValue(ProgramMethod method); void methodReturnsObjectWithUpperBoundType( DexEncodedMethod method, AppView<?> appView, TypeElement type);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java index 9857098..d0b0349 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -12,6 +12,10 @@ return false; } + public boolean isPostMethodProcessor() { + return false; + } + public abstract MethodProcessingContext createMethodProcessingContext(ProgramMethod method); public abstract boolean isProcessedConcurrently(ProgramMethod method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java index a25e9dc..a54b717 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -50,6 +50,11 @@ } @Override + public boolean isPostMethodProcessor() { + return true; + } + + @Override public boolean shouldApplyCodeRewritings(ProgramMethod method) { assert !wave.contains(method); return !processed.contains(method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java index 9bd75fe..bea221a 100644 --- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -38,29 +38,25 @@ private final AppView<?> appView; private final CallSiteInformation callSiteInformation; - private final PostMethodProcessor.Builder postMethodProcessorBuilder; private final Deque<SortedProgramMethodSet> waves; private ProcessorContext processorContext; private PrimaryMethodProcessor( AppView<AppInfoWithLiveness> appView, - PostMethodProcessor.Builder postMethodProcessorBuilder, CallGraph callGraph) { this.appView = appView; this.callSiteInformation = callGraph.createCallSiteInformation(appView); - this.postMethodProcessorBuilder = postMethodProcessorBuilder; - this.waves = createWaves(appView, callGraph, callSiteInformation); + this.waves = createWaves(appView, callGraph); } static PrimaryMethodProcessor create( AppView<AppInfoWithLiveness> appView, - PostMethodProcessor.Builder postMethodProcessorBuilder, ExecutorService executorService, Timing timing) throws ExecutionException { CallGraph callGraph = CallGraph.builder(appView).build(executorService, timing); - return new PrimaryMethodProcessor(appView, postMethodProcessorBuilder, callGraph); + return new PrimaryMethodProcessor(appView, callGraph); } @Override @@ -84,29 +80,18 @@ return callSiteInformation; } - private Deque<SortedProgramMethodSet> createWaves( - AppView<?> appView, CallGraph callGraph, CallSiteInformation callSiteInformation) { + private Deque<SortedProgramMethodSet> createWaves(AppView<?> appView, CallGraph callGraph) { InternalOptions options = appView.options(); Deque<SortedProgramMethodSet> waves = new ArrayDeque<>(); Set<Node> nodes = callGraph.nodes; - ProgramMethodSet reprocessing = ProgramMethodSet.create(); int waveCount = 1; while (!nodes.isEmpty()) { SortedProgramMethodSet wave = callGraph.extractLeaves(); - wave.forEach( - method -> { - if (callSiteInformation.hasSingleCallSite(method) && options.enableInlining) { - callGraph.cycleEliminationResult.forEachRemovedCaller(method, reprocessing::add); - } - }); waves.addLast(wave); if (Log.ENABLED && Log.isLoggingEnabledFor(PrimaryMethodProcessor.class)) { Log.info(getClass(), "Wave #%d: %d", waveCount++, wave.size()); } } - if (!reprocessing.isEmpty()) { - postMethodProcessorBuilder.put(reprocessing); - } options.testing.waveModifier.accept(waves); return waves; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java index 430d8ad..88fc606 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/D8NestBasedAccessDesugaring.java
@@ -109,22 +109,20 @@ new NestBasedAccessDesugaringUseRegistry(method, eventConsumer))); } - private class NestBasedAccessDesugaringUseRegistry extends UseRegistry { + private class NestBasedAccessDesugaringUseRegistry extends UseRegistry<ClasspathMethod> { private final NestBasedAccessDesugaringEventConsumer eventConsumer; - private final ClasspathMethod context; NestBasedAccessDesugaringUseRegistry( ClasspathMethod context, NestBasedAccessDesugaringEventConsumer eventConsumer) { - super(appView.dexItemFactory()); + super(context, appView.dexItemFactory()); this.eventConsumer = eventConsumer; - this.context = context; } private void registerFieldAccess(DexField reference, boolean isGet) { DexClassAndField field = reference.lookupMemberOnClass(appView.definitionForHolder(reference)); - if (field != null && needsDesugaring(field, context)) { + if (field != null && needsDesugaring(field, getContext())) { ensureFieldAccessBridge(field, isGet, eventConsumer); } } @@ -135,7 +133,7 @@ } DexClassAndMethod method = reference.lookupMemberOnClass(appView.definitionForHolder(reference)); - if (method != null && needsDesugaring(method, context)) { + if (method != null && needsDesugaring(method, getContext())) { ensureMethodBridge(method, eventConsumer); } }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java index f9ca2e9..8f658a3 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -453,10 +453,12 @@ assert method.isClasspathMethod(); return appView .getSyntheticItems() - .createFixedClasspathClass( + .ensureFixedClasspathClass( SyntheticKind.INIT_TYPE_ARGUMENT, method.asClasspathMethod().getHolder(), - dexItemFactory) + appView, + ignored -> {}, + ignored -> {}) .getType(); } });
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java index 0332cce..264eda0 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; @@ -33,9 +32,10 @@ import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.optimize.Inliner.InlineAction; +import com.android.tools.r8.ir.optimize.Inliner.InlineResult; import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason; import com.android.tools.r8.ir.optimize.Inliner.Reason; -import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo; +import com.android.tools.r8.ir.optimize.Inliner.RetryAction; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy; import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; @@ -123,13 +123,6 @@ ProgramMethod singleTarget, Reason reason, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) { - DexEncodedMethod singleTargetMethod = singleTarget.getDefinition(); - MethodOptimizationInfo targetOptimizationInfo = singleTargetMethod.getOptimizationInfo(); - if (targetOptimizationInfo.neverInline()) { - whyAreYouNotInliningReporter.reportMarkedAsNeverInline(); - return false; - } - // Do not inline if the inlinee is greater than the api caller level. // TODO(b/188498051): We should not force inline lower api method calls. if (reason != Reason.FORCE @@ -147,10 +140,10 @@ return false; } - if (method.getDefinition() == singleTargetMethod) { + if (method.isStructurallyEqualTo(singleTarget)) { // Cannot handle recursive inlining at this point. // Force inlined method should never be recursive. - assert !targetOptimizationInfo.forceInline(); + assert !singleTarget.getOptimizationInfo().forceInline(); whyAreYouNotInliningReporter.reportRecursiveMethod(); return false; } @@ -188,20 +181,22 @@ } if (reason == Reason.DUAL_CALLER) { + assert methodProcessor.isPrimaryMethodProcessor() || methodProcessor.isPostMethodProcessor(); if (satisfiesRequirementsForSimpleInlining(invoke, singleTarget)) { // When we have a method with two call sites, we simply inline the method as we normally do // when the method is small. We still need to ensure that the other call site is also // inlined, though. Therefore, we record here that we have seen one of the two call sites // as we normally do. - inliner.recordDoubleInliningCandidate(method, singleTarget); - } else if (inliner.isDoubleInliningEnabled()) { - if (!inliner.satisfiesRequirementsForDoubleInlining(method, singleTarget)) { + inliner.recordDoubleInliningCandidate(method, singleTarget, methodProcessor); + } else if (inliner.isDoubleInliningEnabled(methodProcessor)) { + if (!inliner.satisfiesRequirementsForDoubleInlining( + method, singleTarget, methodProcessor)) { whyAreYouNotInliningReporter.reportInvalidDoubleInliningCandidate(); return false; } } else { // TODO(b/142300882): Should in principle disallow inlining in this case. - inliner.recordDoubleInliningCandidate(method, singleTarget); + inliner.recordDoubleInliningCandidate(method, singleTarget, methodProcessor); } } else if (reason == Reason.SIMPLE && !satisfiesRequirementsForSimpleInlining(invoke, singleTarget)) { @@ -265,7 +260,7 @@ } @Override - public InlineAction computeInlining( + public InlineResult computeInlining( InvokeMethod invoke, SingleResolutionResult resolutionResult, ProgramMethod singleTarget, @@ -288,11 +283,21 @@ return null; } - Reason reason = reasonStrategy.computeInliningReason(invoke, singleTarget, context); + Reason reason = + reasonStrategy.computeInliningReason(invoke, singleTarget, context, methodProcessor); if (reason == Reason.NEVER) { return null; } + if (reason == Reason.SIMPLE + && !singleTarget.getDefinition().isProcessed() + && methodProcessor.isPrimaryMethodProcessor()) { + // The single target has this method as single caller, but the single target is not yet + // processed. Enqueue the context for processing in the secondary optimization pass to allow + // the single caller inlining to happen. + return new RetryAction(); + } + if (!singleTarget .getDefinition() .isInliningCandidate(method, reason, appView.appInfo(), whyAreYouNotInliningReporter)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java index 41b6fe6..0e26fa3 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ForcedInliningOracle.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.ir.code.InvokeDirect; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.optimize.Inliner.InlineAction; +import com.android.tools.r8.ir.optimize.Inliner.InlineResult; import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason; import com.android.tools.r8.ir.optimize.Inliner.Reason; import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; @@ -61,7 +62,7 @@ } @Override - public InlineAction computeInlining( + public InlineResult computeInlining( InvokeMethod invoke, SingleResolutionResult resolutionResult, ProgramMethod singleTarget, @@ -79,12 +80,7 @@ if (info == null) { return null; } - assert method.getDefinition() != info.target.getDefinition(); - // Even though call to Inliner::performForcedInlining is supposed to be controlled by - // the caller, it's still suspicious if we want to force inline something that is marked - // with neverInline() flag. - assert !info.target.getDefinition().getOptimizationInfo().neverInline(); assert passesInliningConstraints( invoke, resolutionResult, info.target, Reason.FORCE, whyAreYouNotInliningReporter); return new InlineAction(info.target, invoke, Reason.FORCE);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index 7591f78..2f61964 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -86,8 +86,11 @@ private final LensCodeRewriter lensCodeRewriter; final MainDexInfo mainDexInfo; + // The set of callers of single caller methods where the single caller method could not be inlined + // due to not being processed at the time of inlining. + private final LongLivedProgramMethodSetBuilder<ProgramMethodSet> singleInlineCallers; + // State for inlining methods which are known to be called twice. - private boolean applyDoubleInlining = false; private LongLivedProgramMethodSetBuilder<ProgramMethodSet> doubleInlineCallers; private final ProgramMethodSet doubleInlineSelectedTargets = ProgramMethodSet.create(); private final Map<DexEncodedMethod, ProgramMethod> doubleInlineeCandidates = @@ -106,6 +109,8 @@ : ImmutableSet.of(intrinsics.throwNpe, intrinsics.throwParameterIsNullException); this.lensCodeRewriter = lensCodeRewriter; this.mainDexInfo = appView.appInfo().getMainDexInfo(); + this.singleInlineCallers = + LongLivedProgramMethodSetBuilder.createConcurrentForIdentitySet(appView.graphLens()); availableApiExceptions = appView.options().canHaveDalvikCatchHandlerVerificationBug() ? new AvailableApiExceptions(appView.options()) @@ -149,8 +154,8 @@ return false; } - boolean isDoubleInliningEnabled() { - return applyDoubleInlining; + boolean isDoubleInliningEnabled(MethodProcessor methodProcessor) { + return methodProcessor.isPostMethodProcessor(); } private ConstraintWithTarget instructionAllowedForInlining( @@ -210,19 +215,20 @@ } synchronized boolean satisfiesRequirementsForDoubleInlining( - ProgramMethod method, ProgramMethod target) { - if (applyDoubleInlining) { + ProgramMethod method, ProgramMethod target, MethodProcessor methodProcessor) { + if (isDoubleInliningEnabled(methodProcessor)) { // Don't perform the actual inlining if this was not selected. return doubleInlineSelectedTargets.contains(target); } // Just preparing for double inlining. - recordDoubleInliningCandidate(method, target); + recordDoubleInliningCandidate(method, target, methodProcessor); return false; } - synchronized void recordDoubleInliningCandidate(ProgramMethod method, ProgramMethod target) { - if (applyDoubleInlining) { + synchronized void recordDoubleInliningCandidate( + ProgramMethod method, ProgramMethod target, MethodProcessor methodProcessor) { + if (isDoubleInliningEnabled(methodProcessor)) { return; } @@ -250,9 +256,23 @@ // The double inline callers are always rewritten up until the graph lens of the primary // optimization pass, so we can safely merge them into the methods to reprocess (which may be // rewritten with a newer graph lens). - postMethodProcessorBuilder.getMethodsToReprocessBuilder().merge(doubleInlineCallers); + postMethodProcessorBuilder + .getMethodsToReprocessBuilder() + .rewrittenWithLens(appView) + .merge( + doubleInlineCallers + .rewrittenWithLens(appView) + .removeIf( + appView, + method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite())) + .merge( + singleInlineCallers + .rewrittenWithLens(appView) + .removeIf( + appView, + method -> method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite())); doubleInlineCallers = null; - applyDoubleInlining = true; + singleInlineCallers.clear(); } /** @@ -571,7 +591,18 @@ } } - public static class InlineAction { + public abstract static class InlineResult { + + InlineAction asInlineAction() { + return null; + } + + boolean isRetryAction() { + return false; + } + } + + public static class InlineAction extends InlineResult { public final ProgramMethod target; public final Invoke invoke; @@ -586,6 +617,11 @@ this.reason = reason; } + @Override + InlineAction asInlineAction() { + return this; + } + void setShouldSynthesizeInitClass() { assert !shouldSynthesizeNullCheckForReceiver; shouldSynthesizeInitClass = true; @@ -799,6 +835,14 @@ } } + public static class RetryAction extends InlineResult { + + @Override + boolean isRetryAction() { + return true; + } + } + static class InlineeWithReason { final Reason reason; @@ -1004,7 +1048,7 @@ oracle.isForcedInliningOracle() ? NopWhyAreYouNotInliningReporter.getInstance() : WhyAreYouNotInliningReporter.createFor(singleTarget, appView, context); - InlineAction action = + InlineResult inlineResult = oracle.computeInlining( invoke, resolutionResult, @@ -1012,11 +1056,18 @@ context, classInitializationAnalysis, whyAreYouNotInliningReporter); - if (action == null) { + if (inlineResult == null) { assert whyAreYouNotInliningReporter.unsetReasonHasBeenReportedFlag(); continue; } + if (inlineResult.isRetryAction()) { + enqueueMethodForReprocessing(context); + continue; + } + + InlineAction action = inlineResult.asInlineAction(); + DexProgramClass downcastClass = getDowncastTypeIfNeeded(strategy, invoke, singleTarget); if (downcastClass != null && AccessControl.isClassAccessible(downcastClass, context, appView) @@ -1240,6 +1291,14 @@ assert IteratorUtils.peekNext(blockIterator) == firstInlineeBlock; } + public void enqueueMethodForReprocessing(ProgramMethod method) { + singleInlineCallers.add(method, appView.graphLens()); + } + + public void pruneMethod(ProgramMethod method) { + singleInlineCallers.remove(method.getReference(), appView.graphLens()); + } + public static boolean verifyNoMethodsInlinedDueToSingleCallSite(AppView<?> appView) { for (DexProgramClass clazz : appView.appInfo().classes()) { for (DexEncodedMethod method : clazz.methods()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java index 1e5c3ae..f839991 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningOracle.java
@@ -8,7 +8,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.ClassInitializationAnalysis; import com.android.tools.r8.ir.code.InvokeMethod; -import com.android.tools.r8.ir.optimize.Inliner.InlineAction; +import com.android.tools.r8.ir.optimize.Inliner.InlineResult; import com.android.tools.r8.ir.optimize.Inliner.Reason; import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; @@ -29,7 +29,7 @@ Reason reason, WhyAreYouNotInliningReporter whyAreYouNotInliningReporter); - InlineAction computeInlining( + InlineResult computeInlining( InvokeMethod invoke, SingleResolutionResult resolutionResult, ProgramMethod singleTarget,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java index e4229c2..26430a4 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -25,14 +25,11 @@ import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT; import static com.android.tools.r8.utils.MapUtils.ignoreKey; -import com.android.tools.r8.graph.AccessFlags; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClassAndField; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.graph.DexEncodedMember; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; @@ -43,9 +40,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.FieldResolutionResult; import com.android.tools.r8.graph.GraphLens; -import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues; import com.android.tools.r8.ir.analysis.type.ArrayTypeElement; @@ -78,7 +73,6 @@ import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions; import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.conversion.PostMethodProcessor.Builder; -import com.android.tools.r8.ir.optimize.Inliner.Constraint; import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData; import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData; import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldMappingData; @@ -132,7 +126,6 @@ import java.util.concurrent.ExecutorService; import java.util.function.Consumer; import java.util.function.Function; -import java.util.function.Predicate; public class EnumUnboxerImpl extends EnumUnboxer { @@ -919,203 +912,6 @@ } } - private class EnumAccessibilityUseRegistry extends UseRegistry { - - private ProgramMethod context; - private Constraint constraint; - - public EnumAccessibilityUseRegistry(DexItemFactory factory) { - super(factory); - } - - public Constraint computeConstraint(ProgramMethod method) { - constraint = Constraint.ALWAYS; - context = method; - method.registerCodeReferences(this); - return constraint; - } - - public Constraint deriveConstraint(DexType targetHolder, AccessFlags<?> flags) { - DexProgramClass contextHolder = context.getHolder(); - if (targetHolder == contextHolder.type) { - return Constraint.ALWAYS; - } - if (flags.isPublic()) { - return Constraint.ALWAYS; - } - if (flags.isPrivate()) { - // Enum unboxing is currently happening only cf to dex, and no class should be in a nest - // at this point. If that is the case, we just don't unbox the enum, or we would need to - // support Constraint.SAMENEST in the enum unboxer. - assert !contextHolder.isInANest(); - // Only accesses within the enum are allowed since all enum methods and fields will be - // moved to the same class, and the enum itself becomes an integer, which is - // accessible everywhere. - return Constraint.NEVER; - } - assert flags.isProtected() || flags.isPackagePrivate(); - // Protected is in practice equivalent to package private in this analysis since we are - // accessing the member from an enum context where subclassing is limited. - // At this point we don't support unboxing enums with subclasses, so we assume either - // same package access, or we just don't unbox. - // The only protected methods in java.lang.Enum are clone, finalize and the constructor. - // Besides calls to the constructor in the instance initializer, Enums with calls to such - // methods cannot be unboxed. - return targetHolder.isSamePackage(contextHolder.type) ? Constraint.PACKAGE : Constraint.NEVER; - } - - @Override - public void registerTypeReference(DexType type) { - if (type.isArrayType()) { - registerTypeReference(type.toBaseType(factory)); - return; - } - - if (type.isPrimitiveType()) { - return; - } - - DexClass definition = appView.definitionFor(type); - if (definition == null) { - constraint = Constraint.NEVER; - return; - } - constraint = constraint.meet(deriveConstraint(type, definition.accessFlags)); - } - - @Override - public void registerInitClass(DexType type) { - registerTypeReference(type); - } - - @Override - public void registerInstanceOf(DexType type) { - registerTypeReference(type); - } - - @Override - public void registerNewInstance(DexType type) { - registerTypeReference(type); - } - - @Override - public void registerInvokeVirtual(DexMethod method) { - registerVirtualInvoke(method, false); - } - - @Override - public void registerInvokeInterface(DexMethod method) { - registerVirtualInvoke(method, true); - } - - private void registerVirtualInvoke(DexMethod method, boolean isInterface) { - if (method.holder.isArrayType()) { - return; - } - // Perform resolution and derive unboxing constraints based on the accessibility of the - // resolution result. - MethodResolutionResult resolutionResult = - appView.appInfo().resolveMethod(method, isInterface); - if (!resolutionResult.isVirtualTarget()) { - constraint = Constraint.NEVER; - return; - } - registerTarget( - resolutionResult.getInitialResolutionHolder(), resolutionResult.getSingleTarget()); - } - - private void registerTarget(DexClass initialResolutionHolder, DexEncodedMember<?, ?> target) { - if (target == null) { - // This will fail at runtime. - constraint = Constraint.NEVER; - return; - } - DexType resolvedHolder = target.getHolderType(); - if (initialResolutionHolder == null) { - constraint = Constraint.NEVER; - return; - } - Constraint memberConstraint = deriveConstraint(resolvedHolder, target.getAccessFlags()); - // We also have to take the constraint of the initial resolution holder into account. - Constraint classConstraint = - deriveConstraint(initialResolutionHolder.type, initialResolutionHolder.accessFlags); - Constraint instructionConstraint = memberConstraint.meet(classConstraint); - constraint = instructionConstraint.meet(constraint); - } - - @Override - public void registerInvokeDirect(DexMethod method) { - registerSingleTargetInvoke(method, DexEncodedMethod::isDirectMethod); - } - - @Override - public void registerInvokeStatic(DexMethod method) { - registerSingleTargetInvoke(method, DexEncodedMethod::isStatic); - } - - private void registerSingleTargetInvoke( - DexMethod method, Predicate<DexEncodedMethod> methodValidator) { - if (method.holder.isArrayType()) { - return; - } - MethodResolutionResult resolutionResult = - appView.appInfo().unsafeResolveMethodDueToDexFormat(method); - DexEncodedMethod target = resolutionResult.getSingleTarget(); - if (target == null || !methodValidator.test(target)) { - constraint = Constraint.NEVER; - return; - } - registerTarget(resolutionResult.getInitialResolutionHolder(), target); - } - - @Override - public void registerInvokeSuper(DexMethod method) { - // Invoke-super can only target java.lang.Enum methods since we do not unbox enums with - // subclasses. Calls to java.lang.Object methods would have resulted in the enum to be marked - // as unboxable. The methods of java.lang.Enum called are already analyzed in the enum - // unboxer analysis, so invoke-super is always valid. - assert method.holder == factory.enumType; - } - - @Override - public void registerCallSite(DexCallSite callSite) { - // This is reached after lambda desugaring, so this should not be a lambda call site. - // We do not unbox enums with invoke custom since it's not clear the accessibility - // constraints would be correct if the method holding the invoke custom is moved to - // another class. - assert appView.options().isGeneratingClassFiles() - || !factory.isLambdaMetafactoryMethod(callSite.bootstrapMethod.asMethod()); - constraint = Constraint.NEVER; - } - - private void registerFieldInstruction(DexField field) { - FieldResolutionResult fieldResolutionResult = appView.appInfo().resolveField(field, context); - registerTarget( - fieldResolutionResult.getInitialResolutionHolder(), - fieldResolutionResult.getResolvedField()); - } - - @Override - public void registerInstanceFieldRead(DexField field) { - registerFieldInstruction(field); - } - - @Override - public void registerInstanceFieldWrite(DexField field) { - registerFieldInstruction(field); - } - - @Override - public void registerStaticFieldRead(DexField field) { - registerFieldInstruction(field); - } - - @Override - public void registerStaticFieldWrite(DexField field) { - registerFieldInstruction(field); - } - } - private void analyzeInitializers() { enumUnboxingCandidatesInfo.forEachCandidate( enumClass -> {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java index c085286..24d3ad4 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -165,11 +165,6 @@ } @Override - public boolean neverInline() { - return false; - } - - @Override public boolean checksNullReceiverBeforeAnySideEffect() { return UNKNOWN_CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java index aff19ef..08ef5d9 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -92,8 +92,6 @@ public abstract boolean forceInline(); - public abstract boolean neverInline(); - public abstract boolean checksNullReceiverBeforeAnySideEffect(); public abstract boolean triggersClassInitBeforeAnySideEffect();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java index e28b6ad..f93dc2a 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -137,7 +137,7 @@ Timing timing) { DexEncodedMethod definition = method.getDefinition(); identifyBridgeInfo(definition, code, feedback, timing); - analyzeReturns(code, feedback, timing); + analyzeReturns(code, feedback, methodProcessor, timing); if (options.enableInlining) { identifyInvokeSemanticsForInlining(definition, code, feedback, timing); } @@ -166,13 +166,15 @@ timing.end(); } - private void analyzeReturns(IRCode code, OptimizationFeedback feedback, Timing timing) { + private void analyzeReturns( + IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor, Timing timing) { timing.begin("Identify returns argument"); - analyzeReturns(code, feedback); + analyzeReturns(code, feedback, methodProcessor); timing.end(); } - private void analyzeReturns(IRCode code, OptimizationFeedback feedback) { + private void analyzeReturns( + IRCode code, OptimizationFeedback feedback, MethodProcessor methodProcessor) { ProgramMethod context = code.context(); DexEncodedMethod method = context.getDefinition(); List<BasicBlock> normalExits = code.computeNormalExitBlocks(); @@ -204,7 +206,7 @@ feedback.methodReturnsAbstractValue(method, appView, abstractReturnValue); if (checkCastAndInstanceOfMethodSpecialization != null) { checkCastAndInstanceOfMethodSpecialization.addCandidateForOptimization( - context, abstractReturnValue); + context, abstractReturnValue, methodProcessor); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java index dd6f917..9f1ea2d 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -361,6 +361,10 @@ return isFlagSet(HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG); } + void unsetInlinedIntoSingleCallSite() { + clearFlag(HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG); + } + void markInlinedIntoSingleCallSite() { setFlag(HAS_BEEN_INLINED_INTO_SINGLE_CALL_SITE_FLAG); } @@ -456,11 +460,6 @@ } @Override - public boolean neverInline() { - return inlining == InlinePreference.NeverInline; - } - - @Override public boolean checksNullReceiverBeforeAnySideEffect() { return isFlagSet(CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG); } @@ -596,13 +595,6 @@ inlining = InlinePreference.Default; } - // TODO(b/140214568): Should be package-private. - public void markNeverInline() { - // For concurrent scenarios we should allow the flag to be already set - assert inlining == InlinePreference.Default || inlining == InlinePreference.NeverInline; - inlining = InlinePreference.NeverInline; - } - void markCheckNullReceiverBeforeAnySideEffect(boolean mark) { setFlag(CHECKS_NULL_RECEIVER_BEFORE_ANY_SIDE_EFFECT_FLAG, mark); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java index 472a65b..1b4acdd 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -199,7 +199,7 @@ } @Override - public synchronized void unsetAbstractReturnValue(DexEncodedMethod method) { + public synchronized void unsetAbstractReturnValue(ProgramMethod method) { getMethodOptimizationInfoForUpdating(method).unsetAbstractReturnValue(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java index 366a1ac..8b13771 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackIgnore.java
@@ -79,7 +79,7 @@ DexEncodedMethod method, AppView<AppInfoWithLiveness> appView, AbstractValue value) {} @Override - public void unsetAbstractReturnValue(DexEncodedMethod method) {} + public void unsetAbstractReturnValue(ProgramMethod method) {} @Override public void methodReturnsObjectWithUpperBoundType(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java index f33bf52..86ea927 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -107,8 +107,10 @@ } @Override - public void unsetAbstractReturnValue(DexEncodedMethod method) { - method.getMutableOptimizationInfo().unsetAbstractReturnValue(); + public void unsetAbstractReturnValue(ProgramMethod method) { + if (method.getOptimizationInfo().isMutableOptimizationInfo()) { + method.getDefinition().getMutableOptimizationInfo().unsetAbstractReturnValue(); + } } @Override @@ -242,4 +244,13 @@ public void setUnusedArguments(ProgramMethod method, BitSet unusedArguments) { method.getDefinition().getMutableOptimizationInfo().setUnusedArguments(unusedArguments); } + + public void unsetInlinedIntoSingleCallSite(ProgramMethod method) { + if (method.getOptimizationInfo().isMutableOptimizationInfo()) { + method + .getOptimizationInfo() + .asMutableMethodOptimizationInfo() + .unsetInlinedIntoSingleCallSite(); + } + } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java index c6b0aa4..43b302b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.conversion.CallSiteInformation; +import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.optimize.Inliner; import com.android.tools.r8.ir.optimize.Inliner.Reason; import com.android.tools.r8.shaking.AppInfoWithLiveness; @@ -31,7 +32,10 @@ @Override public Reason computeInliningReason( - InvokeMethod invoke, ProgramMethod target, ProgramMethod context) { + InvokeMethod invoke, + ProgramMethod target, + ProgramMethod context, + MethodProcessor methodProcessor) { DexEncodedMethod targetMethod = target.getDefinition(); DexMethod targetReference = target.getReference(); if (targetMethod.getOptimizationInfo().forceInline()) { @@ -52,7 +56,7 @@ if (isSingleCallerInliningTarget(target)) { return Reason.SINGLE_CALLER; } - if (isDoubleInliningTarget(target)) { + if (isDoubleInliningTarget(target, methodProcessor)) { return Reason.DUAL_CALLER; } return Reason.SIMPLE; @@ -72,11 +76,13 @@ return true; } - private boolean isDoubleInliningTarget(ProgramMethod candidate) { - // 10 is found from measuring. - if (callSiteInformation.hasDoubleCallSite(candidate) - || inliner.isDoubleInlineSelectedTarget(candidate)) { - return candidate.getDefinition().getCode().estimatedSizeForInliningAtMost(10); + private boolean isDoubleInliningTarget(ProgramMethod candidate, MethodProcessor methodProcessor) { + if (methodProcessor.isPrimaryMethodProcessor() || methodProcessor.isPostMethodProcessor()) { + if (callSiteInformation.hasDoubleCallSite(candidate) + || inliner.isDoubleInlineSelectedTarget(candidate)) { + // 10 is found from measuring. + return candidate.getDefinition().getCode().estimatedSizeForInliningAtMost(10); + } } return false; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java index ae23a74..aed242b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/FixedInliningReasonStrategy.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.optimize.Inliner.Reason; public class FixedInliningReasonStrategy implements InliningReasonStrategy { @@ -18,7 +19,10 @@ @Override public Reason computeInliningReason( - InvokeMethod invoke, ProgramMethod target, ProgramMethod context) { + InvokeMethod invoke, + ProgramMethod target, + ProgramMethod context, + MethodProcessor methodProcessor) { return reason; } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java index f2ae52a..00ff53c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningReasonStrategy.java
@@ -6,9 +6,14 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.optimize.Inliner.Reason; public interface InliningReasonStrategy { - Reason computeInliningReason(InvokeMethod invoke, ProgramMethod target, ProgramMethod context); + Reason computeInliningReason( + InvokeMethod invoke, + ProgramMethod target, + ProgramMethod context, + MethodProcessor methodProcessor); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java index 0fbb615..dd67717 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizer.java
@@ -385,7 +385,8 @@ if (instruction.isInvokeCustom()) { // Just invalidate any candidates referenced from non-static context. - CallSiteReferencesInvalidator invalidator = new CallSiteReferencesInvalidator(factory); + CallSiteReferencesInvalidator invalidator = + new CallSiteReferencesInvalidator(context, factory); invalidator.registerCallSite(instruction.asInvokeCustom().getCallSite()); continue; } @@ -726,10 +727,10 @@ new StaticizingProcessor(appView, this, converter).run(feedback, executorService); } - private class CallSiteReferencesInvalidator extends UseRegistry { + private class CallSiteReferencesInvalidator extends UseRegistry<ProgramMethod> { - private CallSiteReferencesInvalidator(DexItemFactory factory) { - super(factory); + private CallSiteReferencesInvalidator(ProgramMethod context, DexItemFactory factory) { + super(context, factory); } private void registerMethod(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java index 906ac7f..18a964f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -15,10 +15,10 @@ import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.conversion.IRConverter; +import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.Action; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.android.tools.r8.utils.collections.SortedProgramMethodSet; @@ -34,7 +34,7 @@ * * <p>TODO(b/151596599): Also handle methods that implement casts. */ -public class CheckCastAndInstanceOfMethodSpecialization implements Action { +public class CheckCastAndInstanceOfMethodSpecialization { private static final OptimizationFeedbackSimple feedback = OptimizationFeedbackSimple.getInstance(); @@ -52,14 +52,16 @@ this.converter = converter; } - public void addCandidateForOptimization(ProgramMethod method, AbstractValue abstractReturnValue) { + public void addCandidateForOptimization( + ProgramMethod method, AbstractValue abstractReturnValue, MethodProcessor methodProcessor) { if (!converter.isInWave()) { return; } + assert methodProcessor.isPrimaryMethodProcessor(); if (isCandidateForInstanceOfOptimization(method, abstractReturnValue)) { synchronized (this) { if (candidatesForInstanceOfOptimization.isEmpty()) { - converter.addWaveDoneAction(this); + converter.addWaveDoneAction(() -> execute(methodProcessor)); } candidatesForInstanceOfOptimization.add(method); } @@ -72,18 +74,18 @@ && abstractReturnValue.isSingleBoolean(); } - @Override - public void execute() { + public void execute(MethodProcessor methodProcessor) { assert !candidatesForInstanceOfOptimization.isEmpty(); ProgramMethodSet processed = ProgramMethodSet.create(); for (ProgramMethod method : candidatesForInstanceOfOptimization) { if (!processed.contains(method)) { - processCandidateForInstanceOfOptimization(method); + processCandidateForInstanceOfOptimization(method, methodProcessor); } } } - private void processCandidateForInstanceOfOptimization(ProgramMethod method) { + private void processCandidateForInstanceOfOptimization( + ProgramMethod method, MethodProcessor methodProcessor) { DexEncodedMethod definition = method.getDefinition(); if (!definition.isNonPrivateVirtualMethod()) { return; @@ -140,16 +142,27 @@ parentMethodDefinition.getCode().buildIR(parentMethod, appView, parentMethod.getOrigin()); converter.markProcessed(code, feedback); // Fixup method optimization info (the method no longer returns a constant). - feedback.unsetAbstractReturnValue(parentMethod.getDefinition()); + feedback.unsetAbstractReturnValue(parentMethod); feedback.unsetClassInlinerMethodConstraint(parentMethod); } else { return; } - method.getHolder().removeMethod(method.getReference()); - appView.withArgumentPropagator( argumentPropagator -> argumentPropagator.transferArgumentInformation(method, parentMethod)); + + // Each call to the override is now a call to the parent method, so it is no longer true that + // parent method has been inlined into its single call site. + feedback.unsetInlinedIntoSingleCallSite(parentMethod); + + // For the same reason, we no longer have single or dual caller information for the parent + // method. + methodProcessor.getCallSiteInformation().unsetCallSiteInformation(parentMethod); + + // Remove the method and notify other optimizations that the override has been removed to allow + // the optimizations to fixup their state. + method.getHolder().removeMethod(method.getReference()); + converter.pruneMethod(method); } private ProgramMethod resolveOnSuperClass(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java index 2c1b9a3..5c73aee 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
@@ -30,7 +30,7 @@ public abstract SourceCodeProvider getSourceCodeProvider(); - public abstract Consumer<UseRegistry> getRegistryCallback(); + public abstract Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method); @Override public boolean isEmptyVoidMethod() { @@ -78,7 +78,7 @@ } private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry registry) { - getRegistryCallback().accept(registry); + getRegistryCallback(method).accept(registry); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java index 6bd2080..c06c056 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/SynthesizedCode.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.synthetic; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.UseRegistry; import java.util.function.Consumer; @@ -21,5 +22,5 @@ } @Override - public abstract Consumer<UseRegistry> getRegistryCallback(); + public abstract Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method); }
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java index 85e7b1a..362f5f5 100644 --- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java +++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.naming; import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.MapIdEnvironment; +import com.android.tools.r8.MapIdProvider; import com.android.tools.r8.StringConsumer; import com.android.tools.r8.Version; import com.android.tools.r8.errors.Unreachable; @@ -33,17 +35,19 @@ // Hash of the Proguard map (excluding the header up to and including the hash marker). public static class ProguardMapId { + private final String id; private final String hash; - private ProguardMapId(String id) { + private ProguardMapId(String id, String hash) { assert id != null; - this.hash = id; + assert hash != null; + this.id = id; + this.hash = hash; } - /** Truncated prefix of the content hash. */ - // TODO(b/201269335): Update this to be a full "source-file marker". + /** Id for the map file (user defined or a truncated prefix of the content hash). */ public String getId() { - return hash.substring(0, PG_MAP_ID_LENGTH); + return id; } /** The actual content hash. */ @@ -84,7 +88,7 @@ private ProguardMapId computeProguardMapId() { ProguardMapIdBuilder builder = new ProguardMapIdBuilder(); classNameMapper.write(builder); - return builder.build(); + return builder.build(options.mapIdProvider); } private void writeBody() { @@ -138,14 +142,31 @@ private final Hasher hasher = Hashing.sha256().newHasher(); + private MapIdProvider getProviderOrDefault(MapIdProvider provider) { + return provider != null + ? provider + : environment -> environment.getMapHash().substring(0, PG_MAP_ID_LENGTH); + } + + private MapIdEnvironment getEnvironment(String hash) { + return new MapIdEnvironment() { + @Override + public String getMapHash() { + return hash; + } + }; + } + @Override public ProguardMapIdBuilder accept(String string) { hasher.putString(string, StandardCharsets.UTF_8); return this; } - public ProguardMapId build() { - return new ProguardMapId(hasher.hash().toString()); + public ProguardMapId build(MapIdProvider mapIdProvider) { + String hash = hasher.hash().toString(); + String id = getProviderOrDefault(mapIdProvider).get(getEnvironment(hash)); + return new ProguardMapId(id, hash); } }
diff --git a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java index b8ddc06..da906a8 100644 --- a/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java +++ b/src/main/java/com/android/tools/r8/naming/SourceFileRewriter.java
@@ -4,15 +4,10 @@ package com.android.tools.r8.naming; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexDebugEvent; -import com.android.tools.r8.graph.DexDebugEvent.SetFile; -import com.android.tools.r8.graph.DexDebugInfo; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.shaking.ProguardConfiguration; -import java.util.Arrays; /** * Visit program {@link DexClass}es and replace their sourceFile with the given string. @@ -30,54 +25,24 @@ } public void run() { + boolean isMinifying = appView.options().isMinifying(); ProguardConfiguration proguardConfiguration = appView.options().getProguardConfiguration(); - String renameSourceFile = proguardConfiguration.getRenameSourceFileAttribute(); - boolean hasRenameSourceFileAttribute = renameSourceFile != null; - // Return early if a user wants to keep the current source file attribute as-is. - if (!hasRenameSourceFileAttribute - && proguardConfiguration.getKeepAttributes().sourceFile - && appView.options().forceProguardCompatibility) { + boolean hasKeptNonRenamedSourceFile = + proguardConfiguration.getRenameSourceFileAttribute() == null + && proguardConfiguration.getKeepAttributes().sourceFile; + // If source file is kept without a rewrite, it is only modified it in a minifing full-mode. + if (hasKeptNonRenamedSourceFile + && (!isMinifying || appView.options().forceProguardCompatibility)) { return; } - boolean isMinifying = appView.options().isMinifying(); assert !isMinifying || appView.appInfo().hasLiveness(); - // Now, the user wants either to remove source file attribute or to rename it for non-kept - // classes. DexString defaultRenaming = getSourceFileRenaming(proguardConfiguration); for (DexClass clazz : application.classes()) { - // We only parse sourceFile if -keepattributes SourceFile, but for compat we should add - // a source file name, otherwise line positions will not be printed on the JVM or old version - // of ART. - if (!hasRenameSourceFileAttribute - && proguardConfiguration.getKeepAttributes().sourceFile - && !(isMinifying && appView.withLiveness().appInfo().isMinificationAllowed(clazz.type))) { + if (hasKeptNonRenamedSourceFile + && !appView.withLiveness().appInfo().isMinificationAllowed(clazz.type)) { continue; } clazz.sourceFile = defaultRenaming; - clazz.forEachMethod(encodedMethod -> { - // Abstract methods do not have code_item. - if (encodedMethod.shouldNotHaveCode()) { - return; - } - Code code = encodedMethod.getCode(); - if (code == null) { - return; - } - if (code.isDexCode()) { - DexDebugInfo dexDebugInfo = code.asDexCode().getDebugInfo(); - if (dexDebugInfo == null) { - return; - } - // Thanks to a single global source file, we can safely remove DBG_SET_FILE entirely. - dexDebugInfo.events = - Arrays.stream(dexDebugInfo.events) - .filter(dexDebugEvent -> !(dexDebugEvent instanceof SetFile)) - .toArray(DexDebugEvent[]::new); - } else { - assert code.isCfCode(); - // CF has nothing equivalent to SetFile, so there is nothing to remove. - } - }); } }
diff --git a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java index fadc223..9fcd27a 100644 --- a/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java +++ b/src/main/java/com/android/tools/r8/optimize/InvokeSingleTargetExtractor.java
@@ -7,14 +7,16 @@ 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.graph.UseRegistry; -public class InvokeSingleTargetExtractor extends UseRegistry { +public class InvokeSingleTargetExtractor extends UseRegistry<ProgramMethod> { + private InvokeKind kind = InvokeKind.NONE; private DexMethod target; - public InvokeSingleTargetExtractor(DexItemFactory factory) { - super(factory); + public InvokeSingleTargetExtractor(ProgramMethod context, DexItemFactory factory) { + super(context, factory); } private void setTarget(DexMethod target, InvokeKind kind) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java index 2b9252f..5dedc07 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -397,7 +397,7 @@ method -> { if (method.getDefinition().hasCode()) { method.registerCodeReferences( - new UseRegistry(appView.dexItemFactory()) { + new UseRegistry<ProgramMethod>(method, appView.dexItemFactory()) { @Override public void registerInstanceFieldRead(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java index 9d9a731..a851131 100644 --- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java +++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLensFactory.java
@@ -101,10 +101,9 @@ executorService); } - private static class NonReboundMemberReferencesRegistry extends UseRegistry { + private static class NonReboundMemberReferencesRegistry extends UseRegistry<ProgramMethod> { private final AppInfoWithClassHierarchy appInfo; - private final ProgramMethod context; private final FieldAccessInfoCollectionImpl fieldAccessInfoCollection; private final MethodAccessInfoCollection.ConcurrentBuilder methodAccessInfoCollectionBuilder; private final Set<DexField> seenFieldReferences; @@ -117,9 +116,8 @@ MethodAccessInfoCollection.ConcurrentBuilder methodAccessInfoCollectionBuilder, Set<DexField> seenFieldReferences, Set<DexMethod> seenMethodReferences) { - super(appView.dexItemFactory()); + super(context, appView.dexItemFactory()); this.appInfo = appView.appInfo(); - this.context = context; this.fieldAccessInfoCollection = fieldAccessInfoCollection; this.methodAccessInfoCollectionBuilder = methodAccessInfoCollectionBuilder; this.seenFieldReferences = seenFieldReferences; @@ -209,7 +207,7 @@ if (method.getHolderType().isArrayType()) { return; } - DexClass holder = appInfo.definitionFor(method.getHolderType(), context); + DexClass holder = appInfo.definitionFor(method.getHolderType(), getContext()); if (holder == null) { return; }
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java index a990959..43220e6 100644 --- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java +++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -40,7 +40,7 @@ return false; } InvokeSingleTargetExtractor targetExtractor = - new InvokeSingleTargetExtractor(appView.dexItemFactory()); + new InvokeSingleTargetExtractor(method, appView.dexItemFactory()); method.registerCodeReferences(targetExtractor); DexMethod target = targetExtractor.getTarget(); // javac-generated visibility forward bridge method has same descriptor (name, signature and
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java index 645bac0..080e691 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.optimize.argumentpropagation; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethodSignature; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo; import com.android.tools.r8.graph.ProgramMethod; @@ -20,11 +21,15 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; +import com.android.tools.r8.utils.collections.DexMethodSignatureSet; import com.google.common.collect.Sets; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.function.BiConsumer; /** Optimization that propagates information about arguments from call sites to method entries. */ public class ArgumentPropagator { @@ -144,14 +149,26 @@ timing.end(); // Set the optimization info on each method. + Map<Set<DexProgramClass>, DexMethodSignatureSet> interfaceDispatchOutsideProgram = + new IdentityHashMap<>(); populateParameterOptimizationInfo( - immediateSubtypingInfo, stronglyConnectedProgramComponents, executorService, timing); + immediateSubtypingInfo, + stronglyConnectedProgramComponents, + (stronglyConnectedProgramComponent, signature) -> { + interfaceDispatchOutsideProgram + .computeIfAbsent( + stronglyConnectedProgramComponent, (unused) -> DexMethodSignatureSet.create()) + .add(signature); + }, + executorService, + timing); // Using the computed optimization info, build a graph lens that describes the mapping from // methods with constant parameters to methods with the constant parameters removed. Set<DexProgramClass> affectedClasses = Sets.newConcurrentHashSet(); ArgumentPropagatorGraphLens graphLens = - new ArgumentPropagatorProgramOptimizer(appView, immediateSubtypingInfo) + new ArgumentPropagatorProgramOptimizer( + appView, immediateSubtypingInfo, interfaceDispatchOutsideProgram) .run(stronglyConnectedProgramComponents, affectedClasses::add, executorService, timing); // Find all the code objects that need reprocessing. @@ -174,6 +191,7 @@ private void populateParameterOptimizationInfo( ImmediateProgramSubtypingInfo immediateSubtypingInfo, List<Set<DexProgramClass>> stronglyConnectedProgramComponents, + BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram, ExecutorService executorService, Timing timing) throws ExecutionException { @@ -189,7 +207,8 @@ immediateSubtypingInfo, codeScannerResult, reprocessingCriteriaCollection, - stronglyConnectedProgramComponents) + stronglyConnectedProgramComponents, + interfaceDispatchOutsideProgram) .populateOptimizationInfo(executorService, timing); reprocessingCriteriaCollection = null; timing.end();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java index eff9893..30bab53 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -110,7 +110,7 @@ methodsToReprocessInClass.add(method); } else { AffectedMethodUseRegistry registry = - new AffectedMethodUseRegistry(appView, graphLens); + new AffectedMethodUseRegistry(appView, method, graphLens); if (method.registerCodeReferencesWithResult(registry)) { methodsToReprocessInClass.add(method); } @@ -125,14 +125,16 @@ methodsToReprocessBuilder.addAll(methodsToReprocessForClass, currentGraphLens)); } - static class AffectedMethodUseRegistry extends UseRegistryWithResult<Boolean> { + static class AffectedMethodUseRegistry extends UseRegistryWithResult<Boolean, ProgramMethod> { private final AppView<AppInfoWithLiveness> appView; private final ArgumentPropagatorGraphLens graphLens; AffectedMethodUseRegistry( - AppView<AppInfoWithLiveness> appView, ArgumentPropagatorGraphLens graphLens) { - super(appView.dexItemFactory(), false); + AppView<AppInfoWithLiveness> appView, + ProgramMethod context, + ArgumentPropagatorGraphLens graphLens) { + super(context, appView.dexItemFactory(), false); this.appView = appView; this.graphLens = graphLens; }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java index 7118043..5470ed5 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexMethodSignature; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo; @@ -42,6 +43,7 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.function.BiConsumer; import java.util.stream.IntStream; /** @@ -58,17 +60,22 @@ private final ImmediateProgramSubtypingInfo immediateSubtypingInfo; private final List<Set<DexProgramClass>> stronglyConnectedProgramComponents; + private final BiConsumer<Set<DexProgramClass>, DexMethodSignature> + interfaceDispatchOutsideProgram; + ArgumentPropagatorOptimizationInfoPopulator( AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo, MethodStateCollectionByReference methodStates, ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection, - List<Set<DexProgramClass>> stronglyConnectedProgramComponents) { + List<Set<DexProgramClass>> stronglyConnectedProgramComponents, + BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram) { this.appView = appView; this.immediateSubtypingInfo = immediateSubtypingInfo; this.methodStates = methodStates; this.reprocessingCriteriaCollection = reprocessingCriteriaCollection; this.stronglyConnectedProgramComponents = stronglyConnectedProgramComponents; + this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram; } /** @@ -114,7 +121,12 @@ // // To handle this we first propagate any argument information stored for I.m() to A.m() by doing // a top-down traversal over the interfaces in the strongly connected component. - new InterfaceMethodArgumentPropagator(appView, immediateSubtypingInfo, methodStates) + new InterfaceMethodArgumentPropagator( + appView, + immediateSubtypingInfo, + methodStates, + signature -> + interfaceDispatchOutsideProgram.accept(stronglyConnectedComponent, signature)) .run(stronglyConnectedComponent); // Now all the argument information for a given method is guaranteed to be stored on a supertype
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java index 57611a3..51120e1 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -100,14 +100,18 @@ private final AppView<AppInfoWithLiveness> appView; private final ImmediateProgramSubtypingInfo immediateSubtypingInfo; + private final Map<Set<DexProgramClass>, DexMethodSignatureSet> interfaceDispatchOutsideProgram; private final Map<DexClass, DexMethodSignatureSet> libraryVirtualMethods = new ConcurrentHashMap<>(); public ArgumentPropagatorProgramOptimizer( - AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) { + AppView<AppInfoWithLiveness> appView, + ImmediateProgramSubtypingInfo immediateSubtypingInfo, + Map<Set<DexProgramClass>, DexMethodSignatureSet> interfaceDispatchOutsideProgram) { this.appView = appView; this.immediateSubtypingInfo = immediateSubtypingInfo; + this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram; } public ArgumentPropagatorGraphLens run( @@ -121,7 +125,12 @@ ThreadUtils.processItemsWithResults( stronglyConnectedProgramComponents, classes -> - new StronglyConnectedComponentOptimizer().optimize(classes, affectedClassConsumer), + new StronglyConnectedComponentOptimizer() + .optimize( + classes, + interfaceDispatchOutsideProgram.getOrDefault( + classes, DexMethodSignatureSet.empty()), + affectedClassConsumer), executorService); timing.end(); @@ -204,8 +213,9 @@ // similarly to the way we deal with call chains in argument propagation. If a field is only // assigned the parameter of a given method, we would add the flow constraint "parameter p -> // field f". - public ArgumentPropagatorGraphLens.Builder optimize( + private ArgumentPropagatorGraphLens.Builder optimize( Set<DexProgramClass> stronglyConnectedProgramClasses, + DexMethodSignatureSet interfaceDispatchOutsideProgram, Consumer<DexProgramClass> affectedClassConsumer) { // First reserve pinned method signatures. reservePinnedMethodSignatures(stronglyConnectedProgramClasses); @@ -213,7 +223,8 @@ // To ensure that we preserve the overriding relationships between methods, we only remove a // constant or unused parameter from a virtual method when it can be removed from all other // virtual methods in the component with the same method signature. - computePrototypeChangesForVirtualMethods(stronglyConnectedProgramClasses); + computePrototypeChangesForVirtualMethods( + stronglyConnectedProgramClasses, interfaceDispatchOutsideProgram); // Build a graph lens while visiting the classes in the component. // TODO(b/190154391): Consider visiting the interfaces first, and then processing the @@ -225,7 +236,7 @@ stronglyConnectedProgramClassesWithDeterministicOrder.sort( Comparator.comparing(DexClass::getType)); for (DexProgramClass clazz : stronglyConnectedProgramClassesWithDeterministicOrder) { - if (visitClass(clazz, partialGraphLensBuilder)) { + if (visitClass(clazz, interfaceDispatchOutsideProgram, partialGraphLensBuilder)) { affectedClassConsumer.accept(clazz); } } @@ -276,7 +287,8 @@ } private void computePrototypeChangesForVirtualMethods( - Set<DexProgramClass> stronglyConnectedProgramClasses) { + Set<DexProgramClass> stronglyConnectedProgramClasses, + DexMethodSignatureSet interfaceDispatchOutsideProgram) { // Group the virtual methods in the component by their signatures. Map<DexMethodSignature, ProgramMethodSet> virtualMethodsBySignature = computeVirtualMethodsBySignature(stronglyConnectedProgramClasses); @@ -284,7 +296,9 @@ (signature, methods) -> { // Check that there are no keep rules that prohibit prototype changes from any of the // methods. - if (Iterables.any(methods, method -> !isPrototypeChangesAllowed(method))) { + if (Iterables.any( + methods, + method -> !isPrototypeChangesAllowed(method, interfaceDispatchOutsideProgram))) { return; } @@ -340,11 +354,13 @@ return virtualMethodsBySignature; } - private boolean isPrototypeChangesAllowed(ProgramMethod method) { + private boolean isPrototypeChangesAllowed( + ProgramMethod method, DexMethodSignatureSet interfaceDispatchOutsideProgram) { return appView.getKeepInfo(method).isParameterRemovalAllowed(options) && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue() && !appView.appInfo().isBootstrapMethod(method) - && !appView.appInfo().isMethodTargetedByInvokeDynamic(method); + && !appView.appInfo().isMethodTargetedByInvokeDynamic(method) + && !interfaceDispatchOutsideProgram.contains(method); } private SingleValue getReturnValueForVirtualMethods( @@ -416,7 +432,9 @@ // Returns true if the class was changed as a result of argument propagation. private boolean visitClass( - DexProgramClass clazz, ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) { + DexProgramClass clazz, + DexMethodSignatureSet interfaceDispatchOutsideProgram, + ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) { BooleanBox affected = new BooleanBox(); DexMethodSignatureSet instanceInitializerSignatures = DexMethodSignatureSet.create(); clazz.forEachProgramInstanceInitializer(instanceInitializerSignatures::add); @@ -424,7 +442,8 @@ method -> { RewrittenPrototypeDescription prototypeChanges = method.getDefinition().belongsToDirectPool() - ? computePrototypeChangesForDirectMethod(method, instanceInitializerSignatures) + ? computePrototypeChangesForDirectMethod( + method, interfaceDispatchOutsideProgram, instanceInitializerSignatures) : computePrototypeChangesForVirtualMethod(method); DexMethod newMethodSignature = getNewMethodSignature(method, prototypeChanges); if (newMethodSignature != method.getReference()) { @@ -518,9 +537,11 @@ } private RewrittenPrototypeDescription computePrototypeChangesForDirectMethod( - ProgramMethod method, DexMethodSignatureSet instanceInitializerSignatures) { + ProgramMethod method, + DexMethodSignatureSet interfaceDispatchOutsideProgram, + DexMethodSignatureSet instanceInitializerSignatures) { assert method.getDefinition().belongsToDirectPool(); - if (!isPrototypeChangesAllowed(method)) { + if (!isPrototypeChangesAllowed(method, interfaceDispatchOutsideProgram)) { return RewrittenPrototypeDescription.none(); } // TODO(b/199864962): Allow parameter removal from check-not-null classified methods.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java index e1e5f63..0b617b1 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexMethodSignature; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo; import com.android.tools.r8.graph.MethodResolutionResult; @@ -43,12 +44,15 @@ // methods) on the seen but not finished interfaces. final Map<DexProgramClass, MethodStateCollectionBySignature> methodStatesToPropagate = new IdentityHashMap<>(); + final Consumer<DexMethodSignature> interfaceDispatchOutsideProgram; public InterfaceMethodArgumentPropagator( AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo, - MethodStateCollectionByReference methodStates) { + MethodStateCollectionByReference methodStates, + Consumer<DexMethodSignature> interfaceDispatchOutsideProgram) { super(appView, immediateSubtypingInfo, methodStates); + this.interfaceDispatchOutsideProgram = interfaceDispatchOutsideProgram; } @Override @@ -135,6 +139,12 @@ return; } + assert resolutionResult.isSingleResolution(); + if (!resolutionResult.getResolutionPair().isProgramMethod()) { + interfaceDispatchOutsideProgram.accept(interfaceMethod); + return; + } + ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod(); if (resolvedMethod == null || resolvedMethod.getHolder().isInterface()
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java index a0ac6e8..e1ee3cb 100644 --- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java +++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -32,12 +32,11 @@ import java.util.function.Consumer; import java.util.function.Predicate; -public class RepackagingUseRegistry extends UseRegistry { +public class RepackagingUseRegistry extends UseRegistry<ProgramDefinition> { private final AppInfoWithLiveness appInfo; private final InternalOptions options; private final RepackagingConstraintGraph constraintGraph; - private final ProgramDefinition context; private final InitClassLens initClassLens; private final RepackagingConstraintGraph.Node node; private final RepackagingConstraintGraph.Node missingTypeNode; @@ -47,11 +46,10 @@ RepackagingConstraintGraph constraintGraph, ProgramDefinition context, RepackagingConstraintGraph.Node missingTypeNode) { - super(appView.dexItemFactory()); + super(context, appView.dexItemFactory()); this.appInfo = appView.appInfo(); this.options = appView.options(); this.constraintGraph = constraintGraph; - this.context = context; this.initClassLens = appView.initClassLens(); this.node = constraintGraph.getNode(context.getDefinition()); this.missingTypeNode = missingTypeNode; @@ -63,7 +61,7 @@ return true; } if (accessFlags.isProtected() - && !appInfo.isSubtype(context.getContextType(), referencedClass.getType())) { + && !appInfo.isSubtype(getContext().getContextType(), referencedClass.getType())) { return true; } return false; @@ -77,7 +75,7 @@ } if (accessFlags.isProtected()) { if (!appInfo.isSubtype( - context.getContextType(), resolutionResult.getResolvedHolder().getType())) { + getContext().getContextType(), resolutionResult.getResolvedHolder().getType())) { return true; } // Check for assignability if we are generating CF: @@ -85,7 +83,8 @@ if (isInvoke && options.isGeneratingClassFiles() && !appInfo.isSubtype( - resolutionResult.getInitialResolutionHolder().getType(), context.getContextType())) { + resolutionResult.getInitialResolutionHolder().getType(), + getContext().getContextType())) { return true; } } @@ -129,7 +128,7 @@ MethodResolutionResult methodResult = resolutionResult.asMethodResolutionResult(); if (methodResult.isClassNotFoundResult() || methodResult.isArrayCloneMethodResult() - || methodResult.isNoSuchMethodErrorResult(context.getContextClass(), appInfo)) { + || methodResult.isNoSuchMethodErrorResult(getContext().getContextClass(), appInfo)) { return; } node.addNeighbor(missingTypeNode);
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java index 60a6967..8cc522d 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceMethodResultImpl.java
@@ -111,16 +111,7 @@ .rewritePosition(stackTraceContext.getRewritePosition()))); } } - // Mapped ranges can have references to overloaded signatures. We distinguish those by - // looking at the cardinal mapping range. - for (MappedRange mappedRangeForPosition : mappedRangesForPosition) { - if (narrowedRanges.isEmpty() - || mappedRangeForPosition.originalRange == null - || !mappedRangeForPosition.originalRange.isCardinal) { - narrowedRanges.add(new Pair<>(mappedRange.getFirst(), new ArrayList<>())); - } - ListUtils.last(narrowedRanges).getSecond().add(mappedRangeForPosition); - } + narrowedRanges.add(new Pair<>(mappedRange.getFirst(), mappedRangesForPosition)); } } return new RetraceFrameResultImpl(
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java index e26e351..a50643a 100644 --- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java +++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceRegularExpressionParser.java
@@ -171,7 +171,7 @@ // TODO(b/145731185): Extend support for identifiers with strings inside back ticks. private static final String javaIdentifierSegment = - "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*"; + "\\p{javaJavaIdentifierStart}(?:-|\\p{javaJavaIdentifierPart})*"; private static final String METHOD_NAME_REGULAR_EXPRESSION = "(?:(" + javaIdentifierSegment + "|\\<init\\>|\\<clinit\\>))";
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java index 25f01cf..d48a739 100644 --- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java +++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -21,10 +21,9 @@ import com.android.tools.r8.utils.AndroidApiLevel; import java.util.ListIterator; -public class DefaultEnqueuerUseRegistry extends UseRegistry { +public class DefaultEnqueuerUseRegistry extends UseRegistry<ProgramMethod> { protected final AppView<? extends AppInfoWithClassHierarchy> appView; - private final ProgramMethod context; protected final Enqueuer enqueuer; private final AndroidApiLevelCompute computeApiLevel; private AndroidApiLevel maxApiReferenceLevel; @@ -34,156 +33,151 @@ ProgramMethod context, Enqueuer enqueuer, AndroidApiLevelCompute computeApiLevel) { - super(appView.dexItemFactory()); + super(context, appView.dexItemFactory()); this.appView = appView; - this.context = context; this.enqueuer = enqueuer; this.computeApiLevel = computeApiLevel; this.maxApiReferenceLevel = appView.options().minApiLevel; } - public ProgramMethod getContext() { - return context; - } - public DexProgramClass getContextHolder() { - return context.getHolder(); + return getContext().getHolder(); } public DexEncodedMethod getContextMethod() { - return context.getDefinition(); + return getContext().getDefinition(); } @Override public void registerInitClass(DexType clazz) { - enqueuer.traceInitClass(clazz, context); + enqueuer.traceInitClass(clazz, getContext()); } @Override public void registerInvokeVirtual(DexMethod invokedMethod) { setMaxApiReferenceLevel(invokedMethod); - enqueuer.traceInvokeVirtual(invokedMethod, context); + enqueuer.traceInvokeVirtual(invokedMethod, getContext()); } @Override public void registerInvokeDirect(DexMethod invokedMethod) { setMaxApiReferenceLevel(invokedMethod); - enqueuer.traceInvokeDirect(invokedMethod, context); + enqueuer.traceInvokeDirect(invokedMethod, getContext()); } @Override public void registerInvokeStatic(DexMethod invokedMethod) { setMaxApiReferenceLevel(invokedMethod); - enqueuer.traceInvokeStatic(invokedMethod, context); + enqueuer.traceInvokeStatic(invokedMethod, getContext()); } @Override public void registerInvokeInterface(DexMethod invokedMethod) { setMaxApiReferenceLevel(invokedMethod); - enqueuer.traceInvokeInterface(invokedMethod, context); + enqueuer.traceInvokeInterface(invokedMethod, getContext()); } @Override public void registerInvokeSuper(DexMethod invokedMethod) { setMaxApiReferenceLevel(invokedMethod); - enqueuer.traceInvokeSuper(invokedMethod, context); + enqueuer.traceInvokeSuper(invokedMethod, getContext()); } @Override public void registerInstanceFieldRead(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceInstanceFieldRead(field, context); + enqueuer.traceInstanceFieldRead(field, getContext()); } @Override public void registerInstanceFieldReadFromMethodHandle(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceInstanceFieldReadFromMethodHandle(field, context); + enqueuer.traceInstanceFieldReadFromMethodHandle(field, getContext()); } @Override public void registerInstanceFieldWrite(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceInstanceFieldWrite(field, context); + enqueuer.traceInstanceFieldWrite(field, getContext()); } @Override public void registerInstanceFieldWriteFromMethodHandle(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceInstanceFieldWriteFromMethodHandle(field, context); + enqueuer.traceInstanceFieldWriteFromMethodHandle(field, getContext()); } @Override public void registerNewInstance(DexType type) { setMaxApiReferenceLevel(type); - enqueuer.traceNewInstance(type, context); + enqueuer.traceNewInstance(type, getContext()); } @Override public void registerStaticFieldRead(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceStaticFieldRead(field, context); + enqueuer.traceStaticFieldRead(field, getContext()); } @Override public void registerStaticFieldReadFromMethodHandle(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceStaticFieldReadFromMethodHandle(field, context); + enqueuer.traceStaticFieldReadFromMethodHandle(field, getContext()); } @Override public void registerStaticFieldWrite(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceStaticFieldWrite(field, context); + enqueuer.traceStaticFieldWrite(field, getContext()); } @Override public void registerStaticFieldWriteFromMethodHandle(DexField field) { setMaxApiReferenceLevel(field); - enqueuer.traceStaticFieldWriteFromMethodHandle(field, context); + enqueuer.traceStaticFieldWriteFromMethodHandle(field, getContext()); } @Override public void registerConstClass( DexType type, ListIterator<? extends CfOrDexInstruction> iterator) { - enqueuer.traceConstClass(type, context, iterator); + enqueuer.traceConstClass(type, getContext(), iterator); } @Override public void registerCheckCast(DexType type) { - enqueuer.traceCheckCast(type, context); + enqueuer.traceCheckCast(type, getContext()); } @Override public void registerSafeCheckCast(DexType type) { - enqueuer.traceSafeCheckCast(type, context); + enqueuer.traceSafeCheckCast(type, getContext()); } @Override public void registerTypeReference(DexType type) { - enqueuer.traceTypeReference(type, context); + enqueuer.traceTypeReference(type, getContext()); } @Override public void registerInstanceOf(DexType type) { - enqueuer.traceInstanceOf(type, context); + enqueuer.traceInstanceOf(type, getContext()); } @Override public void registerExceptionGuard(DexType guard) { - enqueuer.traceExceptionGuard(guard, context); + enqueuer.traceExceptionGuard(guard, getContext()); } @Override public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) { super.registerMethodHandle(methodHandle, use); - enqueuer.traceMethodHandle(methodHandle, use, context); + enqueuer.traceMethodHandle(methodHandle, use, getContext()); } @Override public void registerCallSite(DexCallSite callSite) { super.registerCallSite(callSite); - enqueuer.traceCallSite(callSite, context); + enqueuer.traceCallSite(callSite, getContext()); } private void setMaxApiReferenceLevel(DexReference reference) {
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java index ab19637..b8f2b8f 100644 --- a/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java +++ b/src/main/java/com/android/tools/r8/shaking/MainDexDirectReferenceTracer.java
@@ -13,7 +13,6 @@ import com.android.tools.r8.graph.DexCallSite; import com.android.tools.r8.graph.DexClass; 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.DexMethodHandle; import com.android.tools.r8.graph.DexProgramClass; @@ -30,14 +29,12 @@ public class MainDexDirectReferenceTracer { private final AnnotationDirectReferenceCollector annotationDirectReferenceCollector = new AnnotationDirectReferenceCollector(); - private final DirectReferencesCollector codeDirectReferenceCollector; private final AppInfoWithClassHierarchy appInfo; private final Consumer<DexType> consumer; public MainDexDirectReferenceTracer( AppInfoWithClassHierarchy appInfo, Consumer<DexType> consumer) { - this.codeDirectReferenceCollector = new DirectReferencesCollector(appInfo.dexItemFactory()); this.appInfo = appInfo; this.consumer = consumer; } @@ -59,12 +56,12 @@ traceMethodDirectDependencies(definition.getReference(), consumer); return definition.hasCode(); }, - method -> method.registerCodeReferences(codeDirectReferenceCollector)); + this::runOnCode); } } public void runOnCode(ProgramMethod method) { - method.registerCodeReferences(codeDirectReferenceCollector); + method.registerCodeReferences(new DirectReferencesCollector(method)); } public static boolean hasReferencesOutsideMainDexClasses( @@ -102,10 +99,10 @@ } } - private class DirectReferencesCollector extends UseRegistry { + private class DirectReferencesCollector extends UseRegistry<ProgramMethod> { - private DirectReferencesCollector(DexItemFactory factory) { - super(factory); + private DirectReferencesCollector(ProgramMethod context) { + super(context, appInfo.dexItemFactory()); } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java index 8128db2..6ebd54b 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -17,6 +17,7 @@ import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexApplication; import com.android.tools.r8.graph.DexClass; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMember; import com.android.tools.r8.graph.DexEncodedMethod; @@ -52,6 +53,7 @@ import com.android.tools.r8.graph.TopDownClassHierarchyTraversal; import com.android.tools.r8.graph.TreeFixerBase; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.graph.UseRegistryWithResult; import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; @@ -440,19 +442,6 @@ } return false; } - // Only merge if api reference level of source class is equal to target class. - if (appView.options().apiModelingOptions().enableApiCallerIdentification) { - AndroidApiLevel sourceApiLevel = - sourceClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax); - AndroidApiLevel targetApiLevel = - targetClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax); - if (sourceApiLevel != targetApiLevel) { - if (Log.ENABLED) { - AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass); - } - return false; - } - } return true; } @@ -518,6 +507,20 @@ } return false; } + // Only merge if api reference level of source class is equal to target class. The check is + // somewhat expensive. + if (appView.options().apiModelingOptions().enableApiCallerIdentification) { + AndroidApiLevel sourceApiLevel = + sourceClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax); + AndroidApiLevel targetApiLevel = + targetClass.getApiReferenceLevel(appView, apiReferenceLevelCache::lookupMax); + if (sourceApiLevel != targetApiLevel) { + if (Log.ENABLED) { + AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass); + } + return false; + } + } return true; } @@ -559,13 +562,13 @@ // Check that all accesses from [source] to classes or members from the current package of // [source] will continue to work. This is guaranteed if the methods of [source] do not access // any private or protected classes or members from the current package of [source]. - IllegalAccessDetector registry = new IllegalAccessDetector(appView, source); TraversalContinuation result = source.traverseProgramMethods( method -> { - registry.setContext(method); - method.registerCodeReferences(registry); - if (registry.foundIllegalAccess()) { + boolean foundIllegalAccess = + method.registerCodeReferencesWithResult( + new IllegalAccessDetector(appView, method)); + if (foundIllegalAccess) { return TraversalContinuation.BREAK; } return TraversalContinuation.CONTINUE; @@ -1977,78 +1980,66 @@ // Searches for a reference to a non-public class, field or method declared in the same package // as [source]. - public static class IllegalAccessDetector extends UseRegistry { - - private boolean foundIllegalAccess; - private ProgramMethod context; + public static class IllegalAccessDetector extends UseRegistryWithResult<Boolean, ProgramMethod> { private final AppView<? extends AppInfoWithClassHierarchy> appView; - private final DexClass source; public IllegalAccessDetector( - AppView<? extends AppInfoWithClassHierarchy> appView, DexClass source) { - super(appView.dexItemFactory()); + AppView<? extends AppInfoWithClassHierarchy> appView, ProgramMethod context) { + super(context, appView.dexItemFactory(), false); this.appView = appView; - this.source = source; } - public boolean foundIllegalAccess() { - return foundIllegalAccess; - } + private boolean checkFieldReference(DexField field) { + DexType baseType = + appView.graphLens().lookupType(field.holder.toBaseType(appView.dexItemFactory())); + if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) { + if (checkTypeReference(field.holder) || checkTypeReference(field.type)) { + return true; + } - public void setContext(ProgramMethod context) { - this.context = context; - } - - private void checkFieldReference(DexField field) { - if (!foundIllegalAccess) { - DexType baseType = - appView.graphLens().lookupType(field.holder.toBaseType(appView.dexItemFactory())); - if (baseType.isClassType() && baseType.isSamePackage(source.type)) { - checkTypeReference(field.holder); - checkTypeReference(field.type); - - DexEncodedField definition = appView.appInfo().resolveField(field).getResolvedField(); - if (definition == null || !definition.accessFlags.isPublic()) { - foundIllegalAccess = true; - } + DexEncodedField definition = appView.appInfo().resolveField(field).getResolvedField(); + if (definition == null || !definition.accessFlags.isPublic()) { + setResult(true); + return true; } } + return false; } - private void checkMethodReference(DexMethod method, OptionalBool isInterface) { - if (!foundIllegalAccess) { - DexType baseType = - appView.graphLens().lookupType(method.holder.toBaseType(appView.dexItemFactory())); - if (baseType.isClassType() && baseType.isSamePackage(source.type)) { - checkTypeReference(method.holder); - checkTypeReference(method.proto.returnType); - for (DexType type : method.proto.parameters.values) { - checkTypeReference(type); - } - MethodResolutionResult resolutionResult = - isInterface.isUnknown() - ? appView.appInfo().unsafeResolveMethodDueToDexFormat(method) - : appView.appInfo().resolveMethod(method, isInterface.isTrue()); - if (!resolutionResult.isSingleResolution() - || !resolutionResult.asSingleResolution().getResolvedMethod().isPublic()) { - foundIllegalAccess = true; - } + private boolean checkMethodReference(DexMethod method, OptionalBool isInterface) { + DexType baseType = + appView.graphLens().lookupType(method.holder.toBaseType(appView.dexItemFactory())); + if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) { + if (checkTypeReference(method.holder) + || checkTypeReference(method.proto.returnType) + || Iterables.any(method.getParameters(), this::checkTypeReference)) { + return true; + } + + MethodResolutionResult resolutionResult = + isInterface.isUnknown() + ? appView.appInfo().unsafeResolveMethodDueToDexFormat(method) + : appView.appInfo().resolveMethod(method, isInterface.isTrue()); + if (!resolutionResult.isSingleResolution() + || !resolutionResult.asSingleResolution().getResolvedMethod().isPublic()) { + setResult(true); + return true; } } + return false; } - private void checkTypeReference(DexType type) { - if (!foundIllegalAccess) { - DexType baseType = - appView.graphLens().lookupType(type.toBaseType(appView.dexItemFactory())); - if (baseType.isClassType() && baseType.isSamePackage(source.type)) { - DexClass clazz = appView.definitionFor(baseType); - if (clazz == null || !clazz.accessFlags.isPublic()) { - foundIllegalAccess = true; - } + private boolean checkTypeReference(DexType type) { + DexType baseType = appView.graphLens().lookupType(type.toBaseType(appView.dexItemFactory())); + if (baseType.isClassType() && baseType.isSamePackage(getContext().getHolderType())) { + DexClass clazz = appView.definitionFor(baseType); + if (clazz == null || !clazz.accessFlags.isPublic()) { + setResult(true); + return true; } } + return false; } @Override @@ -2058,41 +2049,36 @@ @Override public void registerInvokeVirtual(DexMethod method) { - assert context != null; MethodLookupResult lookup = - appView.graphLens().lookupMethod(method, context.getReference(), Type.VIRTUAL); + appView.graphLens().lookupMethod(method, getContext().getReference(), Type.VIRTUAL); checkMethodReference(lookup.getReference(), OptionalBool.FALSE); } @Override public void registerInvokeDirect(DexMethod method) { - assert context != null; MethodLookupResult lookup = - appView.graphLens().lookupMethod(method, context.getReference(), Type.DIRECT); + appView.graphLens().lookupMethod(method, getContext().getReference(), Type.DIRECT); checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN); } @Override public void registerInvokeStatic(DexMethod method) { - assert context != null; MethodLookupResult lookup = - appView.graphLens().lookupMethod(method, context.getReference(), Type.STATIC); + appView.graphLens().lookupMethod(method, getContext().getReference(), Type.STATIC); checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN); } @Override public void registerInvokeInterface(DexMethod method) { - assert context != null; MethodLookupResult lookup = - appView.graphLens().lookupMethod(method, context.getReference(), Type.INTERFACE); + appView.graphLens().lookupMethod(method, getContext().getReference(), Type.INTERFACE); checkMethodReference(lookup.getReference(), OptionalBool.TRUE); } @Override public void registerInvokeSuper(DexMethod method) { - assert context != null; MethodLookupResult lookup = - appView.graphLens().lookupMethod(method, context.getReference(), Type.SUPER); + appView.graphLens().lookupMethod(method, getContext().getReference(), Type.SUPER); checkMethodReference(lookup.getReference(), OptionalBool.UNKNOWN); } @@ -2184,7 +2170,7 @@ } @Override - public Consumer<UseRegistry> getRegistryCallback() { + public Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method) { return registry -> { assert registry.getTraversalContinuation().shouldContinue(); switch (type) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java index a05d505..c641e9e 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClasspathClassDefinition.java
@@ -5,8 +5,8 @@ import com.android.tools.r8.graph.DexClasspathClass; import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; +import com.android.tools.r8.utils.structural.HasherWrapper; import com.android.tools.r8.utils.structural.RepresentativeMap; -import com.google.common.hash.Hasher; /** * Definition of a synthetic classpath class. @@ -44,7 +44,7 @@ } @Override - void internalComputeHash(Hasher hasher, RepresentativeMap map) { + void internalComputeHash(HasherWrapper hasher, RepresentativeMap map) { clazz.hashWithTypeEquivalence(hasher, map); }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java index 7c1decc..36c6573 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -8,10 +8,9 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; +import com.android.tools.r8.utils.structural.HasherWrapper; import com.android.tools.r8.utils.structural.RepresentativeMap; import com.google.common.hash.HashCode; -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; /** * Base type for the definition of a synthetic item. @@ -70,7 +69,7 @@ boolean intermediate, ClassToFeatureSplitMap classToFeatureSplitMap, SyntheticItems syntheticItems) { - Hasher hasher = Hashing.murmur3_128().newHasher(); + HasherWrapper hasher = HasherWrapper.murmur3128Hasher(); hasher.putInt(kind.id); if (getKind().isFixedSuffixSynthetic) { // Fixed synthetics are non-shareable. Its unique type is used as the hash key. @@ -87,7 +86,7 @@ return hasher.hash(); } - abstract void internalComputeHash(Hasher hasher, RepresentativeMap map); + abstract void internalComputeHash(HasherWrapper hasher, RepresentativeMap map); final boolean isEquivalentTo( D other,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java index e407c3a..caf0f42 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -492,7 +492,9 @@ // Recheck if it is present now the lock is held. dexClass = appView.definitionFor(type); if (dexClass != null) { - assert dexClass.isProgramClass(); + if (!dexClass.isProgramClass()) { + errorOnInvalidSyntheticEnsure(dexClass, "program", appView); + } return dexClass.asProgramClass(); } assert !isSyntheticClass(type); @@ -625,17 +627,57 @@ return new ProgramMethod(clazz, methodDefinition); } - public DexClasspathClass createFixedClasspathClass( - SyntheticKind kind, DexClasspathClass context, DexItemFactory factory) { - // Obtain the outer synthesizing context in the case the context itself is synthetic. - // This is to ensure a flat input-type -> synthetic-item mapping. - SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context); - DexType type = SyntheticNaming.createFixedType(kind, outerContext, factory); - SyntheticClasspathClassBuilder classBuilder = - new SyntheticClasspathClassBuilder(type, kind, outerContext, factory); - DexClasspathClass clazz = classBuilder.build(); - addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz)); - return clazz; + private void errorOnInvalidSyntheticEnsure(DexClass dexClass, String kind, AppView<?> appView) { + String classKind = + dexClass.isProgramClass() + ? "program" + : dexClass.isClasspathClass() ? "classpath" : "library"; + throw appView + .reporter() + .fatalError( + "Cannot ensure " + + dexClass.type + + " as a synthetic " + + kind + + " class, because it is already a " + + classKind + + " class."); + } + + private DexClasspathClass internalEnsureDexClasspathClass( + SyntheticKind kind, + Consumer<SyntheticClasspathClassBuilder> classConsumer, + Consumer<DexClasspathClass> onCreationConsumer, + SynthesizingContext outerContext, + DexType type, + AppView<?> appView) { + synchronized (type) { + DexClass clazz = appView.definitionFor(type); + if (clazz != null) { + if (!clazz.isClasspathClass()) { + errorOnInvalidSyntheticEnsure(clazz, "classpath", appView); + } + return clazz.asClasspathClass(); + } + SyntheticClasspathClassBuilder classBuilder = + new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory()); + classConsumer.accept(classBuilder); + DexClasspathClass definition = classBuilder.build(); + addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, definition)); + onCreationConsumer.accept(definition); + return definition; + } + } + + public DexClasspathClass ensureFixedClasspathClassFromType( + SyntheticKind kind, + DexType contextType, + AppView<?> appView, + Consumer<SyntheticClasspathClassBuilder> classConsumer) { + SynthesizingContext outerContext = SynthesizingContext.fromType(contextType); + DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory()); + return internalEnsureDexClasspathClass( + kind, classConsumer, ignored -> {}, outerContext, type, appView); } public DexClasspathClass ensureFixedClasspathClass( @@ -644,29 +686,12 @@ AppView<?> appView, Consumer<SyntheticClasspathClassBuilder> classConsumer, Consumer<DexClasspathClass> onCreationConsumer) { + // Obtain the outer synthesizing context in the case the context itself is synthetic. + // This is to ensure a flat input-type -> synthetic-item mapping. SynthesizingContext outerContext = SynthesizingContext.fromNonSyntheticInputContext(context); DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory()); - DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type); - if (dexClass != null) { - assert dexClass.isClasspathClass(); - return dexClass.asClasspathClass(); - } - synchronized (context) { - dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type); - if (dexClass != null) { - assert dexClass.isClasspathClass(); - return dexClass.asClasspathClass(); - } - // Obtain the outer synthesizing context in the case the context itself is synthetic. - // This is to ensure a flat input-type -> synthetic-item mapping. - SyntheticClasspathClassBuilder classBuilder = - new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory()); - classConsumer.accept(classBuilder); - DexClasspathClass clazz = classBuilder.build(); - addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, clazz)); - onCreationConsumer.accept(clazz); - return clazz; - } + return internalEnsureDexClasspathClass( + kind, classConsumer, onCreationConsumer, outerContext, type, appView); } public DexClassAndMethod ensureFixedClasspathClassMethod( @@ -737,28 +762,6 @@ return internalEnsureDexProgramClass(kind, fn, onCreationConsumer, outerContext, type, appView); } - public DexClasspathClass ensureFixedClasspathClassFromType( - SyntheticKind kind, - DexType contextType, - AppView<?> appView, - Consumer<SyntheticClasspathClassBuilder> fn) { - SynthesizingContext outerContext = SynthesizingContext.fromType(contextType); - DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory()); - synchronized (contextType) { - DexClass clazz = appView.definitionFor(type); - if (clazz != null) { - assert clazz.isClasspathClass(); - return clazz.asClasspathClass(); - } - SyntheticClasspathClassBuilder classBuilder = - new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory()); - fn.accept(classBuilder); - DexClasspathClass definition = classBuilder.build(); - addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, definition)); - return definition; - } - } - /** Create a single synthetic method item. */ public ProgramMethod createMethod( SyntheticKind kind,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java index a9d11c7..d00eb21 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
@@ -6,8 +6,8 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; +import com.android.tools.r8.utils.structural.HasherWrapper; import com.android.tools.r8.utils.structural.RepresentativeMap; -import com.google.common.hash.Hasher; import java.util.function.Consumer; /** @@ -59,7 +59,7 @@ } @Override - void internalComputeHash(Hasher hasher, RepresentativeMap map) { + void internalComputeHash(HasherWrapper hasher, RepresentativeMap map) { method.getDefinition().hashWithTypeEquivalence(hasher, map); }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java index 3506dab..077bc76 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticProgramClassDefinition.java
@@ -5,8 +5,8 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; +import com.android.tools.r8.utils.structural.HasherWrapper; import com.android.tools.r8.utils.structural.RepresentativeMap; -import com.google.common.hash.Hasher; import java.util.function.Consumer; /** @@ -54,7 +54,7 @@ } @Override - void internalComputeHash(Hasher hasher, RepresentativeMap map) { + void internalComputeHash(HasherWrapper hasher, RepresentativeMap map) { clazz.hashWithTypeEquivalence(hasher, map); }
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java index 0a1ec66..a3700fe 100644 --- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java +++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -283,17 +283,15 @@ }); } - class MethodUseCollector extends UseRegistry { + class MethodUseCollector extends UseRegistry<ProgramMethod> { - private final ProgramMethod context; private final GraphLens graphLens; private final InitClassLens initClassLens; private final DefinitionContext referencedFrom; public MethodUseCollector( ProgramMethod context, GraphLens graphLens, InitClassLens initClassLens) { - super(appInfo.dexItemFactory()); - this.context = context; + super(context, appInfo.dexItemFactory()); this.graphLens = graphLens; this.initClassLens = initClassLens; this.referencedFrom = DefinitionContextUtils.create(context); @@ -303,7 +301,7 @@ @Override public void registerInvokeDirect(DexMethod method) { - MethodLookupResult lookupResult = graphLens.lookupInvokeDirect(method, context); + MethodLookupResult lookupResult = graphLens.lookupInvokeDirect(method, getContext()); assert lookupResult.getType().isDirect(); DexMethod rewrittenMethod = lookupResult.getReference(); DexClass holder = appInfo.definitionFor(rewrittenMethod.getHolderType()); @@ -313,14 +311,14 @@ @Override public void registerInvokeInterface(DexMethod method) { - MethodLookupResult lookupResult = graphLens.lookupInvokeInterface(method, context); + MethodLookupResult lookupResult = graphLens.lookupInvokeInterface(method, getContext()); assert lookupResult.getType().isInterface(); handleInvokeWithDynamicDispatch(lookupResult); } @Override public void registerInvokeStatic(DexMethod method) { - MethodLookupResult lookupResult = graphLens.lookupInvokeStatic(method, context); + MethodLookupResult lookupResult = graphLens.lookupInvokeStatic(method, getContext()); assert lookupResult.getType().isStatic(); DexMethod rewrittenMethod = lookupResult.getReference(); handleRewrittenMethodResolution( @@ -329,7 +327,7 @@ @Override public void registerInvokeSuper(DexMethod method) { - MethodLookupResult lookupResult = graphLens.lookupInvokeSuper(method, context); + MethodLookupResult lookupResult = graphLens.lookupInvokeSuper(method, getContext()); assert lookupResult.getType().isSuper(); DexMethod rewrittenMethod = lookupResult.getReference(); MethodResolutionResult resolutionResult = @@ -341,12 +339,12 @@ } handleRewrittenMethodReference( rewrittenMethod, - resolutionResult.lookupInvokeSuperTarget(context.getHolder(), appInfo)); + resolutionResult.lookupInvokeSuperTarget(getContext().getHolder(), appInfo)); } @Override public void registerInvokeVirtual(DexMethod method) { - MethodLookupResult lookupResult = graphLens.lookupInvokeVirtual(method, context); + MethodLookupResult lookupResult = graphLens.lookupInvokeVirtual(method, getContext()); assert lookupResult.getType().isVirtual(); handleInvokeWithDynamicDispatch(lookupResult); } @@ -489,7 +487,7 @@ // For lambdas that implement an interface, also keep the interface method by simulating an // invoke to it from the current context. - LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, context); + LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo, getContext()); if (descriptor != null) { for (DexType interfaceType : descriptor.interfaces) { DexClass interfaceDefinition = appInfo.definitionFor(interfaceType);
diff --git a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java index 91b0638..6af263f 100644 --- a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java +++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -5,7 +5,9 @@ package com.android.tools.r8.utils; import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.Set; import java.util.function.Consumer; @@ -24,4 +26,10 @@ collection.forEach(consumer); } } + + public static <T> Collection<T> sort(Collection<T> items, Comparator<T> comparator) { + ArrayList<T> ts = new ArrayList<>(items); + ts.sort(comparator); + return ts; + } }
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 9449f1b..9d2657a 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -11,10 +11,11 @@ import com.android.tools.r8.DexIndexedConsumer; import com.android.tools.r8.DumpOptions; import com.android.tools.r8.FeatureSplit; +import com.android.tools.r8.MapIdProvider; import com.android.tools.r8.ProgramConsumer; import com.android.tools.r8.StringConsumer; import com.android.tools.r8.Version; -import com.android.tools.r8.androidapi.AndroidApiClass; +import com.android.tools.r8.androidapi.AndroidApiForHashingClass; import com.android.tools.r8.cf.CfVersion; import com.android.tools.r8.dex.Marker; import com.android.tools.r8.dex.Marker.Backend; @@ -33,6 +34,7 @@ import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexClasspathClass; import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItem; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexLibraryClass; @@ -85,12 +87,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiConsumer; -import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Function; @@ -281,6 +281,7 @@ public BiPredicate<String, Long> dexClassChecksumFilter = (name, checksum) -> true; public boolean cfToCfDesugar = false; public boolean forceAnnotateSynthetics = false; + public boolean readDebugSetFileEvent = false; public int callGraphLikelySpuriousCallEdgeThreshold = 50; @@ -866,6 +867,8 @@ public Consumer<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer = null; + public MapIdProvider mapIdProvider = null; + public static boolean assertionsEnabled() { boolean assertionsEnabled = false; assert assertionsEnabled = true; // Intentional side-effect. @@ -1370,8 +1373,10 @@ public boolean enableApiCallerIdentification = false; public boolean checkAllApiReferencesAreSet = true; + public boolean useHashingDatabase = true; - public void visitMockedApiReferences(BiConsumer<ClassReference, AndroidApiClass> consumer) { + public void visitMockedApiLevelsForReferences( + DexItemFactory factory, Consumer<AndroidApiForHashingClass> consumer) { if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) { return; } @@ -1385,62 +1390,37 @@ classReferences.forEach( classReference -> { consumer.accept( - classReference, - new AndroidApiClass(classReference) { + new AndroidApiForHashingClass() { + @Override + public DexType getType() { + return factory.createType(classReference.getDescriptor()); + } + @Override public AndroidApiLevel getApiLevel() { - return classApiMapping.getOrDefault(classReference, AndroidApiLevel.B); + return classApiMapping.getOrDefault(classReference, AndroidApiLevel.UNKNOWN); } @Override - public int getMemberCount() { - return 0; + public void visitMethodsWithApiLevels( + BiConsumer<DexMethod, AndroidApiLevel> consumer) { + methodApiMapping.forEach( + (methodReference, apiLevel) -> { + if (methodReference.getHolderClass().equals(classReference)) { + consumer.accept(factory.createMethod(methodReference), apiLevel); + } + }); } @Override - public TraversalContinuation visitFields( - BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) { - for (Entry<FieldReference, AndroidApiLevel> entry : - fieldApiMapping.entrySet()) { - if (!entry.getKey().getHolderClass().equals(classReference)) { - continue; - } - if (EntryUtils.accept(entry, visitor).shouldBreak()) { - return TraversalContinuation.BREAK; - } - } - return TraversalContinuation.CONTINUE; - } - - @Override - public TraversalContinuation visitMethods( - BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) { - for (Entry<MethodReference, AndroidApiLevel> entry : - methodApiMapping.entrySet()) { - if (!entry.getKey().getHolderClass().equals(classReference)) { - continue; - } - if (EntryUtils.accept(entry, visitor).shouldBreak()) { - return TraversalContinuation.BREAK; - } - } - return TraversalContinuation.CONTINUE; - } - - @Override - protected TraversalContinuation visitFields( - BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor, - ClassReference holder, - int minApi) { - return null; - } - - @Override - protected TraversalContinuation visitMethods( - BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor, - ClassReference holder, - int minApi) { - return null; + public void visitFieldsWithApiLevels( + BiConsumer<DexField, AndroidApiLevel> consumer) { + fieldApiMapping.forEach( + (fieldReference, apiLevel) -> { + if (fieldReference.getHolderClass().equals(classReference)) { + consumer.accept(factory.createField(fieldReference), apiLevel); + } + }); } }); });
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java index 7687a88..911290b 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java +++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -4,7 +4,10 @@ package com.android.tools.r8.utils.collections; +import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; + import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexDefinitionSupplier; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.GraphLens; @@ -13,6 +16,7 @@ import com.android.tools.r8.utils.SetUtils; import java.util.Set; import java.util.function.IntFunction; +import java.util.function.Predicate; public class LongLivedProgramMethodSetBuilder<T extends ProgramMethodSet> { @@ -98,15 +102,37 @@ return this; } + @Deprecated public void remove(DexMethod method) { methods.remove(method); } + public void remove(DexMethod method, GraphLens currentGraphLens) { + assert isEmpty() || verifyIsRewrittenWithLens(currentGraphLens); + methods.remove(method); + } + public LongLivedProgramMethodSetBuilder<T> removeAll(Iterable<DexMethod> methods) { methods.forEach(this::remove); return this; } + public LongLivedProgramMethodSetBuilder<T> removeIf( + DexDefinitionSupplier definitions, Predicate<ProgramMethod> predicate) { + methods.removeIf( + method -> { + DexProgramClass holder = + asProgramClassOrNull(definitions.definitionFor(method.getHolderType())); + ProgramMethod definition = method.lookupOnProgramClass(holder); + if (definition == null) { + assert false; + return true; + } + return predicate.test(definition); + }); + return this; + } + public LongLivedProgramMethodSetBuilder<T> rewrittenWithLens( AppView<AppInfoWithLiveness> appView) { return rewrittenWithLens(appView.graphLens());
diff --git a/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java index d1b6653..2062478 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java +++ b/src/main/java/com/android/tools/r8/utils/structural/DefaultHashingVisitor.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.utils.structural; import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept; -import com.google.common.hash.Hasher; /** * Default visitor for hashing a structural item. @@ -14,11 +13,11 @@ */ public class DefaultHashingVisitor { - public static <T> void run(T item, Hasher hasher, StructuralMapping<T> accept) { + public static <T> void run(T item, HasherWrapper hasher, StructuralMapping<T> accept) { run(item, hasher, (i, visitor) -> visitor.visit(i, accept)); } - public static <T> void run(T item, Hasher hasher, HashingAccept<T> hashingAccept) { + public static <T> void run(T item, HasherWrapper hasher, HashingAccept<T> hashingAccept) { HashingVisitorWithTypeEquivalence.run(item, hasher, t -> t, hashingAccept); } }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HasherWrapper.java b/src/main/java/com/android/tools/r8/utils/structural/HasherWrapper.java new file mode 100644 index 0000000..8427cfe --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/structural/HasherWrapper.java
@@ -0,0 +1,88 @@ +// 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.utils.structural; + +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; + +/** + * This is an interface that mimics the Hasher interface in Guava which allows us to use hashing in + * the tests. + */ +public interface HasherWrapper { + + void putBoolean(boolean value); + + void putInt(int value); + + void putFloat(float value); + + void putLong(long value); + + void putDouble(double value); + + void putBytes(byte[] content); + + String hashCodeAsString(); + + <T> T hash(); + + static HasherWrapper sha256Hasher() { + return new HasherWrapped(Hashing.sha256().newHasher()); + } + + static HasherWrapper murmur3128Hasher() { + return new HasherWrapped(Hashing.murmur3_128().newHasher()); + } + + class HasherWrapped implements HasherWrapper { + + private final Hasher hasher; + + public HasherWrapped(Hasher hasher) { + this.hasher = hasher; + } + + @Override + public void putBoolean(boolean value) { + hasher.putBoolean(value); + } + + @Override + public void putInt(int value) { + hasher.putInt(value); + } + + @Override + public void putFloat(float value) { + hasher.putFloat(value); + } + + @Override + public void putLong(long value) { + hasher.putLong(value); + } + + @Override + public void putDouble(double value) { + hasher.putDouble(value); + } + + @Override + public void putBytes(byte[] content) { + hasher.putBytes(content); + } + + @Override + public <T> T hash() { + return (T) hasher.hash(); + } + + @Override + public String hashCodeAsString() { + return hasher.hash().toString(); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java index 696c454..538ddcd 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java +++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitor.java
@@ -9,11 +9,9 @@ import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept; -import com.google.common.hash.Hasher; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; -import java.util.function.BiConsumer; public abstract class HashingVisitor { @@ -55,7 +53,4 @@ } public abstract <S> void visit(S item, StructuralMapping<S> accept); - - @Deprecated - public abstract <S> void visit(S item, BiConsumer<S, Hasher> hasher); }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java index ff0e4ba..303f212 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java +++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -8,9 +8,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept; import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept; -import com.google.common.hash.Hasher; import java.util.Iterator; -import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.ToDoubleFunction; @@ -21,19 +19,19 @@ public class HashingVisitorWithTypeEquivalence extends HashingVisitor { public static <T> void run( - T item, Hasher hasher, RepresentativeMap map, StructuralMapping<T> accept) { + T item, HasherWrapper hasher, RepresentativeMap map, StructuralMapping<T> accept) { run(item, hasher, map, (i, visitor) -> visitor.visit(i, accept)); } public static <T> void run( - T item, Hasher hasher, RepresentativeMap map, HashingAccept<T> hashingAccept) { + T item, HasherWrapper hasher, RepresentativeMap map, HashingAccept<T> hashingAccept) { hashingAccept.acceptHashing(item, new HashingVisitorWithTypeEquivalence(hasher, map)); } - private final Hasher hash; + private final HasherWrapper hash; private final RepresentativeMap representatives; - private HashingVisitorWithTypeEquivalence(Hasher hash, RepresentativeMap representatives) { + private HashingVisitorWithTypeEquivalence(HasherWrapper hash, RepresentativeMap representatives) { this.hash = hash; this.representatives = representatives; } @@ -85,11 +83,6 @@ } } - @Override - public <S> void visit(S item, BiConsumer<S, Hasher> hasher) { - hasher.accept(item, hash); - } - private static class ItemSpecification<T> extends StructuralSpecification<T, ItemSpecification<T>> {
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java index 8740299..057a18c 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java +++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralItem.java
@@ -3,9 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils.structural; -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; - /** Specified types must implement methods to determine equality, hashing and order. */ public interface StructuralItem<T extends StructuralItem<T>> extends Ordered<T> { @@ -60,24 +57,17 @@ * <p>This should *not* be overwritten, instead items should overwrite acceptHashing which will * ensure that the effect is in place for any HashingVisitor. */ - default void hash(Hasher hasher) { + default void hash(HasherWrapper hasher) { DefaultHashingVisitor.run(self(), hasher, StructuralItem::acceptHashing); } - /** Hashing method to use from tests to avoid having guava types shared between R8 and tests. */ - default String hashForTesting() { - Hasher hasher = Hashing.sha256().newHasher(); - hash(hasher); - return hasher.hash().toString(); - } - /** * Implementation of the default hashing with a type equivalence on the item. * * <p>This should *not* be overwritten, instead items should overwrite acceptHashing which will * ensure that the effect is in place for any HashingVisitor. */ - default void hashWithTypeEquivalence(Hasher hasher, RepresentativeMap map) { + default void hashWithTypeEquivalence(HasherWrapper hasher, RepresentativeMap map) { HashingVisitorWithTypeEquivalence.run(self(), hasher, map, StructuralItem::acceptHashing); }
diff --git a/src/main/resources/api_database/api_database_ambiguous.txt b/src/main/resources/api_database/api_database_ambiguous.txt new file mode 100644 index 0000000..3ffb464 --- /dev/null +++ b/src/main/resources/api_database/api_database_ambiguous.txt
@@ -0,0 +1,473 @@ +828623182530e5b5c55265b6a20263be:9 +850665aac5116ea7c5dd3ed4d672cc68:1 +d853b59bd420a046a715750d924360d0:1 +1a9e947630aeed44da4b6e668e8ac214:1 +d96d3dfd316fb498b8989986135bb848:1 +281b8e41d83f2962772157285ee84c38:1 +943b393cd0aa3d44e2609c7a7008e36f:1 +b1f7571f9f4648ce69a9e79455d3df8a:1 +22def9631f444a8421104a4c6fd26845:1 +d448211aa9d792c542c8a12499062780:1 +843e12eb3c7a2c2c7c2d1b64058d619e:1 +6fa6dc2897ec7eaec581306eb1cbacae:1 +1b6414136749889f64248ad232774cd4:1 +aeb599339c06ae54c794c71be6b2b1f0:1 +9a57214d87842677a74f893161a4b9be:1 +aa69312c004e15f4fa8efce471b0cfc1:1 +619d04422f81707ae4354cb9c8b41ed5:1 +8fe152aee223813be38a1103ba72c66e:1 +e19b30d13798c227510c8fb9d5ca20ec:1 +c892a71c9b51dff23b4b6d4d78c46ca0:1 +97d4f84cdaf7386e47d1c8bea51a45b5:1 +e1ed03e2c2a9fd3a5866fb3771530dc9:1 +2efc71cc840f54a63d8ce5ff313a7e2f:1 +e367ef6e11b586255cbde00200814031:1 +e1863e976cd3e82c00e43304db807943:1 +3fb35789d4aee6489350c670908bd59e:1 +e63feec2ab98295f4cd1f09d21e35ce9:1 +f081ee3e1cb0dec48d0127716393edec:1 +1db593f23925e8dc2fb326b2a368335c:1 +089be95001c81698ed7a1ae6eadb480b:1 +62e65d09c285da8da8dbecff2a85eb5e:1 +466714572719ed5745a5b5cd886c880b:1 +080573d2f4292d0789f942bd39af7594:1 +449e3c63d3565b07e6e39ae06b36e9a1:1 +96ca46ef9f7f9ed890508a01899764eb:1 +603e49aeac3c895300c860d6bd596135:1 +0de08429c1f0d8c9804107e8dac151f0:1 +aba1819af7abfe17006adcf74c572eab:1 +a1061d47d7a301608f4f7b396bb81ee3:1 +cf60514a6e7b90495f5a47161478a33f:1 +e8890acb1c71aa0076dffdb89dff32e3:1 +987a12e7a344e556d9544c953d62ae8b:1 +8f28909290b786920a4b9fb635ba6677:1 +a0d0240922deab6b46a7ba1509aa3168:1 +d9eb1dbdb92903db5b787e2e70e34c39:1 +9c45ce862efc619f1cca962c8caeb844:1 +0ef069b7a04f779edb4eddc5d9e68664:1 +e04bb82da6212e6b9993e2f2177d7b34:1 +e97bced61f81e5e6486cabccafd486b9:1 +c2d6153e0b11fa485a1db9b895d7ca4f:1 +4a3f92803a9af2349140ee2d20d4016f:1 +45c104bb4202da85198b615491d1de23:1 +c841eae393bc4c731d85b057b263effa:1 +641c008d5e49914df8db013a5bb050e7:1 +00217564c415ce5c03abf66b6863f30a:1 +a98b9be4a37764162034e849091e2f2a:1 +7275881c062d5fa141b8beef6d60f05b:1 +36779dd0e73608472e1c22fca2914eba:1 +ddda7f2d9e16b789b36d9235d1a4b52a:1 +1e39ef60367e236fa336f96705b2b10f:1 +5b17c7c02bc7ab47a30ea2856738af8c:1 +9963627e831a6315767f99893ca660c1:1 +43baf0e2fd2a825c22e70c4f589441df:1 +eceb74f53e3044ee33a39ed0b6c9cc73:1 +769d9f4e5046a4e0a480b614eb530cb1:1 +844cdb718520e79a957bd880c6e046b5:1 +f25a0a19ee375dbe75922bf1817f37de:1 +fe40c696eb0567e090d8968b387100e4:1 +885d4879046a451b2330409a9c316a86:1 +ebb652ecc8bc2f4a2d616a8294624e1d:1 +60c0494c6547c2aa37081b0a045f2dbb:1 +d97969716904e1a32e4ce6c4de736fac:1 +c12acdd4d8e17d803e32eb32757be4e9:1 +83255b102f7327ffe0819cc9d48d764a:1 +a159e58a81a5d7e46b1d44f845486c25:1 +c9a7fe526ed238e56f029bdd9eadd4a8:1 +a5352e6f5efd89293cad68bde5359174:21 +0873c171286bf72adca073b8232909ad:21 +75fcc63a7299ec405cbbbae6c6f4124a:21 +4242f75bd579b5860e448896bd97161e:21 +f8e3f0c3200fb1dd44acfbd301d96bed:21 +3e972f384811f77fd62ce384910c7605:21 +746752a0385785d6c4a6af907b506ff8:21 +f2c67cccefcb8826a54b39122e002062:21 +e40adee8247464101fcb01d083025c31:21 +d96183e4ed9b9b2b075bd5f8c4364100:21 +0a8ab6673c7b4e87cb2faf2d7005362a:21 +810a68c672694a924abe0f0c59a9a394:21 +fe284dc96dc744beee1ddbb1a8e4c4ee:21 +d34d323d00595f4051cfe0621331317d:21 +711a52bc9e3eafe73e0c64c4ae6d5e6a:21 +542566d5040f1a84a95caf3c78e8d54f:21 +fadc0a0d51e6ccdc60992c058edd2711:21 +ccb12040fbbda66de180c11e6251049d:21 +052dc8ce89610356ae4c22dacb2668f6:21 +eb911a19cc8e2139a72a6bf025f0ef92:21 +8a9b11e0d4a3993f76485c069942489c:21 +b50ba38de9db0ecba8a7618f6138380b:21 +5a50c9965f9cd4ee5a321bbd530cc5b2:21 +301f4d37021f2c80999eedb2219554bd:21 +391832837fd0c5bd42b6d5d58dcd80f6:21 +535462c1df188932ba4375a241ac0a3b:21 +517cb4d902b3c235f31b41bcd012b184:21 +dd9d0f0182859fbb91bfe9cb3504becf:21 +4634339ec4a3dfef9d6a8a22e18c1354:21 +4015e477f56bfed839fc88a1ba749750:21 +be73d1320eb5c1229bc65e73f82039f3:21 +1c2ab72e50012527678e434999c9044a:21 +a09c3b287f292693c49b6ac6139f3aca:21 +67ce8616e53660f5898eaa146c7f3835:21 +5b728323f7668ae4ff0e895afaf15cea:21 +b070068bc98b0eafc27ab9bb38e7cfbb:21 +eadaf0fe065d92924ebd1cade7c57e35:21 +724567eb08f8a1a1dba97c82bd0a8d00:21 +06065f8f4550b0f3530468ae56e4437b:21 +eea2af8f17a87b391353b3878325bbd1:21 +aa462a0178a7561e7c756b157cbeeb38:21 +3af411c682f8264bb7c70794211c6248:21 +b972f761c2e0edfca60661ae2abf0218:21 +da13bac632b4f32422190d415f31bab8:21 +a7c7530bd46ed4e558e4d448f1ab9b7c:21 +e2c48e8e5e90a5dcc4cd070af2c1b20e:21 +0fc8da4c46369ca9dbcee59fb490681c:21 +5bb64f5d5c0764c9c1076e2c00ee3b3c:21 +f92f02539108f4c7034055e630ce5b1b:21 +21206168597de4cc003cf94bb2476441:21 +80ffcde01a4a187455c15c7b11ae7c33:21 +5adbafe8f34e077daeacc70305eed4f1:21 +d7d607c796c81475b0c57f2646b7e662:21 +4f89f7e86a6cb4ab329ea69918ae6f49:21 +0f88600ff268e2f32d56c63ce955aeba:21 +aeb824437b0a504c4a1a3109729a8817:21 +bd00bb3c1478ff33a781c1b48bfa9112:21 +eabb0f12ea319b61bdf53d9f4a6d7044:21 +b5653063b6061db848ef666076d4a44b:21 +1bf6b15eb573dea85120bcc84f419023:21 +71ae94c8959d23501b3a25269ed58f50:21 +c0673d423ec86b469092b2eedbf4e8db:21 +52986dd229c1677a8a8fea05023f7625:21 +364cf0eb88afb77fbc7209d2c1c7273e:21 +072b20ca146065654d29d72fa24fc89a:21 +50ddd3dc05b1198214cb57e9250d7076:21 +3eb1459c5b79cc1a562b145846a1b957:21 +d84c006146e1a54bba7a7122983ef085:21 +3a16ed65c968daf194834ce7a0225b8a:21 +6321e1881f171dc79e50d8e0cbc6ce7d:21 +5800fc71343c619b5e005bbab60912e0:21 +8e087b8bbcd92fe9389bf2f469f383f6:21 +e7e98eb2a51cc3cbf30bd532d8942294:21 +c173773ee22e94ab734c05bd504d3325:21 +5bef782b74608c4de25f4d053ae543a9:21 +ba7c51aed579c0f1b473ef2035b15a17:21 +169dfedc30ae5b95af848f3184206b4b:21 +549d9811413f0c4b84cd1e84290127f1:21 +668fa5f927950cbb34952cdb7e631eb8:21 +580fe13c6b95c05247c1770fff6d0fe9:21 +02d6383509108efe665de4a2aff24702:21 +68749591b1210c540ab306d416f49c68:21 +75269722fb78a1b61a850da9a73984fc:21 +58c819dd55c2558fed93faef88f45e89:21 +e479c8a303ccdaf45f673c78971d6a44:21 +cd94f79ea296e6126eb563a6e82a73b3:21 +ab652b1e99797dd3cc3bfb7e48db5d18:21 +4d3ab1362ff964f3947b437b16aba576:21 +53dea4d4defbb7512a31f4fa659437ae:21 +252e68c63ccea47b1d02a47837383c4e:21 +a9fff0b0f85b598b277aaf29fe537ad8:21 +caf732f52661f4c573b342a3d1da85bc:21 +9401285d5c41e4e0ee4581c62f4a2fc4:21 +5d0c7f0ad2c60ef78ae47f854b90a194:21 +983db0abe1d8976946a8a19eee412c9d:21 +e7ca6d1abe4cd55a491cdf58eb8f4602:17 +a4fc9c89f980d489cd69b5a9a5379279:28 +b63216a8584c12bf002f4ee8c3b0ac3e:19 +1ad1cfa76f9692958656f17b710a0ecc:19 +ebf47efadc4c50002b13da0b1cef0646:18 +106de6ca4d0e49988f3328ce380ff517:18 +2f4aedd17528c30f8a9f16b030ff708b:18 +93aca156e3f15799914f868852723e47:18 +17cbc97604a781acb78c668b2437ffbd:18 +1709449e191fe131236bd0b8c1928b71:18 +1587f690fd5c0c886630334e89e397ff:18 +a6cf882fc575147513b7b1b505fdd335:18 +dc6ad4bf6892204b9fca73d1ee9cab04:18 +9ae673cfbb5d90688edafbf7ec2862e4:18 +acf250fcdbf54725219af8c6a8a614d9:18 +07319a58381faa784dda046d5e8fd8ae:18 +77e978f6318d01be894de8944fdc05b4:18 +81be9a78d26d25c6ff47c7e9af30a92e:18 +e87bf9d0084b39c443e421cdb5d3fc43:18 +9e973ee7f4f5ad967d2ba5bf5d88911c:18 +b47e7bbf50ad208599056f8d0f1cc5ba:18 +47a9e25289b4ece230adcc3b60797712:18 +42e4cc1882d334757afbe74f4d8cec8e:18 +024481748991b858bf7e03b0b87ce125:18 +b6ced7e04b527d83cd660894a0059ea7:18 +717e536c3a13f3602fbaf1f607ee2963:18 +18b540e31006aad6ae7a26d967ec0e0e:18 +f1483fe126d64bd7544dba7fdf0559f4:18 +3c4a0d8270a52e43316a711a2fe83268:18 +2e13bb1bfba423473a6efba65d9c8190:18 +47a5d9010bdeb561176cf070ef483e91:18 +4f2e7d32ed2c7ead316770fbe84d8aae:18 +c625e1090fc4d72afd0673839af5d46f:18 +4cc2dc623ee702db7a5899c5c401a98c:18 +eff51a62305642c720d163085061ed63:18 +780c7e0f80b5479d00e2c3679974644d:18 +69e416f1afc250d6239788b4b1fb9bb4:18 +6d60d78c1ce897131dcf45743a8d6e49:18 +e16e8dcca650cf79557cb76a86edb4e9:18 +d71a32697989404ae782e105a5eb5e4c:18 +bfebe4871ed9c4b5f3300fe8dfa2a0a3:18 +93e215f230901f20bdca460cfe8c42af:18 +8efacada80287ebb7607758a0a82b0a0:18 +487bc1228fd817b641056a1a008984f8:18 +dc6c2915cd6f0a6fd0ab01365349599d:18 +1a3dbf72e4bd135a621b8ad78a7151c1:18 +01fd60d48ba7e1434bb72696282c7113:18 +83c42b475763d64935a9ddfc6a374b64:18 +ed9a26aaf7ddb748bd1e624aeb4fdb81:18 +eeb05b41976f7bb96572330854aaab34:18 +3c17437ba337e7ce8edad092f745a363:18 +8d8fd1ffc3ae24cb43798f4eb41fc4de:18 +003f7ab7088710994a289bab815aacbf:18 +488d943cdcedfca2b076152769fc3c51:18 +cab779407e29ba3377e6a47a4f842ec5:18 +a0ddcf0f7bb4aa9d38db983941f6908d:18 +b64b41d26010783cae2dff88c7e21f1d:18 +aac6d054ad0f9bea1f64702aea698ee3:18 +90b4163293f9e35f7091692b31d3b5d7:18 +9d66fceb1054d5e2ff207591ec216b70:18 +93a65566b576af220207e30cc89ef490:18 +b3248e6a2d819bd8bb42a15179188c52:18 +f95f0545feb040d2d6bfcad6897364d0:18 +058ec3044e95ed7fcaff93a2f3105d6f:18 +39ef3516cb80752cac1c541b43a1c2fa:18 +c4b4a2994711cb342b0c8c8ef409ce8b:18 +719c3b73816fa0db79747ea264abab5a:18 +c1295d79d79340152c6b98f1c9003e8c:18 +1211e656a8eaa4423e7678533a1e7e2a:18 +1d7847b88e0feaecd299b3c5c18be763:18 +e0c7417a74df21b55367f9e1ea6c2d37:18 +e49b0a2bd830bb3b96934490b2bc6ebe:18 +91790d7c5cf146ab3936919184a70015:18 +ec36ea4bb9634f7ec5c4d3c1cc9dd71b:18 +860e7b1a68c02eef7d2e2d7b6a3e13b5:18 +458fda8d9863be24862bb62c910f427d:18 +e05ed0452c12e62ef74e9161159b1231:18 +9419764761c059b9c2f9cf640abfdeb0:18 +ea02175c4ef64b6370f619acd84f3c3c:18 +0b01d40e1ca37aa854e77c98cc153e77:18 +eb12f3e690b201aa171d794a8288af10:18 +b955e5418d490425f99d58f441ba4525:18 +34305e28da7351c9fe8a69e055e1c250:4 +f9a8c4364b7598dbeca0186f60e71ea6:4 +6a6e8a76be9fd95bf68f4be6f6cf1bbf:4 +14a880d95891af72380815d1c91baf73:4 +a2fa13ef7ba19d24adf59f8ecc12f0b4:4 +acd568e2592b223aaec17240031ed688:4 +cf994432aef1a88d7b82ffeecbbb96dc:4 +31927400c1d74c4dbcefe4c8c787ab8a:4 +94961af3e92bca38554a63bef2e453a6:4 +c562088538d99567be8dfdc54f830661:4 +459a7fef5f29ed6bafedadf927e315ee:4 +5c9d5847e56faea367acff998cb9e02b:4 +766fb7e580b7f7211bee8e77b98859b7:4 +53dc80d117986033f21a0b5f8609c87c:4 +3d8114f2ba5b5b51c547181a89a3466f:4 +89418ba487f62fbae3843e5dff5b9040:4 +ebe40ec90a872c67a1c27ae2c1eea950:4 +35462fa6c34cbb3610497e0e7ec5604a:4 +4f7b3ab75c9ad8d5b10fec60265a3809:4 +5123777f5c011dbd46ae38689b72a7d4:4 +38c98f315f4b01c1aa53a76a214a919e:4 +25989076c985cb6136599803fbcb48d5:4 +a96139fddc921f9c720e39737bfc1e23:4 +5cc99cf3cb2f9f5b2f3da2068a3c3b14:4 +9d57682c086fd2c37173c59f7df7f7f1:4 +bae7808504fe1ce016d86125d969287f:4 +b0767396a0f7f31b48ac96880c9cbf8b:4 +e2babe17ce0f4f5b30fee91c06c0e8fa:4 +d1c3eecefff445a312cfee0fa59d1431:4 +d5702e33cc1c8a04039657bec033a3e8:4 +1449b4ca79da7eaf4a3e04a2aa7b764d:4 +f50415b6582d0e25f6949d844bf93ee8:4 +3fef2395a8a091e7dea7a3b99dfeaea6:4 +03e302c7776c88610dafb333323263af:4 +b7d9936f2c7c87d2edac782f5403001b:4 +affb389d45f01bc18197bf0f49f4b118:4 +a4eb5a999085dfe2424b2896e38b25d1:4 +9cc9248b4494b77d7b40bbbf4dcef8b3:4 +043d915c3e3d58cb001a8b32deaffea3:4 +f9d3d4bc8a5944b63f51ae472e86813d:4 +a5e0393e11eadd75dd36fb5379ae018c:4 +5980bcaa38c00e4b837f2e9785bda3d2:4 +cc63328c0b60b9a790f8a1457dfaa511:4 +afdf18c7f6d8a437d84e390721b9b8b3:4 +b08cfcf9149aa4e94b5fb9695271c575:4 +10a9f2ae427efa8d37d59f435e83d62e:4 +d689449577a88d1b20d321d4e5fd32d9:4 +75298a5c8b84105fd9b9df7184dc110a:4 +5c8ed88fdd5d5ccb1ad7a61ecdf1ef9e:4 +35a4cc8262e2a04a80fc506d1551c2b7:4 +7ef43aabb95d2f3ad79438accc09cb4c:4 +aa6a754d71d091d666fea53f8769dfe9:4 +c8341c7fe17e7ae1484fdc25aef13df9:4 +4b6c6d5caf6e3d47ffd3dfdb329842f2:4 +a93c08a489709b0b6402127f3287a0a2:4 +500d600d630b1130b906355e694caf25:4 +e2f20c930af14f873866419d98bb33c5:4 +4f67d44d8a9c73b20f9dccb0fe708910:4 +3207a78e01966b63f857a845d312c5db:4 +dc772adf9d92e1330e1e6c1bc8bbdbea:4 +9a0f97a242e5b5c8bf70dd24775d973a:4 +c51604fe4030e75ce4ed3f545e470eee:4 +e8627b4481475f494ae99206e4d614a4:4 +a3abcd010f778136d77b5c00575faa93:4 +d806274a83ce72b89939c7e1fcc7dbf1:4 +e42535765b64f8f96c6a5b13c5aa30e9:4 +0389b0ce442166bc4298bd49f8ecfbff:4 +e6c5da69c2396e1a4c404a82509edb1e:4 +d62e86b3047119ec0941418ab6365bd4:4 +3c957e719e0ffce99b544fcc9a97ab3a:4 +638115e7b634086640ad6b300846ad6d:4 +f42bda5821df4a01dc97cb7d611a98a2:4 +15884ea4526d7af7c6dd5c5c89b81c97:4 +23fb4c5f959189ae9c90b968647934f3:4 +6b538e97c482ee0993607820484dcb08:4 +e7e718f70ee49560800db2d60270603f:11 +61c32de62fbec72e56083dade766f0f9:11 +f9684960dbadc5206f33484afeba57e1:11 +f849d1e2b1fbe25e1c88aa103ce68255:11 +1186fd5fd194d7f5c3c1b19aa753d315:11 +c74e6a2c593956c2e74a9f89dac73eeb:11 +e528c2b6258e260f575488c35f7683fb:11 +e5c61babf2a6ff8f6939b7a1db7e621f:11 +48921ae81d5ad84778fd75c9f58e1bf9:11 +92e7e194987c3b4db1a4a129af2cd05f:11 +f1d93cdeba77ba27e048fd0fbc6b0bad:11 +2cb543910592d445cf09fec159d0b421:11 +cd3bfb7c471b99eb7b7acb8011e4687f:11 +df936e8a13b49cdb9b5f82e150aca29b:11 +0e452471f905f2fb311c6146b31b5705:11 +b78f4f3edd36b6326873eea475ef2476:11 +fa0bc15e6c1bcb787bdea1073a4e2709:11 +417ebe49420a8d103b8b65c5d574fd84:11 +18f7890363dbd6262668cdcffb0f3a8d:11 +28c73ab584871ffb89a5304f87335f95:11 +aa4a8bfad0ec9e57489297bc67664cfd:11 +9a5dd5c667e21679c42fc7b2cd79769c:11 +59654e2c6df1b6a8a77928223e2ab332:11 +fea47d75f1c8cdb3a12de27678c005b6:11 +c6dd561601e904cee3707ba4cfe1350f:11 +02ceeb351445bd756a73fd5ab2a3d0c4:11 +3a3e699a3fdaaaf0b0d809d114c54e19:11 +ccad7981fb017550a8f7b8925a1d0648:11 +0803d11e374ba9493cfdd8fc17f7d34c:11 +31b7ca8caafc4ac8fd6ab18a01f5e9f5:11 +762dbaa4b1beab4b7113a750639d7f6f:11 +cf1b9fa0beb54f8f57c79446a3186baa:11 +13c8db7cad8e47068720c9f9b9d8213c:11 +d942884e00b9f288de96a8b0733372d9:11 +cd5c01104e0c46080a8d499f87de4991:11 +d8c43560e159bcb899c2180b8e01d8e3:24 +ae9c7aee12dfaaa56e43834dcea91f5f:24 +12233f83b06081671815562dce938d3b:24 +106252e3f31ee17239e1ee4184147deb:24 +1fb2cd38813bad6e5eeef74bcbcb133d:24 +c5bbd4174eee5cb68830801317071dd5:24 +b876313caea388f03200ac720ed528af:24 +f4fa53a97c83899fd2da9f4112f35339:24 +dc32c09ecdd9bda4703aa944cf1eb4ca:24 +07a59dae4166f6d504bb0793c5e75178:24 +efcb5024ce3514453d44735222903a55:24 +43445dc9e50e2c4fc327097d07ff749a:24 +bcb1787dd2def454fd3f31d1b2c0967a:24 +cace738770550444ee1f91000c32c147:24 +2cc65559141cece33ffdbc4e34a2ce3b:24 +b55a09768f05f29b15e1ff3a9feb7c92:24 +54282406a85f4410d4dfc72d5873ee96:24 +1c38913c447705e315ec4486da558c9d:24 +ca3f30645fa7b480b213f4228386a09a:24 +6d8800c5cc8ad9e4f3f28e777207a870:24 +b8dc6f9b9587159f978d643aeb8e4d07:24 +44c0d27dc28d4da28655c3c62a0d7cac:24 +91eb697571f68f5850875286c5e36080:24 +ae908f7abd7d347b0bf39fead3531c12:24 +d27ceaaa92270c450aeee582cc17b985:24 +145e730e204fdca9cf169521a3812ed1:24 +294141fdc8f17ac3c7e4d8687d8ad389:24 +cfde9ea95c663bed6d56df8778b01b79:24 +8569110aa82eb44ff0b12819867c66f8:24 +48de820418038280f70eca64f878c67b:24 +f389ac23dc4ef6641321adf43d712c32:24 +ef4661e0074b431762177e10c2ac8570:24 +46de27224f4cf6fb745e30bbd6aad267:24 +75c79913d4df6b467a67c40ce67a32fc:24 +2a000aca1e17b3b0343ddf151ee313f4:24 +e9d59e0325c6dd1dd78a998365435fac:24 +0a1d88442fc34d0081115d949f1c5fa9:24 +0a7ef5f8b75fa2dd79787b03dff923c9:24 +b247285b58ca44aad532b1c3b5a5dacf:24 +63f61697ff1319a76632338fe0c13ae9:24 +e8df9edc7e29252645d2b8a6a55c4e0f:24 +aaba729f45b1617d59cec26aa41937e3:24 +7e7ae6228d4ab54b90f0f580731941d7:24 +de1c84862deb139962240505133690f0:24 +9660da0a81f885de5a3b16f1f07a1067:24 +9dbabf8883b7a4e120fcbceb00face40:24 +c3f5202b30c04801da693013368a0da2:24 +b73113e1f7c2702cfd997dd84e5ac003:24 +9b5946e54278b49302da6ccd2a3a06e1:24 +c7a5a5244ef8d55b160e29df7e310b76:24 +74fa889a011613ee983f45aed7dcb656:24 +ccb57b751fd0ed3f7244a0e096b19076:24 +dcd61dd6bd1e0fec6888029656162559:24 +c2fa9ac71ea25473f3c384e5d3c3770b:24 +43de03bf819ac106a9a66f3d7b473bd1:24 +3ed4b5f668130ecec1aadb21bf0a671a:24 +ba4d4fc3c411bf5b477323a1329574b3:24 +4e82fa0797beeab61a7a0b9b3ba7505e:24 +1cff05a0b4291e8f289a1794f016eab5:24 +11e854ed754739797dbae75ff3159e82:24 +456c3251cceb566a82d3175390b6a663:24 +76fd080fdaa80390ca05399f4d0b49ae:24 +2daef0ca0e135ad904bf7e931690cf2b:24 +d49bee1da6282ff1efd4017e2c26ff55:24 +94bf636e74064a1f45c5e81777a8b541:24 +b502fdd440592e2d637704ebef9baf01:24 +67395fd629dae7b237421aa91bc12150:24 +b69dd3d2f26fc766f3b4193b089fa71e:24 +5a13a7fac55b3378629744099623199b:24 +6c21462d7f8529e6d017e054b02d96d3:24 +ef1a937ed3ed1277a46cbbfb589bcc7c:24 +a9446b05afd48f274f6469e9617dadde:24 +03b1c2c2b9e04b66ace80c8d891dad71:24 +5af3ef227d419594dc0316392c0fd762:24 +b4146bc53ba79957adf0bf7306b2212c:24 +f1136465059f10586c4fb8379126e010:24 +8796edfd2200b66560e180ecf2c8bd3a:24 +b36bec8fd49158c020b6d8c09cc02baf:26 +799e6e8b1fe3f2c45ec9703491bd67d5:8 +12011f7bb75e99008d3ef5bbe9087e6f:8 +53737c53af7f40961557a85fb4d11b55:8 +9fac9097934ab48cd9de3d124943c3fd:8 +a85598686e913ff9a84c078990e2b74f:8 +9389bbac5d37c3655c2d112b5fb15d79:8 +c7143cf932c4620b38f241a67c698de7:8 +6bb42f291857bf93ef2407e3fe8910b3:8 +d5360a8c6fd960a52c125ad6ee65b369:8 +4c0513c3ea8aa4e0d1671697ade55dc0:8 +9ab999023c47266804e1c23912c49590:8 +c83ba14b99739545b5611680823c5e64:8 +6c3b66ed41f23693ee6ed1a2f74383ca:8 +e064938f6319932b4685797aa810233f:8 +edf457cbc465fa576479f66ed5689a8e:8 +befe448f726cd55c55d708a8ed55b087:8 +4a35639e3583bbc1b36b56f0b50a2849:8 +a2c469af732e034c5017e1fbcbb6fba0:8 +47ef9cb8fb8fa55cfe73612c56fc5a0d:8 +58e29b4d0c775a250e2751d3cb70e44b:8 +2248103eacf690b2060d9f624dee5e41:8 +7aaff576c6f4f28ee8d80236c3943c3a:8 +62068b685c80a1da76ef1824af304e47:5 +beef3d82791567a66852903d9acde2d8:14 +8f8e727dba622a81127060f53efbaf60:14 +a58ec4e669821ad885c5e539bea8f8c0:14 +c183aebc8596eae7dbaf32f831d4bf1a:14 +579a40bd08705acc7099b631706917cd:14 +c979623104ac1df2ff0b56f2eb8f40a3:14 +4a406313d3d1fc7c56f2e4ae88033ef5:14 +0534bb586552faf59923fcbb66c5c9a8:14 +027b76c362f10b646fce08dd34457533:29
diff --git a/src/main/resources/api_database/api_database_api_level.ser b/src/main/resources/api_database/api_database_api_level.ser new file mode 100644 index 0000000..e9e539a --- /dev/null +++ b/src/main/resources/api_database/api_database_api_level.ser Binary files differ
diff --git a/src/main/resources/api_database/api_database_hash_lookup.ser b/src/main/resources/api_database/api_database_hash_lookup.ser new file mode 100644 index 0000000..42a3b61 --- /dev/null +++ b/src/main/resources/api_database/api_database_hash_lookup.ser Binary files differ
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java index 534a42c..7996dc4 100644 --- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java +++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -42,6 +42,14 @@ return is(compilerVersion) && this.targetVersion == targetVersion; } + public boolean isNewerThanOrEqualTo(KotlinCompilerVersion otherVersion) { + return kotlinc.getCompilerVersion().isGreaterThanOrEqualTo(otherVersion); + } + + public boolean isOlderThan(KotlinCompilerVersion otherVersion) { + return !isNewerThanOrEqualTo(otherVersion); + } + public boolean isFirst() { return index == 0; }
diff --git a/src/test/java/com/android/tools/r8/L8TestRunResult.java b/src/test/java/com/android/tools/r8/L8TestRunResult.java index a9a9a78..8f6bab8 100644 --- a/src/test/java/com/android/tools/r8/L8TestRunResult.java +++ b/src/test/java/com/android/tools/r8/L8TestRunResult.java
@@ -49,6 +49,12 @@ } @Override + public <E extends Throwable> L8TestRunResult inspectFailure( + ThrowingConsumer<CodeInspector, E> consumer) { + throw new Unimplemented(); + } + + @Override public L8TestRunResult disassemble() throws IOException, ExecutionException { throw new Unimplemented(); }
diff --git a/src/test/java/com/android/tools/r8/MarkerMatcher.java b/src/test/java/com/android/tools/r8/MarkerMatcher.java index d766770..22610d3 100644 --- a/src/test/java/com/android/tools/r8/MarkerMatcher.java +++ b/src/test/java/com/android/tools/r8/MarkerMatcher.java
@@ -179,6 +179,21 @@ }; } + public static Matcher<Marker> markerPgMapId(Matcher<String> predicate) { + return new MarkerMatcher() { + @Override + protected boolean eval(Marker marker) { + return predicate.matches(marker.getPgMapId()); + } + + @Override + protected void explain(Description description) { + description.appendText("with pg_map_id matching "); + predicate.describeTo(description); + } + }; + } + public static Matcher<Marker> markerR8Mode(String r8Mode) { return new MarkerMatcher() { @Override
diff --git a/src/test/java/com/android/tools/r8/ProguardTestRunResult.java b/src/test/java/com/android/tools/r8/ProguardTestRunResult.java index 3c2bbfc..c5cccbe 100644 --- a/src/test/java/com/android/tools/r8/ProguardTestRunResult.java +++ b/src/test/java/com/android/tools/r8/ProguardTestRunResult.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.utils.ThrowingConsumer; import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.io.IOException; -import java.util.concurrent.ExecutionException; public class ProguardTestRunResult extends SingleTestRunResult<ProguardTestRunResult> { @@ -34,14 +33,8 @@ return super.getStackTrace().retrace(proguardMap); } - public StackTrace getOriginalStackTrace() { - return super.getStackTrace(); - } - @Override - public CodeInspector inspector() throws IOException, ExecutionException { - // See comment in base class. - assertSuccess(); + protected CodeInspector internalGetCodeInspector() throws IOException { assertNotNull(app); return new CodeInspector(app, proguardMap); }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java index 08dd682..5d3c306 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -204,7 +204,7 @@ b -> b.addProguardConfiguration( getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaringnplus")) .run(); } @@ -244,7 +244,7 @@ b -> b.addProguardConfiguration( getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 0, "lambdadesugaringnplus")) .run(); }
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java index b247acf..08bb64b 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -17,7 +17,6 @@ import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization; import com.android.tools.r8.utils.TestDescriptionWatcher; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -33,7 +32,9 @@ public abstract class R8RunExamplesCommon { protected enum Input { - DX, JAVAC, JAVAC_ALL, JAVAC_NONE + JAVAC, + JAVAC_ALL, + JAVAC_NONE } protected enum Output { @@ -90,8 +91,6 @@ private Path getInputFile() { switch(input) { - case DX: - return getOriginalDexFile(); case JAVAC: return getOriginalJarFile(""); case JAVAC_ALL: @@ -104,12 +103,7 @@ } public R8Command.Builder addInputFile(R8Command.Builder builder) { - if (input == Input.DX) { - // If input is DEX code, use the tool helper to add the DEX sources as R8 disallows them. - ToolHelper.getAppBuilder(builder).addProgramFiles(getOriginalDexFile()); - } else { - builder.addProgramFiles(getInputFile()); - } + builder.addProgramFiles(getInputFile()); return builder; } @@ -121,10 +115,6 @@ return Paths.get(getExampleDir(), pkg, ToolHelper.DEFAULT_DEX_FILENAME); } - private DexTool getTool() { - return input == Input.DX ? DexTool.DX : DexTool.NONE; - } - @Rule public ExpectedException thrown = ExpectedException.none(); @@ -171,10 +161,6 @@ } private boolean shouldCompileFail() { - if (input == Input.DX && getFailingCompileDxToDex().contains(mainClass)) { - assert output == Output.DEX; - return true; - } if (output == Output.CF && getFailingCompileCf().contains(mainClass)) { return true; } @@ -212,7 +198,7 @@ TestCondition condition = output == Output.CF ? getFailingRunCf().get(mainClass) : getFailingRun().get(mainClass); - if (condition != null && condition.test(getTool(), compiler, vm.getVersion(), mode)) { + if (condition != null && condition.test(DexTool.NONE, compiler, vm.getVersion(), mode)) { thrown.expect(Throwable.class); } @@ -248,12 +234,12 @@ private boolean shouldMatchJVMOutput(DexVm.Version version) { TestCondition condition = getOutputNotIdenticalToJVMOutput().get(mainClass); - return condition == null || !condition.test(getTool(), compiler, version, mode); + return condition == null || !condition.test(DexTool.NONE, compiler, version, mode); } private boolean shouldSkipVm(DexVm.Version version) { TestCondition condition = getSkip().get(mainClass); - return condition != null && condition.test(getTool(), compiler, version, mode); + return condition != null && condition.test(DexTool.NONE, compiler, version, mode); } protected abstract String getExampleDir(); @@ -264,10 +250,6 @@ protected abstract Set<String> getFailingCompileCfToDex(); - protected Set<String> getFailingCompileDxToDex() { - return ImmutableSet.of(); - } - // TODO(mathiasr): Add CompilerSet for CfToDex so we can fold this into getFailingRun(). protected abstract Set<String> getFailingRunCfToDex();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java index d8a62da..922f2d3 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -95,7 +95,6 @@ makeTest(Input.JAVAC_ALL, CompilerUnderTest.R8, CompilationMode.RELEASE, test)); fullTestList.add( makeTest(Input.JAVAC_ALL, CompilerUnderTest.R8, CompilationMode.DEBUG, test)); - fullTestList.add(makeTest(Input.DX, CompilerUnderTest.R8, CompilationMode.RELEASE, test)); fullTestList.add( makeTest( Input.JAVAC_ALL, CompilerUnderTest.R8, CompilationMode.RELEASE, test, Output.CF)); @@ -158,12 +157,6 @@ } @Override - protected Set<String> getFailingCompileDxToDex() { - return new ImmutableSet.Builder<String>() - .build(); - } - - @Override protected Set<String> getFailingCompileCf() { return new ImmutableSet.Builder<String>() .build();
diff --git a/src/test/java/com/android/tools/r8/R8TestRunResult.java b/src/test/java/com/android/tools/r8/R8TestRunResult.java index 3ef4901..716971d 100644 --- a/src/test/java/com/android/tools/r8/R8TestRunResult.java +++ b/src/test/java/com/android/tools/r8/R8TestRunResult.java
@@ -10,7 +10,6 @@ import com.android.tools.r8.naming.retrace.StackTrace; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.ThrowingBiConsumer; -import com.android.tools.r8.utils.ThrowingConsumer; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.graphinspector.GraphInspector; import java.io.IOException; @@ -52,10 +51,6 @@ return super.getStackTrace().retraceAllowExperimentalMapping(proguardMap); } - public StackTrace getOriginalStackTrace() { - return super.getStackTrace(); - } - @Override protected CodeInspector internalGetCodeInspector() throws IOException { assertNotNull(app); @@ -63,12 +58,6 @@ } public <E extends Throwable> R8TestRunResult inspectOriginalStackTrace( - ThrowingConsumer<StackTrace, E> consumer) throws E { - consumer.accept(getOriginalStackTrace()); - return self(); - } - - public <E extends Throwable> R8TestRunResult inspectOriginalStackTrace( ThrowingBiConsumer<StackTrace, CodeInspector, E> consumer) throws E, IOException { consumer.accept(getOriginalStackTrace(), internalGetCodeInspector()); return self();
diff --git a/src/test/java/com/android/tools/r8/SanityCheck.java b/src/test/java/com/android/tools/r8/SanityCheck.java index a7b1990..df1486c 100644 --- a/src/test/java/com/android/tools/r8/SanityCheck.java +++ b/src/test/java/com/android/tools/r8/SanityCheck.java
@@ -12,12 +12,14 @@ import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.utils.ZipUtils; +import com.google.common.collect.Sets; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Enumeration; +import java.util.Set; import java.util.function.Predicate; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -45,6 +47,11 @@ } boolean licenseSeen = false; final Enumeration<? extends ZipEntry> entries = zipFile.entries(); + Set<String> apiDatabaseFiles = + Sets.newHashSet( + "api_database/api_database_ambiguous.txt", + "api_database/api_database_api_level.ser", + "api_database/api_database_hash_lookup.ser"); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); @@ -56,12 +63,16 @@ licenseSeen = true; } else if (entryTester.test(name)) { // Allow. + } else if (apiDatabaseFiles.contains(name)) { + // Allow all api database files. + apiDatabaseFiles.remove(name); } else if (name.endsWith("/")) { assertTrue("Unexpected directory entry in" + jar, allowDirectories); } else { fail("Unexpected entry '" + name + "' in " + jar); } } + assertTrue(apiDatabaseFiles.isEmpty()); assertTrue("No LICENSE entry found in " + jar, licenseSeen); }
diff --git a/src/test/java/com/android/tools/r8/SingleTestRunResult.java b/src/test/java/com/android/tools/r8/SingleTestRunResult.java index 17546ee..726acbd 100644 --- a/src/test/java/com/android/tools/r8/SingleTestRunResult.java +++ b/src/test/java/com/android/tools/r8/SingleTestRunResult.java
@@ -60,6 +60,10 @@ } public StackTrace getStackTrace() { + return getOriginalStackTrace(); + } + + public StackTrace getOriginalStackTrace() { if (runtime.isDex()) { return StackTrace.extractFromArt(getStdErr(), runtime.asDex().getVm()); } else { @@ -129,6 +133,12 @@ return self(); } + public <E extends Throwable> RR inspectOriginalStackTrace( + ThrowingConsumer<StackTrace, E> consumer) throws E { + consumer.accept(getOriginalStackTrace()); + return self(); + } + public RR disassemble(PrintStream ps) throws IOException, ExecutionException { ToolHelper.disassemble(app, ps); return self();
diff --git a/src/test/java/com/android/tools/r8/TestRunResult.java b/src/test/java/com/android/tools/r8/TestRunResult.java index 90d4f75..bd5ad83 100644 --- a/src/test/java/com/android/tools/r8/TestRunResult.java +++ b/src/test/java/com/android/tools/r8/TestRunResult.java
@@ -38,6 +38,9 @@ public abstract <E extends Throwable> RR inspect(ThrowingConsumer<CodeInspector, E> consumer) throws IOException, ExecutionException, E; + public abstract <E extends Throwable> RR inspectFailure( + ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E; + public abstract RR disassemble() throws IOException, ExecutionException; public <E extends Throwable> RR apply(ThrowingConsumer<RR, E> fn) throws E {
diff --git a/src/test/java/com/android/tools/r8/TestRunResultCollection.java b/src/test/java/com/android/tools/r8/TestRunResultCollection.java index e166be4..c5421ac 100644 --- a/src/test/java/com/android/tools/r8/TestRunResultCollection.java +++ b/src/test/java/com/android/tools/r8/TestRunResultCollection.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8; +import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.ThrowingConsumer; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -57,6 +58,12 @@ return inspectIf(Predicates.alwaysTrue(), consumer); } + @Override + public <E extends Throwable> RR inspectFailure(ThrowingConsumer<CodeInspector, E> consumer) + throws IOException, E { + throw new Unimplemented(); + } + public RR applyIf(Predicate<C> filter, Consumer<TestRunResult<?>> thenConsumer) { return applyIf(filter, thenConsumer, r -> {}); }
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index 56a2b80..0d02a13 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -90,6 +90,7 @@ static final Path[] EMPTY_PATH = {}; public static final String SOURCE_DIR = "src/main/java/"; + public static final String RESOURCES_DIR = "src/main/resources/"; public static final String BUILD_DIR = "build/"; public static final String GENERATED_TEST_BUILD_DIR = BUILD_DIR + "generated/test/"; public static final String LIBS_DIR = BUILD_DIR + "libs/";
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java new file mode 100644 index 0000000..d7db635 --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -0,0 +1,241 @@ +// 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.apimodel; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.androidapi.AndroidApiLevelHashingDatabaseImpl; +import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexMember; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexReference; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.references.ClassReference; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.Pair; +import com.android.tools.r8.utils.structural.DefaultHashingVisitor; +import com.android.tools.r8.utils.structural.HasherWrapper; +import com.android.tools.r8.utils.structural.StructuralItem; +import java.io.FileOutputStream; +import java.io.ObjectOutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; + +public class AndroidApiHashingDatabaseBuilderGenerator extends TestBase { + + /** + * Generate the information needed for looking up api level of references in the android.jar. This + * method will generate three different files and store them in the passed paths. pathToIndices + * will be an int array with hashcode entries for each DexReference. The pathToApiLevels is a byte + * array with a byte describing the api level that the index in pathToIndices has. To ensure that + * this lookup work the generate algorithm tracks all colliding hash codes such and stores them in + * another format. The indices map is populated with all colliding entries and a -1 is inserted + * for the api level. + */ + public static void generate( + List<ParsedApiClass> apiClasses, + Path pathToIndices, + Path pathToApiLevels, + Path ambiguousDefinitions) + throws Exception { + Map<ClassReference, Map<DexMethod, AndroidApiLevel>> methodMap = new HashMap<>(); + Map<ClassReference, Map<DexField, AndroidApiLevel>> fieldMap = new HashMap<>(); + Map<ClassReference, ParsedApiClass> lookupMap = new HashMap<>(); + DexItemFactory factory = new DexItemFactory(); + + Map<Integer, AndroidApiLevel> apiLevelMap = new HashMap<>(); + Map<Integer, Pair<DexReference, AndroidApiLevel>> reverseMap = new HashMap<>(); + Map<AndroidApiLevel, Set<DexReference>> ambiguousMap = new HashMap<>(); + // Populate maps for faster lookup. + for (ParsedApiClass apiClass : apiClasses) { + DexType type = factory.createType(apiClass.getClassReference().getDescriptor()); + AndroidApiLevel existing = apiLevelMap.put(type.hashCode(), apiClass.getApiLevel()); + assert existing == null; + reverseMap.put(type.hashCode(), Pair.create(type, apiClass.getApiLevel())); + } + + for (ParsedApiClass apiClass : apiClasses) { + Map<DexMethod, AndroidApiLevel> methodsForApiClass = new HashMap<>(); + apiClass.visitMethodReferences( + (apiLevel, methods) -> { + methods.forEach( + method -> methodsForApiClass.put(factory.createMethod(method), apiLevel)); + }); + Map<DexField, AndroidApiLevel> fieldsForApiClass = new HashMap<>(); + apiClass.visitFieldReferences( + (apiLevel, fields) -> { + fields.forEach(field -> fieldsForApiClass.put(factory.createField(field), apiLevel)); + }); + methodMap.put(apiClass.getClassReference(), methodsForApiClass); + fieldMap.put(apiClass.getClassReference(), fieldsForApiClass); + lookupMap.put(apiClass.getClassReference(), apiClass); + } + + BiConsumer<DexReference, AndroidApiLevel> addConsumer = + addReferenceToMaps(apiLevelMap, reverseMap, ambiguousMap); + + for (ParsedApiClass apiClass : apiClasses) { + computeAllReferencesInHierarchy( + lookupMap, + factory, + factory.createType(apiClass.getClassReference().getDescriptor()), + apiClass, + AndroidApiLevel.B, + new IdentityHashMap<>()) + .forEach(addConsumer); + } + + int[] indices = new int[apiLevelMap.size()]; + byte[] apiLevel = new byte[apiLevelMap.size()]; + ArrayList<Integer> integers = new ArrayList<>(apiLevelMap.keySet()); + for (int i = 0; i < integers.size(); i++) { + indices[i] = integers.get(i); + AndroidApiLevel androidApiLevel = apiLevelMap.get(integers.get(i)); + apiLevel[i] = + (byte) (androidApiLevel == AndroidApiLevel.NOT_SET ? -1 : androidApiLevel.getLevel()); + } + + try (FileOutputStream fileOutputStream = new FileOutputStream(pathToIndices.toFile()); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { + objectOutputStream.writeObject(indices); + } + + try (FileOutputStream fileOutputStream = new FileOutputStream(pathToApiLevels.toFile()); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) { + objectOutputStream.writeObject(apiLevel); + } + + String ambiguousMapSerialized = serializeAmbiguousMap(ambiguousMap); + Files.write(ambiguousDefinitions, ambiguousMapSerialized.getBytes(StandardCharsets.UTF_8)); + } + + /** This will serialize a collection of DexReferences with the api level they correspond to. */ + private static String serializeAmbiguousMap( + Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) { + Set<String> seen = new HashSet<>(); + StringBuilder sb = new StringBuilder(); + ambiguousMap.forEach( + (apiLevel, references) -> { + references.forEach( + reference -> { + HasherWrapper defaultHasher = AndroidApiLevelHashingDatabaseImpl.getDefaultHasher(); + reference.accept( + type -> DefaultHashingVisitor.run(type, defaultHasher, DexType::acceptHashing), + field -> + DefaultHashingVisitor.run( + field, defaultHasher, StructuralItem::acceptHashing), + method -> + DefaultHashingVisitor.run( + method, defaultHasher, StructuralItem::acceptHashing)); + String referenceHash = defaultHasher.hash().toString(); + if (!seen.add(referenceHash)) { + throw new RuntimeException( + "More than one item with key: <" + + referenceHash + + ">. The choice of key encoding will need to change to generate a valid" + + " api database"); + } + sb.append(referenceHash).append(":").append(apiLevel.getLevel()).append("\n"); + }); + }); + return sb.toString(); + } + + private static BiConsumer<DexReference, AndroidApiLevel> addReferenceToMaps( + Map<Integer, AndroidApiLevel> apiLevelMap, + Map<Integer, Pair<DexReference, AndroidApiLevel>> reverseMap, + Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) { + return ((reference, apiLevel) -> { + AndroidApiLevel existingMethod = apiLevelMap.put(reference.hashCode(), apiLevel); + if (existingMethod != null) { + apiLevelMap.put(reference.hashCode(), AndroidApiLevel.NOT_SET); + Pair<DexReference, AndroidApiLevel> existingPair = reverseMap.get(reference.hashCode()); + addAmbiguousEntry(existingPair.getSecond(), existingPair.getFirst(), ambiguousMap); + addAmbiguousEntry(apiLevel, reference, ambiguousMap); + } else { + reverseMap.put(reference.hashCode(), Pair.create(reference, apiLevel)); + } + }); + } + + private static void addAmbiguousEntry( + AndroidApiLevel apiLevel, + DexReference reference, + Map<AndroidApiLevel, Set<DexReference>> ambiguousMap) { + ambiguousMap.computeIfAbsent(apiLevel, ignored -> new HashSet<>()).add(reference); + } + + @SuppressWarnings("unchecked") + private static <T extends DexMember<?, ?>> + Map<T, AndroidApiLevel> computeAllReferencesInHierarchy( + Map<ClassReference, ParsedApiClass> lookupMap, + DexItemFactory factory, + DexType holder, + ParsedApiClass apiClass, + AndroidApiLevel linkLevel, + Map<T, AndroidApiLevel> additionMap) { + if (!apiClass.getClassReference().getDescriptor().equals(factory.objectDescriptor.toString())) { + apiClass.visitMethodReferences( + (apiLevel, methodReferences) -> { + methodReferences.forEach( + methodReference -> { + T member = (T) factory.createMethod(methodReference).withHolder(holder, factory); + addIfNewOrApiLevelIsLower(linkLevel, additionMap, apiLevel, member); + }); + }); + apiClass.visitFieldReferences( + (apiLevel, fieldReferences) -> { + fieldReferences.forEach( + fieldReference -> { + T member = (T) factory.createField(fieldReference).withHolder(holder, factory); + addIfNewOrApiLevelIsLower(linkLevel, additionMap, apiLevel, member); + }); + }); + apiClass.visitSuperType( + (superType, apiLevel) -> { + computeAllReferencesInHierarchy( + lookupMap, + factory, + holder, + lookupMap.get(superType), + linkLevel.max(apiLevel), + additionMap); + }); + apiClass.visitInterface( + (iFace, apiLevel) -> { + computeAllReferencesInHierarchy( + lookupMap, + factory, + holder, + lookupMap.get(iFace), + linkLevel.max(apiLevel), + additionMap); + }); + } + return additionMap; + } + + private static <T extends DexMember<?, ?>> void addIfNewOrApiLevelIsLower( + AndroidApiLevel linkLevel, + Map<T, AndroidApiLevel> additionMap, + AndroidApiLevel apiLevel, + T member) { + AndroidApiLevel currentApiLevel = apiLevel.max(linkLevel); + AndroidApiLevel existingApiLevel = additionMap.get(member); + if (existingApiLevel == null || currentApiLevel.isLessThanOrEqualTo(existingApiLevel)) { + additionMap.put(member, currentApiLevel); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java new file mode 100644 index 0000000..8a0c060 --- /dev/null +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -0,0 +1,198 @@ +// 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.apimodel; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.androidapi.AndroidApiLevelHashingDatabaseImpl; +import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass; +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.utils.AndroidApiLevel; +import com.android.tools.r8.utils.IntBox; +import com.google.common.collect.ImmutableList; +import java.io.FileInputStream; +import java.io.ObjectInputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class AndroidApiHashingDatabaseBuilderGeneratorTest extends TestBase { + + protected final TestParameters parameters; + private static final Path API_VERSIONS_XML = + Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "api-versions", "api-versions.xml"); + private static final Path API_DATABASE_HASH_LOOKUP = + Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_hash_lookup.ser"); + private static final Path API_DATABASE_API_LEVEL = + Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_api_level.ser"); + private static final Path API_DATABASE_AMBIGUOUS = + Paths.get(ToolHelper.RESOURCES_DIR, "api_database", "api_database_ambiguous.txt"); + private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.R; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public AndroidApiHashingDatabaseBuilderGeneratorTest(TestParameters parameters) { + this.parameters = parameters; + } + + private static class GenerateDatabaseResourceFilesResult { + + private final Path indices; + private final Path apiLevels; + private final Path ambiguous; + + public GenerateDatabaseResourceFilesResult(Path indices, Path apiLevels, Path ambiguous) { + this.indices = indices; + this.apiLevels = apiLevels; + this.ambiguous = ambiguous; + } + } + + private static GenerateDatabaseResourceFilesResult generateResourcesFiles() throws Exception { + return generateResourcesFiles( + AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL)); + } + + private static GenerateDatabaseResourceFilesResult generateResourcesFiles( + List<ParsedApiClass> apiClasses) throws Exception { + TemporaryFolder temp = new TemporaryFolder(); + temp.create(); + Path indices = temp.newFile("indices.ser").toPath(); + Path apiLevels = temp.newFile("apiLevels.ser").toPath(); + Path ambiguous = temp.newFile("ambiguous.ser").toPath(); + AndroidApiHashingDatabaseBuilderGenerator.generate(apiClasses, indices, apiLevels, ambiguous); + return new GenerateDatabaseResourceFilesResult(indices, apiLevels, ambiguous); + } + + @Test + public void testCanParseApiVersionsXml() throws Exception { + // This tests makes a rudimentary check on the number of classes, fields and methods in + // api-versions.xml to ensure that the runtime tests do not vacuously succeed. + List<ParsedApiClass> parsedApiClasses = + AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL); + IntBox numberOfFields = new IntBox(0); + IntBox numberOfMethods = new IntBox(0); + parsedApiClasses.forEach( + apiClass -> { + apiClass.visitFieldReferences( + ((apiLevel, fieldReferences) -> { + fieldReferences.forEach(field -> numberOfFields.increment()); + })); + apiClass.visitMethodReferences( + ((apiLevel, methodReferences) -> { + methodReferences.forEach(field -> numberOfMethods.increment()); + })); + }); + // These numbers will change when updating api-versions.xml + assertEquals(4742, parsedApiClasses.size()); + assertEquals(25144, numberOfFields.get()); + assertEquals(38661, numberOfMethods.get()); + } + + @Test + public void testDatabaseGenerationUpToDate() throws Exception { + GenerateDatabaseResourceFilesResult result = generateResourcesFiles(); + TestBase.filesAreEqual(result.indices, API_DATABASE_HASH_LOOKUP); + TestBase.filesAreEqual(result.apiLevels, API_DATABASE_API_LEVEL); + TestBase.filesAreEqual(result.ambiguous, API_DATABASE_AMBIGUOUS); + } + + @Test + public void initializeApiDatabaseTimeTest() { + DexItemFactory factory = new DexItemFactory(); + long start = System.currentTimeMillis(); + new AndroidApiLevelHashingDatabaseImpl(factory, ImmutableList.of()); + long end = System.currentTimeMillis(); + long timeSpan = end - start; + assertTrue("Time used was " + timeSpan, timeSpan < 100); + } + + @Test + public void testCanLookUpAllParsedApiClassesAndMembers() throws Exception { + List<ParsedApiClass> parsedApiClasses = + AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL); + DexItemFactory factory = new DexItemFactory(); + AndroidApiLevelHashingDatabaseImpl androidApiLevelDatabase = + new AndroidApiLevelHashingDatabaseImpl(factory, ImmutableList.of()); + parsedApiClasses.forEach( + parsedApiClass -> { + DexType type = factory.createType(parsedApiClass.getClassReference().getDescriptor()); + AndroidApiLevel apiLevel = androidApiLevelDatabase.getTypeApiLevel(type); + assertEquals(parsedApiClass.getApiLevel(), apiLevel); + parsedApiClass.visitMethodReferences( + (methodApiLevel, methodReferences) -> + methodReferences.forEach( + methodReference -> { + DexMethod method = factory.createMethod(methodReference); + androidApiLevelDatabase + .getMethodApiLevel(method) + .isLessThanOrEqualTo(methodApiLevel); + })); + parsedApiClass.visitFieldReferences( + (fieldApiLevel, fieldReferences) -> + fieldReferences.forEach( + fieldReference -> { + DexField field = factory.createField(fieldReference); + androidApiLevelDatabase + .getFieldApiLevel(field) + .isLessThanOrEqualTo(fieldApiLevel); + })); + }); + } + + /** + * Main entry point for building a database over references in framework to the api level they + * were introduced. Running main will generate a new jar and run tests on it to ensure it is + * compatible with R8 sources and works as expected. + * + * <p>The generated jar depends on r8NoManifestWithoutDeps. + * + * <p>If the generated jar passes tests it will be moved to third_party/android_jar/api-database/ + * and override the current file in there. + */ + public static void main(String[] args) throws Exception { + List<ParsedApiClass> parsedApiClasses = + AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL); + GenerateDatabaseResourceFilesResult result = generateResourcesFiles(parsedApiClasses); + verifyNoDuplicateHashes(result.indices); + Files.move(result.indices, API_DATABASE_HASH_LOOKUP, REPLACE_EXISTING); + Files.move(result.apiLevels, API_DATABASE_API_LEVEL, REPLACE_EXISTING); + Files.move(result.ambiguous, API_DATABASE_AMBIGUOUS, REPLACE_EXISTING); + } + + private static void verifyNoDuplicateHashes(Path indicesPath) throws Exception { + Set<Integer> elements = new HashSet<>(); + int[] indices; + try (FileInputStream fileInputStream = new FileInputStream(indicesPath.toFile()); + ObjectInputStream indicesObjectStream = new ObjectInputStream(fileInputStream)) { + indices = (int[]) indicesObjectStream.readObject(); + for (int index : indices) { + assertTrue(elements.add(index)); + } + } + assertEquals(elements.size(), indices.length); + } +}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGenerator.java similarity index 99% rename from src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java rename to src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGenerator.java index 8c139fe..c8b6c24 100644 --- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGenerator.java
@@ -43,7 +43,7 @@ import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; -public class AndroidApiDatabaseBuilderGenerator extends TestBase { +public class AndroidApiObjectDatabaseBuilderGenerator extends TestBase { public static String generatedMainDescriptor() { return descriptor(AndroidApiDatabaseBuilderTemplate.class).replace("Template", "");
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java similarity index 95% rename from src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java rename to src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java index 91accca..299a25a 100644 --- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiObjectDatabaseBuilderGeneratorTest.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.apimodel; -import static com.android.tools.r8.apimodel.AndroidApiDatabaseBuilderGenerator.generatedMainDescriptor; +import static com.android.tools.r8.apimodel.AndroidApiObjectDatabaseBuilderGenerator.generatedMainDescriptor; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static org.junit.Assert.assertEquals; @@ -48,7 +48,7 @@ import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) -public class AndroidApiDatabaseBuilderGeneratorTest extends TestBase { +public class AndroidApiObjectDatabaseBuilderGeneratorTest extends TestBase { protected final TestParameters parameters; private static final Path API_VERSIONS_XML = @@ -62,7 +62,7 @@ return getTestParameters().withNoneRuntime().build(); } - public AndroidApiDatabaseBuilderGeneratorTest(TestParameters parameters) { + public AndroidApiObjectDatabaseBuilderGeneratorTest(TestParameters parameters) { this.parameters = parameters; } @@ -75,7 +75,7 @@ TemporaryFolder temp = new TemporaryFolder(); temp.create(); ZipBuilder builder = ZipBuilder.builder(temp.newFile("out.jar").toPath()); - AndroidApiDatabaseBuilderGenerator.generate( + AndroidApiObjectDatabaseBuilderGenerator.generate( apiClasses, (descriptor, content) -> { try { @@ -139,10 +139,10 @@ private static void validateJar(Path generated, List<ParsedApiClass> apiClasses) { List<BiFunction<Path, List<ParsedApiClass>, Boolean>> tests = ImmutableList.of( - AndroidApiDatabaseBuilderGeneratorTest::testGeneratedOutputForVisitClasses, - AndroidApiDatabaseBuilderGeneratorTest::testBuildClassesContinue, - AndroidApiDatabaseBuilderGeneratorTest::testBuildClassesBreak, - AndroidApiDatabaseBuilderGeneratorTest::testNoPlaceHolder); + AndroidApiObjectDatabaseBuilderGeneratorTest::testGeneratedOutputForVisitClasses, + AndroidApiObjectDatabaseBuilderGeneratorTest::testBuildClassesContinue, + AndroidApiObjectDatabaseBuilderGeneratorTest::testBuildClassesBreak, + AndroidApiObjectDatabaseBuilderGeneratorTest::testNoPlaceHolder); tests.forEach( test -> { try {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java index 18b35f5..7c4226c 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -7,6 +7,7 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -41,6 +42,7 @@ .setMinApi(parameters.getApiLevel()) .addKeepMainRule(Main.class) .enableInliningAnnotations() + .enableNoHorizontalClassMergingAnnotations() .addHorizontallyMergedClassesInspector( inspector -> { if (parameters.isDexRuntime() @@ -66,6 +68,7 @@ } } + @NoHorizontalClassMerging static class ApiSetter { static void set() { A.api = new Api();
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java index f4609d4..5392a35 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningFieldTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.apimodel; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; @@ -40,7 +41,7 @@ public void testR8() throws Exception { Field apiField = Api.class.getDeclaredField("foo"); testForR8(parameters.getBackend()) - .addProgramClasses(ApiCaller.class, ApiCallerCaller.class, ApiBuilder.class, Main.class) + .addProgramClasses(ApiCallerCaller.class, ApiBuilder.class, Main.class) .addLibraryClasses(Api.class) .addDefaultRuntimeLibrary(parameters) .setMinApi(parameters.getApiLevel()) @@ -48,6 +49,7 @@ .enableInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1)) + .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1)) .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1)) .apply(ApiModelingTestHelper::enableApiCallerIdentification) .compile() @@ -55,9 +57,9 @@ inspector -> { if (parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) { - assertThat(inspector.clazz(ApiCaller.class), not(isPresent())); + assertThat(inspector.clazz(ApiBuilder.class), not(isPresent())); } else { - assertThat(inspector.clazz(ApiCaller.class), isPresent()); + assertThat(inspector.clazz(ApiBuilder.class), isPresent()); } }) .addRunClasspathClasses(Api.class) @@ -72,19 +74,14 @@ public static class ApiBuilder { - public static Api build() { - return new Api(); + public Api api; + + public ApiBuilder() { + api = new Api(); } - } - @NoHorizontalClassMerging - public static class ApiCaller { - - private Api api; - - public ApiCaller(Api api) { - this.api = api; - System.out.println(api.foo); + public String build() { + return api.foo; } } @@ -93,7 +90,7 @@ @NeverInline public static void callCallApi() { - new ApiCaller(ApiBuilder.build()); + System.out.println(new ApiBuilder().build()); } }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java index a1ecb6a..9446479 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.apimodel; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; @@ -49,6 +50,7 @@ .enableInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() .apply(setMockApiLevelForField(apiField, L_MR1)) + .apply(setMockApiLevelForClass(Api.class, L_MR1)) .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, L_MR1)) .apply(ApiModelingTestHelper::enableApiCallerIdentification) .compile()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java index bdaf09d..a480188 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
@@ -37,10 +37,10 @@ @Test public void testR8() throws Exception { Method apiMethod = Api.class.getDeclaredMethod("apiLevel22"); - Method apiCaller = ApiCaller.class.getDeclaredMethod("callInterfaceMethod", Api.class); - Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall"); + Method apiCaller = ApiCaller.class.getDeclaredMethod("callInterfaceMethod", Object.class); + Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall", Object.class); testForR8(parameters.getBackend()) - .addProgramClasses(Main.class, A.class, ApiCaller.class) + .addProgramClassesAndInnerClasses(Main.class, A.class, ApiCaller.class) .addLibraryClasses(Api.class) .addDefaultRuntimeLibrary(parameters) .setMinApi(parameters.getApiLevel()) @@ -66,10 +66,10 @@ public static class ApiCaller { @KeepConstantArguments - public static void callInterfaceMethod(Api api) { + public static void callInterfaceMethod(Object o) { System.out.println("ApiCaller::callInterfaceMethod"); - if (api != null) { - api.apiLevel22(); + if (o != null) { + ((Api) o).apiLevel22(); } } } @@ -78,16 +78,24 @@ public static class A { @NeverInline - public static void noApiCall() { + public static void noApiCall(Object o) { System.out.println("A::noApiCall"); - ApiCaller.callInterfaceMethod(null); + ApiCaller.callInterfaceMethod(o); } } public static class Main { public static void main(String[] args) { - A.noApiCall(); + A.noApiCall( + args.length > 0 + ? new Api() { + @Override + public void apiLevel22() { + throw new RuntimeException("Foo"); + } + } + : null); } } }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java index f09f09d..a366d8a 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -5,6 +5,7 @@ package com.android.tools.r8.apimodel; import static com.android.tools.r8.apimodel.ApiModelNoInliningOfHigherApiLevelVirtualTest.ApiCaller.callVirtualMethod; +import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat; @@ -49,6 +50,7 @@ .enableInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1)) + .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1)) .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1)) .apply(ApiModelingTestHelper::enableApiCallerIdentification) .compile()
diff --git a/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java index 51a307b..db9f09e 100644 --- a/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java +++ b/src/test/java/com/android/tools/r8/compilerapi/BinaryCompatibilityTestCollection.java
@@ -64,6 +64,9 @@ /** Additional classes that should always be included together to run tests. */ public abstract List<Class<?>> getAdditionalClassesForTests(); + /** Additional classes that should always be included together to run the pending tests. */ + public abstract List<Class<?>> getPendingAdditionalClassesForTests(); + /** Additional JVM args supplied to any external execution. */ public abstract List<String> getVmArgs(); @@ -92,7 +95,8 @@ public void runJunitOnTestClass(Class<? extends T> test) throws Exception { List<Class<? extends T>> testClasses = Collections.singletonList(test); - runJunitOnTestClasses(generateJarForTestClasses(testClasses), testClasses); + runJunitOnTestClasses( + generateJarForTestClasses(testClasses, getPendingAdditionalClassesForTests()), testClasses); } private void runJunitOnTestClasses(Path testJar, Collection<Class<? extends T>> tests) @@ -140,10 +144,12 @@ } public Path generateJarForCheckedInTestClasses() throws Exception { - return generateJarForTestClasses(getCheckedInTestClasses()); + return generateJarForTestClasses(getCheckedInTestClasses(), Collections.emptyList()); } - private Path generateJarForTestClasses(Collection<Class<? extends T>> classes) throws Exception { + private Path generateJarForTestClasses( + Collection<Class<? extends T>> classes, List<Class<?>> additionalPendingClassesForTest) + throws Exception { Path jar = getTemp().newFolder().toPath().resolve("test.jar"); ZipBuilder zipBuilder = ZipBuilder.builder(jar); for (Class<? extends T> test : classes) { @@ -162,6 +168,11 @@ getAdditionalClassesForTests().stream() .map(ToolHelper::getClassFileForTestClass) .collect(Collectors.toList())); + zipBuilder.addFilesRelative( + ToolHelper.getClassPathForTests(), + additionalPendingClassesForTest.stream() + .map(ToolHelper::getClassFileForTestClass) + .collect(Collectors.toList())); return zipBuilder.build(); }
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java index 654eafd..100567d 100644 --- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java +++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -5,8 +5,15 @@ import static org.junit.Assert.assertEquals; +import com.android.tools.r8.compilerapi.mockdata.MockClass; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collections; import java.util.List; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -20,6 +27,8 @@ @RunWith(Parameterized.class) public abstract class CompilerApiTest { + public static final Object PARAMETERS = "none"; + public static final String API_TEST_MODE_KEY = "API_TEST_MODE"; public static final String API_TEST_MODE_EXTERNAL = "external"; @@ -34,11 +43,13 @@ if (runtimes != null && !runtimes.contains("none")) { return Collections.emptyList(); } - return Collections.singletonList("none"); + return Collections.singletonList(PARAMETERS); } + @Rule public final TemporaryFolder temp = new TemporaryFolder(); + public CompilerApiTest(Object none) { - assertEquals("none", none); + assertEquals(PARAMETERS, none); } /** Predicate to determine if the test is being run externally. */ @@ -50,4 +61,30 @@ public boolean isRunningR8Lib() { return API_TEST_LIB_YES.equals(System.getProperty(API_TEST_LIB_KEY)); } + + public Path getNewTempFolder() throws IOException { + return temp.newFolder().toPath(); + } + + public Class<?> getMockClass() { + return MockClass.class; + } + + public Path getJava8RuntimeJar() { + return Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar"); + } + + public List<String> getKeepMainRules(Class<?> clazz) { + return Collections.singletonList( + "-keep class " + clazz.getName() + " { public static void main(java.lang.String[]); }"); + } + + public Path getPathForClass(Class<?> clazz) { + String file = clazz.getName().replace('.', '/') + ".class"; + return Paths.get("build", "classes", "java", "test", file); + } + + public byte[] getBytesForClass(Class<?> clazz) throws IOException { + return Files.readAllBytes(getPathForClass(clazz)); + } }
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java index 8c1e8a5..99c2cc8 100644 --- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java +++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -8,6 +8,8 @@ import static com.android.tools.r8.ToolHelper.isTestingR8Lib; import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest; +import com.android.tools.r8.compilerapi.mockdata.MockClass; import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest; import com.google.common.collect.ImmutableList; import java.nio.file.Path; @@ -26,9 +28,7 @@ ImmutableList.of(ApiTestingSetUpTest.ApiTest.class); private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY = - ImmutableList.of( - // No pending APIs. - ); + ImmutableList.of(CustomMapIdTest.ApiTest.class); private final TemporaryFolder temp; @@ -53,7 +53,12 @@ @Override public List<Class<?>> getAdditionalClassesForTests() { - return ImmutableList.of(CompilerApiTest.class); + return ImmutableList.of(CompilerApiTest.class, MockClass.class); + } + + @Override + public List<Class<?>> getPendingAdditionalClassesForTests() { + return ImmutableList.of(); } @Override
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mapid/CustomMapIdTest.java b/src/test/java/com/android/tools/r8/compilerapi/mapid/CustomMapIdTest.java new file mode 100644 index 0000000..ca7f819 --- /dev/null +++ b/src/test/java/com/android/tools/r8/compilerapi/mapid/CustomMapIdTest.java
@@ -0,0 +1,136 @@ +// 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.compilerapi.mapid; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.DexIndexedConsumer; +import com.android.tools.r8.MapIdEnvironment; +import com.android.tools.r8.MarkerMatcher; +import com.android.tools.r8.ProgramConsumer; +import com.android.tools.r8.R8; +import com.android.tools.r8.R8Command; +import com.android.tools.r8.StringConsumer; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.compilerapi.CompilerApiTest; +import com.android.tools.r8.compilerapi.CompilerApiTestRunner; +import com.android.tools.r8.dex.Marker; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.utils.BooleanBox; +import com.android.tools.r8.utils.ThrowingBiConsumer; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.nio.file.Path; +import java.util.Collection; +import java.util.function.Function; +import org.junit.Test; + +public class CustomMapIdTest extends CompilerApiTestRunner { + + public CustomMapIdTest(TestParameters parameters) { + super(parameters); + } + + @Override + public Class<? extends CompilerApiTest> binaryTestClass() { + return ApiTest.class; + } + + @Test + public void testDefaultMapId() throws Exception { + ApiTest test = new ApiTest(ApiTest.PARAMETERS); + runTest(test::runDefaultMapId, hash -> hash.substring(0, 7)); + } + + @Test + public void testCustomMapId() throws Exception { + ApiTest test = new ApiTest(ApiTest.PARAMETERS); + runTest(test::runCustomMapId, hash -> hash); + } + + private String getMapHash(String mapping) { + String lineHeader = "# pg_map_hash: SHA-256 "; + int i = mapping.indexOf(lineHeader); + assertTrue(i >= 0); + int start = i + lineHeader.length(); + int end = mapping.indexOf('\n', start); + return mapping.substring(start, end); + } + + private void runTest( + ThrowingBiConsumer<ProgramConsumer, StringConsumer, Exception> test, + Function<String, String> hashToId) + throws Exception { + Path output = temp.newFolder().toPath().resolve("out.jar"); + StringBuilder mappingBuilder = new StringBuilder(); + BooleanBox didGetMappingContent = new BooleanBox(false); + test.accept( + new DexIndexedConsumer.ArchiveConsumer(output), + (mappingContent, handler) -> { + mappingBuilder.append(mappingContent); + didGetMappingContent.set(true); + }); + assertTrue(didGetMappingContent.get()); + + // Extract the map hash from the file. This is always set by R8 to a SHA 256 hash. + String mappingContent = mappingBuilder.toString(); + String mapHash = getMapHash(mappingContent); + assertEquals(64, mapHash.length()); + + // Check the map id is also defined in the map file. + String mapId = hashToId.apply(mapHash); + assertThat(mappingContent, containsString("pg_map_id: " + mapId + "\n")); + + // Check that the map id is also present in the markers. + CodeInspector inspector = new CodeInspector(output); + Collection<Marker> markers = inspector.getMarkers(); + MarkerMatcher.assertMarkersMatch(markers, MarkerMatcher.markerPgMapId(equalTo(mapId))); + assertEquals(1, markers.size()); + } + + public static class ApiTest extends CompilerApiTest { + + public ApiTest(Object parameters) { + super(parameters); + } + + public void runDefaultMapId(ProgramConsumer programConsumer, StringConsumer mappingConsumer) + throws Exception { + R8.run( + R8Command.builder() + .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown()) + .addProguardConfiguration(getKeepMainRules(getMockClass()), Origin.unknown()) + .addLibraryFiles(getJava8RuntimeJar()) + .setProgramConsumer(programConsumer) + .setProguardMapConsumer(mappingConsumer) + .build()); + } + + public void runCustomMapId(ProgramConsumer programConsumer, StringConsumer mappingConsumer) + throws Exception { + R8.run( + R8Command.builder() + .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown()) + .addProguardConfiguration(getKeepMainRules(getMockClass()), Origin.unknown()) + .addLibraryFiles(getJava8RuntimeJar()) + .setMapIdProvider(MapIdEnvironment::getMapHash) + .setProgramConsumer(programConsumer) + .setProguardMapConsumer(mappingConsumer) + .build()); + } + + @Test + public void testDefaultMapId() throws Exception { + runDefaultMapId(DexIndexedConsumer.emptyConsumer(), StringConsumer.emptyConsumer()); + } + + @Test + public void testCustomMapId() throws Exception { + runCustomMapId(DexIndexedConsumer.emptyConsumer(), StringConsumer.emptyConsumer()); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClass.java b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClass.java new file mode 100644 index 0000000..340e532 --- /dev/null +++ b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClass.java
@@ -0,0 +1,12 @@ +// 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.compilerapi.mockdata; + +// Class to use as data for the compilation. +public class MockClass { + + public static void main(String[] args) { + System.out.println("Hello world!"); + } +}
diff --git a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java index d307de3..004a443 100644 --- a/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java +++ b/src/test/java/com/android/tools/r8/debug/LineNumberOptimizationTest.java
@@ -20,11 +20,9 @@ @RunWith(Parameterized.class) public class LineNumberOptimizationTest extends DebugTestBase { - private static final int[] ORIGINAL_LINE_NUMBERS = {20, 7, 8, 28, 8, 20, 21, 12, 21, 22, 16, 22}; private static final int[] ORIGINAL_LINE_NUMBERS_DEBUG = { 20, 7, 8, 28, 29, 9, 21, 12, 13, 22, 16, 17 }; - private static final int[] OPTIMIZED_LINE_NUMBERS = {1, 1, 2, 1, 2, 1, 2, 3, 2, 3, 4, 3}; private static final String CLASS1 = "LineNumberOptimization1"; private static final String CLASS2 = "LineNumberOptimization2"; @@ -82,18 +80,6 @@ } @Test - public void testNotOptimized() throws Throwable { - assumeMappingIsNotToPCs(); - testRelease(makeConfig(LineNumberOptimization.OFF, false, false), ORIGINAL_LINE_NUMBERS); - } - - @Test - public void testNotOptimizedWithMap() throws Throwable { - assumeMappingIsNotToPCs(); - testRelease(makeConfig(LineNumberOptimization.OFF, true, false), ORIGINAL_LINE_NUMBERS); - } - - @Test public void testNotOptimizedByEnablingDebug() throws Throwable { testDebug(makeConfig(LineNumberOptimization.OFF, false, true), ORIGINAL_LINE_NUMBERS_DEBUG); } @@ -103,20 +89,6 @@ testDebug(makeConfig(LineNumberOptimization.OFF, true, true), ORIGINAL_LINE_NUMBERS_DEBUG); } - @Test - public void testOptimized() throws Throwable { - assumeMappingIsNotToPCs(); - DebugTestConfig config = makeConfig(LineNumberOptimization.ON, false, false); - config.allowUsingPcForMissingLineNumberTable(); - testRelease(config, OPTIMIZED_LINE_NUMBERS); - } - - @Test - public void testOptimizedWithMap() throws Throwable { - assumeMappingIsNotToPCs(); - testRelease(makeConfig(LineNumberOptimization.ON, true, false), ORIGINAL_LINE_NUMBERS); - } - private void testDebug(DebugTestConfig config, int[] lineNumbers) throws Throwable { runDebugTest( config, @@ -160,55 +132,4 @@ checkLine(FILE1, lineNumbers[11]), run()); } - - // If we compile in release mode the line numbers are slightly different from the debug mode. - // That's why we need a different set of checks for the release mode. - // - // In release mode void returns don't have line number information. On the other hand, because of - // the line number information is moved as late as possible stepping in the debugger is different: - // After a method call we step again on the invoke instructions's line number before moving onto - // the next instruction. - private void testRelease(DebugTestConfig config, int[] lineNumbers) throws Throwable { - runDebugTest( - config, - CLASS1, - breakpoint(CLASS1, "main", MAIN_SIGNATURE), - run(), - checkMethod(CLASS1, "main", MAIN_SIGNATURE), - checkLine(FILE1, lineNumbers[0]), - stepInto(), - checkMethod(CLASS1, "callThisFromSameFile", "()V"), - checkLine(FILE1, lineNumbers[1]), - stepOver(), - checkMethod(CLASS1, "callThisFromSameFile", "()V"), - checkLine(FILE1, lineNumbers[2]), - stepInto(INTELLIJ_FILTER), - checkMethod(CLASS2, "callThisFromAnotherFile", "()V"), - checkLine(FILE2, lineNumbers[3]), - stepOver(), - checkMethod(CLASS1, "callThisFromSameFile", "()V"), - checkLine(FILE1, lineNumbers[4]), - stepOver(), - checkMethod(CLASS1, "main", MAIN_SIGNATURE), - checkLine(FILE1, lineNumbers[5]), - stepOver(), - checkMethod(CLASS1, "main", MAIN_SIGNATURE), - checkLine(FILE1, lineNumbers[6]), - stepInto(), - checkMethod(CLASS1, "callThisFromSameFile", "(I)V"), - checkLine(FILE1, lineNumbers[7]), - stepOver(), - checkMethod(CLASS1, "main", MAIN_SIGNATURE), - checkLine(FILE1, lineNumbers[8]), - stepOver(), - checkMethod(CLASS1, "main", MAIN_SIGNATURE), - checkLine(FILE1, lineNumbers[9]), - stepInto(), - checkMethod(CLASS1, "callThisFromSameFile", "(II)V"), - checkLine(FILE1, lineNumbers[10]), - stepOver(), - checkMethod(CLASS1, "main", MAIN_SIGNATURE), - checkLine(FILE1, lineNumbers[11]), - run()); - } }
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java b/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java new file mode 100644 index 0000000..e64b7d7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/debuginfo/DebugSetFileSmaliTest.java
@@ -0,0 +1,81 @@ +// 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.debuginfo; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertFalse; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersBuilder; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.graph.DexDebugEvent.SetFile; +import com.android.tools.r8.smali.SmaliBuilder; +import com.android.tools.r8.utils.AndroidApiLevel; +import java.util.Arrays; +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 DebugSetFileSmaliTest extends TestBase { + + private final TestParameters parameters; + + public DebugSetFileSmaliTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Parameters(name = "{0}") + public static TestParametersCollection parameters() { + return TestParametersBuilder.builder() + .withDexRuntimes() + .withApiLevel(AndroidApiLevel.B) + .build(); + } + + private static final String CLASS_NAME = "Test"; + private static final String CLASS_SOURCE_FILE = "Test.java"; + private static final String DEBUG_SET_FILE = "SomeFile.java"; + + @Test + public void test() throws Exception { + SmaliBuilder builder = new SmaliBuilder(CLASS_NAME); + builder.setSourceFile(CLASS_SOURCE_FILE); + builder.addMainMethod( + 2, + ".line 1", + " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;", + " const-string v1, \"Hello, world!\"", + // If the following invoke is not present legacy VMs fail with "invalid debug stream"!? + " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V", + ".source \"" + DEBUG_SET_FILE + "\"", + ".line 42", + " const v0, 0", + " throw v0"); + + testForD8(parameters.getBackend()) + .setMinApi(parameters.getApiLevel()) + .addProgramDexFileData(builder.compile()) + .compile() + .inspect( + inspector -> { + assertFalse( + Arrays.stream( + inspector + .clazz(CLASS_NAME) + .mainMethod() + .getMethod() + .getCode() + .asDexCode() + .getDebugInfo() + .events) + .anyMatch(e -> e instanceof SetFile)); + }) + .run(parameters.getRuntime(), CLASS_NAME) + .assertFailureWithErrorThatMatches( + containsString("at Test.main(" + CLASS_SOURCE_FILE + ":42)")); + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java index aeb159d..ee66aab 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -188,31 +188,31 @@ private void assertGeneratedKeepRulesAreCorrect(String keepRules) { String expectedResult = StringUtils.lines( - "-keep class j$.util.List$-EL {", - " void sort(java.util.List, java.util.Comparator);", - "}", "-keep class j$.util.Collection$-EL {", " j$.util.stream.Stream stream(java.util.Collection);", "}", - "-keep class j$.util.stream.IntStream$-CC {", - " j$.util.stream.IntStream range(int, int);", - "}", "-keep class j$.util.Comparator$-CC {", " java.util.Comparator comparingInt(j$.util.function.ToIntFunction);", "}", + "-keep class j$.util.DesugarArrays {", + " j$.util.Spliterator spliterator(java.lang.Object[]);", + " j$.util.Spliterator spliterator(java.lang.Object[], int, int);", + " j$.util.stream.Stream stream(java.lang.Object[]);", + " j$.util.stream.Stream stream(java.lang.Object[], int, int);", + "}", + "-keep class j$.util.List$-EL {", + " void sort(java.util.List, java.util.Comparator);", + "}", "-keep class j$.util.Set$-EL {", " j$.util.Spliterator spliterator(java.util.Set);", "}", - "-keep class j$.util.DesugarArrays {", - " j$.util.Spliterator spliterator(java.lang.Object[]);", - " j$.util.stream.Stream stream(java.lang.Object[], int, int);", - " j$.util.stream.Stream stream(java.lang.Object[]);", - " j$.util.Spliterator spliterator(java.lang.Object[], int, int);", + "-keep class j$.util.Spliterator", + "-keep class j$.util.function.ToIntFunction { *; }", + "-keep class j$.util.stream.IntStream$-CC {", + " j$.util.stream.IntStream range(int, int);", "}", "-keep class j$.util.stream.IntStream", - "-keep class j$.util.stream.Stream", - "-keep class j$.util.Spliterator", - "-keep class j$.util.function.ToIntFunction { *; }"); + "-keep class j$.util.stream.Stream"); assertEquals(expectedResult, keepRules); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java index 8553034..26561ec 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/HelloWorldCompiledOnArtTest.java
@@ -133,7 +133,12 @@ .setMinApi(parameters.getApiLevel()) .enableCoreLibraryDesugaring( LibraryDesugaringTestConfiguration.forApiLevel(parameters.getApiLevel())) - .addOptionsModification(opt -> opt.testing.trackDesugaredAPIConversions = true) + .addOptionsModification( + options -> { + options.testing.enableD8ResourcesPassThrough = true; + options.dataResourceConsumer = options.programConsumer.getDataResourceConsumer(); + options.testing.trackDesugaredAPIConversions = true; + }) .compile(); TestDiagnosticMessages diagnosticMessages = compile.getDiagnosticMessages(); assertTrue(
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java index 66bdd07..6fa1514 100644 --- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java +++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8BootstrapTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.desugar.nestaccesscontrol; -import static com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest.uploadJarsToCloudStorageIfTestFails; import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; @@ -13,7 +12,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.TestRuntime; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest; @@ -55,7 +53,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withCfRuntimes().build(); + return getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK11).build(); } @BeforeClass @@ -95,7 +93,6 @@ private Path[] jarsToCompare() { return new Path[] { - ToolHelper.R8_WITH_RELOCATED_DEPS_JAR, ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR, r8Lib11NoDesugar, r8Lib11Desugar @@ -108,13 +105,8 @@ Path prevGeneratedJar = null; String prevRunResult = null; for (Path jar : jarsToCompare()) { - // All jars except ToolHelper.R8_WITH_RELOCATED_DEPS_JAR are compiled for JDK11. - TestRuntime runtime = - jar == ToolHelper.R8_WITH_RELOCATED_DEPS_JAR - ? parameters.getRuntime() - : TestRuntime.getCheckedInJdk11(); Path generatedJar = - testForExternalR8(Backend.CF, runtime) + testForExternalR8(Backend.CF, parameters.getRuntime()) .useProvidedR8(jar) .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "hello" + JAR_EXTENSION)) .addKeepRules(HELLO_KEEP) @@ -140,7 +132,7 @@ public void testR8() throws Exception { Assume.assumeTrue(!ToolHelper.isWindows()); Assume.assumeTrue(parameters.isCfRuntime()); - Assume.assumeTrue(CfVm.JDK11 == parameters.getRuntime().asCf().getVm()); + Assume.assumeTrue(CfVm.JDK11.lessThanOrEqual(parameters.getRuntime().asCf().getVm())); Path prevGeneratedJar = null; for (Path jar : jarsToCompare()) { Path generatedJar =
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToMissingMethodDeclaredInSuperClassTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToMissingMethodDeclaredInSuperClassTest.java new file mode 100644 index 0000000..fca3a67 --- /dev/null +++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToMissingMethodDeclaredInSuperClassTest.java
@@ -0,0 +1,87 @@ +// 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.graph.invokespecial; + +import static org.junit.Assert.assertEquals; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import java.io.IOException; +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 InvokeSpecialToMissingMethodDeclaredInSuperClassTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public InvokeSpecialToMissingMethodDeclaredInSuperClassTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addProgramClasses(A.class, Main.class) + .addProgramClassFileData(getClassWithTransformedInvoked()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("A.foo()"); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, Main.class) + .addProgramClassFileData(getClassWithTransformedInvoked()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(NoSuchMethodError.class); + } + + private byte[] getClassWithTransformedInvoked() throws IOException { + return transformer(B.class) + .transformMethodInsnInMethod( + "bar", + (opcode, owner, name, descriptor, isInterface, continuation) -> { + assertEquals(INVOKEVIRTUAL, opcode); + assertEquals("notify", name); + continuation.visitMethodInsn( + INVOKESPECIAL, binaryName(B.class), "foo", descriptor, isInterface); + }) + .transform(); + } + + public static class A { + + public void foo() { + System.out.println("A.foo()"); + } + } + + public static class B extends A { + + public void bar() { + notify(); // Will be rewritten to invoke-special B.foo() which is missing, but found in A. + } + } + + public static class Main { + + public static void main(String[] args) { + new B().bar(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToMissingMethodDeclaredInSuperInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToMissingMethodDeclaredInSuperInterfaceTest.java new file mode 100644 index 0000000..2a9e1c0 --- /dev/null +++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToMissingMethodDeclaredInSuperInterfaceTest.java
@@ -0,0 +1,93 @@ +// 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.graph.invokespecial; + +import static org.junit.Assert.assertEquals; +import static org.objectweb.asm.Opcodes.INVOKESPECIAL; +import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import java.io.IOException; +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 InvokeSpecialToMissingMethodDeclaredInSuperInterfaceTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public InvokeSpecialToMissingMethodDeclaredInSuperInterfaceTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addProgramClasses(A.class, C.class, Main.class) + .addProgramClassFileData(getClassWithTransformedInvoked()) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("A.foo()"); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, C.class, Main.class) + .addProgramClassFileData(getClassWithTransformedInvoked()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), Main.class) + // TODO(b/202381923): invoke-special is not mapped correctly. + .applyIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), + runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class), + runResult -> runResult.assertSuccessWithOutputLines("A.foo()")); + } + + private byte[] getClassWithTransformedInvoked() throws IOException { + return transformer(B.class) + .transformMethodInsnInMethod( + "bar", + (opcode, owner, name, descriptor, isInterface, continuation) -> { + assertEquals(INVOKEVIRTUAL, opcode); + assertEquals("notify", name); + continuation.visitMethodInsn( + INVOKESPECIAL, binaryName(B.class), "foo", descriptor, true); + }) + .transform(); + } + + public interface A { + + default void foo() { + System.out.println("A.foo()"); + } + } + + public interface B extends A { + + default void bar() { + notify(); // Will be rewritten to invoke-special B.foo() which is missing, but found in A. + } + } + + static class C implements B {} + + public static class Main { + + public static void main(String[] args) { + new C().bar(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java index aea7348..f280e80 100644 --- a/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java +++ b/src/test/java/com/android/tools/r8/internal/YouTubeCompilationTestBase.java
@@ -5,14 +5,19 @@ import static org.junit.Assert.assertTrue; +import com.android.tools.r8.ByteDataView; +import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer; import com.android.tools.r8.R8TestCompileResult; +import com.android.tools.r8.TestBase; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.FileUtils; +import com.android.tools.r8.utils.ZipUtils; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.analysis.ProtoApplicationStats; import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -84,15 +89,33 @@ return builder.build(); } - protected List<Path> getLibraryFiles() { + protected Path getLibraryFile() { Path filtered = Paths.get(base).resolve("legacy_YouTubeRelease_combined_library_jars_filtered.jar"); if (filtered.toFile().exists()) { - return ImmutableList.of(filtered); + return filtered; } Path unfiltered = Paths.get(base, "legacy_YouTubeRelease_combined_library_jars.jar"); assertTrue(unfiltered.toFile().exists()); - return ImmutableList.of(unfiltered); + return unfiltered; + } + + Path getLibraryFileWithoutDesugaredLibrary() throws IOException { + Path libraryFile = getLibraryFile(); + Path filteredLibraryFile = + Paths.get(libraryFile.toString().replace(".jar", "desugared_lib_filtered.jar")); + ArchiveConsumer consumer = new ArchiveConsumer(filteredLibraryFile); + ZipUtils.iter( + libraryFile, + (entry, inputStream) -> { + String entryString = entry.toString(); + if (entryString.endsWith(".class") && !entryString.startsWith("j$")) { + byte[] bytes = ByteStreams.toByteArray(inputStream); + consumer.accept(ByteDataView.of(bytes), TestBase.extractClassDescriptor(bytes), null); + } + }); + consumer.finished(null); + return filteredLibraryFile; } protected List<Path> getMainDexRuleFiles() {
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java index 24ae3c1..9ae3939 100644 --- a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java +++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.utils.AndroidApiLevel; +import com.google.common.collect.ImmutableList; import java.nio.file.Paths; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,7 +44,7 @@ LibrarySanitizer librarySanitizer = new LibrarySanitizer(temp) .addProgramFiles(getProgramFiles()) - .addLibraryFiles(getLibraryFiles()) + .addLibraryFiles(ImmutableList.of(getLibraryFile())) .sanitize() .assertSanitizedProguardConfigurationIsEmpty();
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java index 830d6fc..50442c9 100644 --- a/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java +++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1612Test.java
@@ -115,7 +115,7 @@ throws IOException, CompilationFailedException { return testForR8(parameters.getBackend()) .addProgramFiles(getProgramFiles()) - .addLibraryFiles(getLibraryFiles()) + .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary()) .addKeepRuleFiles(getKeepRuleFiles()) .addDontWarn("android.app.Activity$TranslucentConversionListener") .allowDiagnosticMessages()
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java index 0b76040..0c3abdd 100644 --- a/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java +++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1620Test.java
@@ -115,7 +115,7 @@ throws IOException, CompilationFailedException { return testForR8(parameters.getBackend()) .addProgramFiles(getProgramFiles()) - .addLibraryFiles(getLibraryFiles()) + .addLibraryFiles(getLibraryFileWithoutDesugaredLibrary()) .addKeepRuleFiles(getKeepRuleFiles()) .addDontWarn("android.app.Activity$TranslucentConversionListener") .allowDiagnosticMessages()
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java index e418ced..5bae6d3 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -11,21 +11,23 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.KotlinTestParameters; import com.android.tools.r8.TestParameters; import com.android.tools.r8.code.NewInstance; import com.android.tools.r8.code.SgetObject; -import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.naming.MemberNaming.MethodSignature; +import com.android.tools.r8.utils.IntBox; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import com.google.common.collect.Lists; import com.google.common.collect.Streams; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -39,6 +41,9 @@ @RunWith(Parameterized.class) public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase { + private final String INTERNAL_SYNTHETIC_MAIN_PREFIX = + "class_inliner_lambda_j_style.MainKt$$InternalSyntheticLambda"; + @Parameterized.Parameters(name = "{0}, {1}") public static List<Object[]> data() { return buildParameters( @@ -50,19 +55,12 @@ super(parameters, kotlinParameters, true); } - private static boolean isKStyleLambda(DexClass clazz) { - return clazz.getSuperType().getTypeName().equals("kotlin.jvm.internal.Lambda"); - } - - private static boolean isJStyleLambda(DexClass clazz) { - return clazz.getSuperType().getTypeName().equals(Object.class.getTypeName()) - && clazz.getInterfaces().size() == 1; - } - @Test public void testJStyleLambdas() throws Exception { - // TODO(b/185497606): Unable to class inline j style lambdas. - assumeTrue(kotlinc.isNot(KOTLINC_1_5_0)); + // SAM interfaces lambdas are implemented by invoke dynamic in kotlin 1.5 unlike 1.4 where a + // class is generated for each. In CF we leave invokeDynamic but for DEX we desugar the classes + // and merge them. + boolean hasKotlinCGeneratedLambdaClasses = kotlinParameters.isOlderThan(KOTLINC_1_5_0); String mainClassName = "class_inliner_lambda_j_style.MainKt"; runTest( "class_inliner_lambda_j_style", @@ -74,7 +72,27 @@ .addNoHorizontalClassMergingRule( "class_inliner_lambda_j_style.SamIface$Consumer") .addHorizontallyMergedClassesInspector( - inspector -> + inspector -> { + if (!hasKotlinCGeneratedLambdaClasses && testParameters.isCfRuntime()) { + inspector.assertNoClassesMerged(); + } else if (!hasKotlinCGeneratedLambdaClasses) { + Set<Set<DexType>> mergeGroups = inspector.getMergeGroups(); + assertEquals(2, mergeGroups.size()); + IntBox seenLambdas = new IntBox(); + assertTrue( + mergeGroups.stream() + .flatMap(Collection::stream) + .allMatch( + type -> { + boolean isDesugaredLambda = + type.toSourceString() + .startsWith(INTERNAL_SYNTHETIC_MAIN_PREFIX); + if (isDesugaredLambda) { + seenLambdas.increment(); + } + return isDesugaredLambda; + })); + } else { inspector .assertIsCompleteMergeGroup( "class_inliner_lambda_j_style.MainKt$testStateless$1", @@ -86,16 +104,31 @@ "class_inliner_lambda_j_style.MainKt$testStateful$2$1", "class_inliner_lambda_j_style.MainKt$testStateful$3", "class_inliner_lambda_j_style.MainKt$testStateful2$1", - "class_inliner_lambda_j_style.MainKt$testStateful3$1")) + "class_inliner_lambda_j_style.MainKt$testStateful3$1"); + } + }) .noClassInlining()) .inspect( inspector -> { - assertThat( - inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"), - isPresent()); - assertThat( - inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"), - isPresent()); + if (testParameters.isCfRuntime() && !hasKotlinCGeneratedLambdaClasses) { + assertEquals(5, inspector.allClasses().size()); + } else if (!hasKotlinCGeneratedLambdaClasses) { + assertThat( + inspector.clazz( + "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda1"), + isPresent()); + assertThat( + inspector.clazz( + "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2"), + isPresent()); + } else { + assertThat( + inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"), + isPresent()); + assertThat( + inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"), + isPresent()); + } }); runTest( @@ -109,14 +142,28 @@ "class_inliner_lambda_j_style.SamIface$Consumer")) .inspect( inspector -> { + if (testParameters.isCfRuntime() && !hasKotlinCGeneratedLambdaClasses) { + assertEquals(5, inspector.allClasses().size()); + return; + } // TODO(b/173337498): MainKt$testStateless$1 should always be class inlined. - assertThat( - inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"), - notIf(isPresent(), testParameters.isDexRuntime())); + if (!hasKotlinCGeneratedLambdaClasses) { + assertThat( + inspector.clazz( + "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda1"), + isPresent()); + } else { + assertThat( + inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"), + notIf(isPresent(), testParameters.isDexRuntime())); + } // TODO(b/173337498): MainKt$testStateful$1 should be class inlined. assertThat( - inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"), + inspector.clazz( + !hasKotlinCGeneratedLambdaClasses + ? "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2" + : "class_inliner_lambda_j_style.MainKt$testStateful$1"), isPresent()); }); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java index 41552ac..4fad86a 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
@@ -73,8 +73,6 @@ @Test public void testR8() throws Exception { - // TODO(b/185497606): Unable to merge jstyle lambda. - assumeTrue(kotlinc.isNot(KOTLINC_1_5_0)); testForR8(parameters.getBackend()) .addProgramFiles(getProgramFiles()) .addKeepMainRule(getMainClassName()) @@ -90,13 +88,15 @@ } private void inspect(HorizontallyMergedClassesInspector inspector) throws IOException { + boolean hasKotlinCGeneratedLambdaClasses = kotlinParameters.isOlderThan(KOTLINC_1_5_0); // Get the Kotlin lambdas in the input. KotlinLambdasInInput lambdasInInput = KotlinLambdasInInput.create(getProgramFiles(), getTestName()); - assertEquals(39, lambdasInInput.getNumberOfJStyleLambdas()); + assertEquals( + hasKotlinCGeneratedLambdaClasses ? 39 : 0, lambdasInInput.getNumberOfJStyleLambdas()); assertEquals(0, lambdasInInput.getNumberOfKStyleLambdas()); - if (!allowAccessModification) { + if (!allowAccessModification && hasKotlinCGeneratedLambdaClasses) { // Only a subset of all J-style Kotlin lambdas are merged without -allowaccessmodification. Set<ClassReference> unmergedLambdas = ImmutableSet.of( @@ -119,8 +119,11 @@ return; } - // All J-style Kotlin lambdas are merged with -allowaccessmodification. - inspector.assertClassReferencesMerged(lambdasInInput.getJStyleLambdas()); + if (!parameters.isCfRuntime() || hasKotlinCGeneratedLambdaClasses) { + // All J-style Kotlin lambdas are merged with -allowaccessmodification or because they are + // generated by R8. + inspector.assertClassReferencesMerged(lambdasInInput.getJStyleLambdas()); + } } private String getExpectedOutput() {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java index 04921e2..80863b5 100644 --- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java +++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -37,6 +37,7 @@ import com.android.tools.r8.graph.DebugLocalInfo; import com.android.tools.r8.graph.DexAnnotationSet; import com.android.tools.r8.graph.DexApplication; +import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexItemFactory; @@ -855,7 +856,7 @@ new SynthesizedCode( (ignored, callerPosition) -> new ReturnVoidCode(voidReturnMethod, callerPosition)) { @Override - public Consumer<UseRegistry> getRegistryCallback() { + public Consumer<UseRegistry> getRegistryCallback(DexClassAndMethod method) { throw new Unreachable(); } };
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java deleted file mode 100644 index de654b8..0000000 --- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileSmaliTest.java +++ /dev/null
@@ -1,128 +0,0 @@ -// Copyright (c) 2017, 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.naming; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import com.android.tools.r8.code.ConstString; -import com.android.tools.r8.code.InvokeVirtual; -import com.android.tools.r8.code.ReturnVoid; -import com.android.tools.r8.code.SgetObject; -import com.android.tools.r8.graph.DexClass; -import com.android.tools.r8.graph.DexCode; -import com.android.tools.r8.graph.DexDebugEvent.SetFile; -import com.android.tools.r8.graph.DexEncodedMethod; -import com.android.tools.r8.shaking.ProguardConfiguration; -import com.android.tools.r8.smali.SmaliBuilder; -import com.android.tools.r8.smali.SmaliBuilder.MethodSignature; -import com.android.tools.r8.smali.SmaliTestBase; -import com.android.tools.r8.utils.FileUtils; -import com.android.tools.r8.utils.StringUtils; -import com.google.common.collect.ImmutableList; -import java.nio.file.Path; -import java.util.Arrays; -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; - -/** - * Tests -renamesourcefileattribute. - */ -@RunWith(Parameterized.class) -public class RenameSourceFileSmaliTest extends SmaliTestBase { - - private static final String TEST_FILE = "TestFile.java"; - - private static final List<String> DEFAULT_PG_CONFIGS = - ImmutableList.of( - "-keep class *** { *; }", - "-dontoptimize", - "-keepattributes SourceFile,LineNumberTable"); - - private void configure(ProguardConfiguration.Builder pg) { - if (renaming) { - pg.setRenameSourceFileAttribute(TEST_FILE); - } - } - - @Parameter - public boolean renaming; - - @Parameters(name="renaming:{0}") - public static Object[] parameters() { - return new Object[] {true, false}; - } - - /** - * replica of {@link RunArtSmokeTest#test} - */ - @Test - public void artSmokeTest() throws Exception { - // Build simple "Hello, world!" application. - SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME); - String originalSourceFile = DEFAULT_CLASS_NAME + FileUtils.JAVA_EXTENSION; - builder.setSourceFile(originalSourceFile); - MethodSignature mainSignature = builder.addMainMethod( - 2, - ".line 1", - " sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;", - " const-string v1, \"Hello, world!\"", - ".source \"PrintStream.java\"", - ".line 337", - " invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V", - ".source \"" + originalSourceFile + "\"", - ".line 2", - " return-void" - ); - Path processedApp = runR8(builder, DEFAULT_PG_CONFIGS, this::configure, null); - - DexClass mainClass = getClass(processedApp, DEFAULT_CLASS_NAME); - verifySourceFileInCodeItem(mainClass, originalSourceFile, TEST_FILE); - - DexEncodedMethod mainMethod = getMethod(processedApp, mainSignature); - assertNotNull(mainMethod); - - DexCode code = mainMethod.getCode().asDexCode(); - assertTrue(code.instructions[0] instanceof SgetObject); - assertTrue(code.instructions[1] instanceof ConstString); - assertTrue(code.instructions[2] instanceof InvokeVirtual); - assertTrue(code.instructions[3] instanceof ReturnVoid); - - // Run the generated code in Art. - String result = runArt(processedApp, DEFAULT_MAIN_CLASS_NAME); - assertEquals(StringUtils.lines("Hello, world!"), result); - - verifySourceFileInDebugInfo(code); - } - - private void verifySourceFileInCodeItem(DexClass clazz, String original, String rename) { - String processedSourceFile = clazz.sourceFile.toString(); - if (renaming) { - assertEquals(rename, processedSourceFile); - } else { - assertEquals(original, processedSourceFile); - } - } - - private void verifySourceFileInDebugInfo(DexCode code) { - assertNotNull(code.getDebugInfo()); - assertNotEquals(0, code.getDebugInfo().events.length); - long setFileCount = - Arrays.stream(code.getDebugInfo().events) - .filter(dexDebugEvent -> dexDebugEvent instanceof SetFile) - .count(); - if (renaming) { - assertEquals(0, setFileCount); - } else { - assertNotEquals(0, setFileCount); - } - } - -}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingRepackagingTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingRepackagingTest.java new file mode 100644 index 0000000..cb4c8cc --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingRepackagingTest.java
@@ -0,0 +1,123 @@ +// 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.naming.applymapping; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertNotEquals; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NeverPropagateValue; +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.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class ApplyMappingRepackagingTest extends TestBase { + + @NeverClassInline + public static class A { + + @NeverPropagateValue public int fieldA = 1; + + @NeverPropagateValue public int fieldB = 2; + + @NeverInline + public void methodA() { + System.out.println("A.methodA"); + } + + @NeverInline + public void methodB() { + System.out.println("A.methodB"); + } + } + + @NeverClassInline + public static class B { + @NeverInline + public void foo() { + System.out.println("B.foo"); + } + } + + public static class C { + + public static void main(String[] args) { + System.out.println(new A().fieldA); + System.out.println(new A().fieldB); + new A().methodA(); + new A().methodB(); + new B().foo(); + } + } + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public ApplyMappingRepackagingTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testApplyMappingFollowedByMinification() + throws IOException, CompilationFailedException, ExecutionException, NoSuchMethodException { + String[] pgMap = + new String[] { + A.class.getTypeName() + " -> baz:", " int fieldA -> foo", " void methodA() -> bar" + }; + R8TestRunResult runResult = + testForR8(parameters.getBackend()) + .addInnerClasses(ApplyMappingRepackagingTest.class) + .enableInliningAnnotations() + .enableMemberValuePropagationAnnotations() + .enableNeverClassInliningAnnotations() + .addApplyMapping(StringUtils.lines(pgMap)) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(C.class) + .addKeepRules("-repackageclasses") + .run(parameters.getRuntime(), C.class) + .assertSuccessWithOutputLines("1", "2", "A.methodA", "A.methodB", "B.foo") + .inspect( + inspector -> { + assertThat(inspector.clazz(B.class), isPresentAndRenamed()); + ClassSubject clazzA = inspector.clazz(A.class); + assertThat(clazzA, isPresent()); + // TODO(b/202194059): Should be baz + assertNotEquals("baz", clazzA.getFinalName()); + FieldSubject fieldA = clazzA.uniqueFieldWithName("fieldA"); + assertThat(fieldA, isPresent()); + // TODO(b/202194059): Should be foo + assertNotEquals("foo", fieldA.getFinalName()); + MethodSubject methodA = clazzA.uniqueMethodWithName("methodA"); + assertThat(methodA, isPresent()); + // TODO(b/202194059): Should be bar + assertNotEquals("bar", methodA.getFinalName()); + assertThat(clazzA.uniqueFieldWithName("fieldB"), isPresentAndRenamed()); + assertThat(clazzA.uniqueMethodWithName("methodB"), isPresentAndRenamed()); + }); + // Ensure that the proguard map is extended with all the new minified names. + for (String pgLine : pgMap) { + runResult.proguardMap().contains(pgLine); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java index 458cb07..4ba3cbf 100644 --- a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java +++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarLambdaRetraceTest.java
@@ -10,7 +10,7 @@ import static org.junit.Assert.assertEquals; import com.android.tools.r8.CompilationMode; -import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.TestParameters; import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine; import com.android.tools.r8.utils.BooleanUtils; import com.google.common.collect.ImmutableList; @@ -24,14 +24,16 @@ @RunWith(Parameterized.class) public class DesugarLambdaRetraceTest extends RetraceTestBase { - @Parameters(name = "Backend: {0}, mode: {1}, compat: {2}") + @Parameters(name = "{0}, mode: {1}, compat: {2}") public static Collection<Object[]> data() { return buildParameters( - ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + CompilationMode.values(), + BooleanUtils.values()); } - public DesugarLambdaRetraceTest(Backend backend, CompilationMode mode, boolean compat) { - super(backend, mode, compat); + public DesugarLambdaRetraceTest(TestParameters parameters, CompilationMode mode, boolean compat) { + super(parameters, mode, compat); } @Override @@ -47,7 +49,7 @@ private int expectedActualStackTraceHeight() { // In debug mode the expected stack trace height differs since there is no lambda desugaring // for CF. - return mode == CompilationMode.RELEASE ? 2 : (backend == Backend.CF ? 4 : 5); + return mode == CompilationMode.RELEASE ? 2 : (parameters.isCfRuntime() ? 4 : 5); } private boolean isSynthesizedLambdaFrame(StackTraceLine line) { @@ -68,7 +70,7 @@ private void checkIsSameExceptForFileName( StackTrace actualStackTrace, StackTrace retracedStackTrace) { // Even when SourceFile is present retrace replaces the file name in the stack trace. - if (backend == Backend.CF) { + if (parameters.isCfRuntime()) { // TODO(122440196): Additional code to locate issue. if (!isSameExceptForFileName(expectedStackTrace).matches(retracedStackTrace)) { System.out.println("Expected original:"); @@ -107,7 +109,7 @@ private void checkIsSameExceptForFileNameAndLineNumber( StackTrace actualStackTrace, StackTrace retracedStackTrace) { // Even when SourceFile is present retrace replaces the file name in the stack trace. - if (backend == Backend.CF) { + if (parameters.isCfRuntime()) { // TODO(122440196): Additional code to locate issue. if (!isSameExceptForFileNameAndLineNumber(expectedStackTrace).matches(retracedStackTrace)) { System.out.println("Expected original:");
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java index 599fa3b..69d3fe6 100644 --- a/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java +++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/DesugarStaticInterfaceMethodsRetraceTest.java
@@ -11,7 +11,7 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.NeverInline; import com.android.tools.r8.R8TestBuilder; -import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.TestParameters; import com.android.tools.r8.utils.BooleanUtils; import com.google.common.collect.ImmutableList; import java.util.Collection; @@ -23,15 +23,17 @@ @RunWith(Parameterized.class) public class DesugarStaticInterfaceMethodsRetraceTest extends RetraceTestBase { - @Parameters(name = "Backend: {0}, mode: {1}, compat: {2}") + @Parameters(name = "{0}, mode: {1}, compat: {2}") public static Collection<Object[]> data() { return buildParameters( - ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + CompilationMode.values(), + BooleanUtils.values()); } public DesugarStaticInterfaceMethodsRetraceTest( - Backend backend, CompilationMode mode, boolean compat) { - super(backend, mode, compat); + TestParameters parameters, CompilationMode mode, boolean compat) { + super(parameters, mode, compat); } @Override @@ -54,11 +56,13 @@ public void testSourceFileAndLineNumberTable() throws Exception { // TODO(b/186015503): This test fails when mapping via PCs. // also the test should be updated to use TestParameters and api levels. - assumeTrue("b/186015503", !backend.isDex() || mode != CompilationMode.RELEASE); + assumeTrue("b/186015503", !parameters.isDexRuntime() || mode != CompilationMode.RELEASE); // This also fails when desugaring due to the change in companion method stacks. assumeTrue( - ToolHelper.getMinApiLevelForDexVm() - .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport())); + parameters.isCfRuntime() + || parameters + .getApiLevel() + .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport())); runTest( ImmutableList.of("-keepattributes SourceFile,LineNumberTable"), // For the desugaring to companion classes the retrace stacktrace is still the same
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java index ae6a985..016d4ac 100644 --- a/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java +++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/InliningRetraceTest.java
@@ -11,12 +11,11 @@ import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationMode; -import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.BooleanUtils; import com.google.common.collect.ImmutableList; import java.util.Collection; -import java.util.Collections; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -25,16 +24,20 @@ @RunWith(Parameterized.class) public class InliningRetraceTest extends RetraceTestBase { - @Parameters(name = "Backend: {0}, mode: {1}") + @Parameters(name = "{0}, mode: {1}, compat: {2}") public static Collection<Object[]> data() { - return ToolHelper.getDexVm().getVersion() == Version.V5_1_1 - ? Collections.emptyList() - : buildParameters( - ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values()); + return buildParameters( + getTestParameters() + .withCfRuntimes() + .withDexRuntimesStartingFromIncluding(Version.V5_1_1) + .withAllApiLevels() + .build(), + CompilationMode.values(), + BooleanUtils.values()); } - public InliningRetraceTest(Backend backend, CompilationMode mode, boolean value) { - super(backend, mode, value); + public InliningRetraceTest(TestParameters parameters, CompilationMode mode, boolean value) { + super(parameters, mode, value); } @Override @@ -43,7 +46,12 @@ } private int expectedActualStackTraceHeight() { - return mode == CompilationMode.RELEASE ? 1 : 4; + int height = mode == CompilationMode.RELEASE ? 1 : 4; + if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) { + // Dalvik places a stack trace line in the bottom. + height += 1; + } + return height; } @Test @@ -60,7 +68,7 @@ @Test public void testLineNumberTableOnly() throws Exception { assumeTrue(compat); - assumeTrue(backend == Backend.DEX); + assumeTrue(parameters.isDexRuntime()); runTest( ImmutableList.of("-keepattributes LineNumberTable"), (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> { @@ -72,7 +80,7 @@ @Test public void testNoLineNumberTable() throws Exception { assumeTrue(compat); - assumeTrue(backend == Backend.DEX); + assumeTrue(parameters.isDexRuntime()); runTest( ImmutableList.of(), (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> {
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java index c84c685..2200ea7 100644 --- a/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java +++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/RetraceTestBase.java
@@ -8,6 +8,8 @@ import com.android.tools.r8.R8TestBuilder; import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine; import com.google.common.collect.ImmutableList; import java.util.Collection; import java.util.List; @@ -15,12 +17,13 @@ import org.junit.Before; public abstract class RetraceTestBase extends TestBase { - protected Backend backend; + + protected TestParameters parameters; protected CompilationMode mode; protected boolean compat; - public RetraceTestBase(Backend backend, CompilationMode mode, boolean compat) { - this.backend = backend; + public RetraceTestBase(TestParameters parameters, CompilationMode mode, boolean compat) { + this.parameters = parameters; this.mode = mode; this.compat = compat; } @@ -39,24 +42,25 @@ public void setup() throws Exception { // Get the expected stack trace by running on the JVM. expectedStackTrace = - testForJvm() - .addTestClasspath() - .run(getMainClass()) + testForRuntime(parameters) + .addProgramClasses(getClasses()) + .run(parameters.getRuntime(), getMainClass()) .assertFailure() - .map(StackTrace::extractFromJvm); + .map(StackTrace::extract); } public void runTest(List<String> keepRules, BiConsumer<StackTrace, StackTrace> checker) throws Exception { R8TestRunResult result = - (compat ? testForR8Compat(backend) : testForR8(backend)) + (compat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend())) .setMode(mode) .enableProguardTestOptions() .addProgramClasses(getClasses()) .addKeepMainRule(getMainClass()) .addKeepRules(keepRules) + .setMinApi(parameters.getApiLevel()) .apply(this::configure) - .run(getMainClass()) + .run(parameters.getRuntime(), getMainClass()) .assertFailure(); // Extract actual stack trace and retraced stack trace from failed run result. @@ -70,4 +74,9 @@ checker.accept(actualStackTrace, retracedStackTrace); } + + protected boolean isNotDalvikNativeStartMethod(StackTraceLine retracedStackTraceLine) { + return !(retracedStackTraceLine.className.equals("dalvik.system.NativeStart") + && retracedStackTraceLine.methodName.equals("main")); + } }
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java index be74127..92b78b6 100644 --- a/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java +++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/StackTrace.java
@@ -11,6 +11,7 @@ import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.DexVm; +import com.android.tools.r8.utils.Box; import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.StringUtils; import com.google.common.base.Equivalence; @@ -203,6 +204,20 @@ return extractFromJvm(result.getStdErr()); } + public static StackTrace extract(SingleTestRunResult<?> result) { + Box<StackTrace> stackTraceBox = new Box<>(); + result.forCfRuntime( + ignored -> { + stackTraceBox.set(extractFromJvm(result.getStdErr())); + }); + result.forDexRuntimeSatisfying( + version -> true, + ignored -> { + stackTraceBox.set(extractFromArt(result.getStdErr())); + }); + return stackTraceBox.get(); + } + public StackTrace retrace(String map, Path tempFolder) throws IOException { Path mapFile = tempFolder.resolve("map"); Path stackTraceFile = tempFolder.resolve("stackTrace");
diff --git a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java index 41c6451..54589a7 100644 --- a/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java +++ b/src/test/java/com/android/tools/r8/naming/retraceproguard/VerticalClassMergingRetraceTest.java
@@ -12,7 +12,8 @@ import com.android.tools.r8.CompilationMode; import com.android.tools.r8.NeverInline; import com.android.tools.r8.R8TestBuilder; -import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.naming.retraceproguard.StackTrace.StackTraceLine; import com.android.tools.r8.utils.BooleanUtils; import com.google.common.collect.ImmutableList; @@ -28,14 +29,21 @@ public class VerticalClassMergingRetraceTest extends RetraceTestBase { private Set<StackTraceLine> haveSeenLines = new HashSet<>(); - @Parameters(name = "Backend: {0}, mode: {1}, compat: {2}") + @Parameters(name = "{0}, mode: {1}, compat: {2}") public static Collection<Object[]> data() { return buildParameters( - ToolHelper.getBackends(), CompilationMode.values(), BooleanUtils.values()); + getTestParameters() + .withCfRuntimes() + .withDexRuntimesStartingFromIncluding(Version.V5_1_1) + .withAllApiLevels() + .build(), + CompilationMode.values(), + BooleanUtils.values()); } - public VerticalClassMergingRetraceTest(Backend backend, CompilationMode mode, boolean compat) { - super(backend, mode, compat); + public VerticalClassMergingRetraceTest( + TestParameters parameters, CompilationMode mode, boolean compat) { + super(parameters, mode, compat); } @Override @@ -55,7 +63,12 @@ private int expectedActualStackTraceHeight() { // In RELEASE mode, a synthetic bridge will be added by vertical class merger. - return mode == CompilationMode.RELEASE ? 3 : 2; + int height = mode == CompilationMode.RELEASE ? 3 : 2; + if (parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik()) { + // Dalvik places a stack trace line in the bottom. + height += 1; + } + return height; } private boolean filterSynthesizedMethodWhenLineNumberAvailable( @@ -79,7 +92,10 @@ mode == CompilationMode.DEBUG ? retracedStackTrace : retracedStackTrace.filter(this::filterSynthesizedMethodWhenLineNumberAvailable); - assertThat(reprocessedStackTrace, isSameExceptForFileName(expectedStackTrace)); + assertThat( + reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod), + isSameExceptForFileName( + expectedStackTrace.filter(this::isNotDalvikNativeStartMethod))); assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); }); } @@ -87,7 +103,7 @@ @Test public void testLineNumberTableOnly() throws Exception { assumeTrue(compat); - assumeTrue(backend == Backend.DEX); + assumeTrue(parameters.isDexRuntime()); runTest( ImmutableList.of("-keepattributes LineNumberTable"), (StackTrace actualStackTrace, StackTrace retracedStackTrace) -> { @@ -95,7 +111,10 @@ mode == CompilationMode.DEBUG ? retracedStackTrace : retracedStackTrace.filter(this::filterSynthesizedMethodWhenLineNumberAvailable); - assertThat(reprocessedStackTrace, isSameExceptForFileName(expectedStackTrace)); + assertThat( + reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod), + isSameExceptForFileName( + expectedStackTrace.filter(this::isNotDalvikNativeStartMethod))); assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); }); } @@ -103,7 +122,7 @@ @Test public void testNoLineNumberTable() throws Exception { assumeTrue(compat); - assumeTrue(backend == Backend.DEX); + assumeTrue(parameters.isDexRuntime()); haveSeenLines.clear(); runTest( ImmutableList.of(), @@ -113,7 +132,9 @@ ? retracedStackTrace : retracedStackTrace.filter(this::filterSynthesizedMethod); assertThat( - reprocessedStackTrace, isSameExceptForFileNameAndLineNumber(expectedStackTrace)); + reprocessedStackTrace.filter(this::isNotDalvikNativeStartMethod), + isSameExceptForFileNameAndLineNumber( + expectedStackTrace.filter(this::isNotDalvikNativeStartMethod))); assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size()); }); }
diff --git a/src/test/java/com/android/tools/r8/naming/sourcefile/SourceFileAttributeCompatTest.java b/src/test/java/com/android/tools/r8/naming/sourcefile/SourceFileAttributeCompatTest.java new file mode 100644 index 0000000..ac17f3c --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/sourcefile/SourceFileAttributeCompatTest.java
@@ -0,0 +1,197 @@ +// 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.naming.sourcefile; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.ProguardVersion; +import com.android.tools.r8.SingleTestRunResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestShrinkerBuilder; +import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import java.util.function.Supplier; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class SourceFileAttributeCompatTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withSystemRuntime().build(); + } + + public SourceFileAttributeCompatTest(TestParameters parameters) { + this.parameters = parameters; + } + + private String getOriginalSourceFile() { + return new Exception().getStackTrace()[0].getFileName(); + } + + private void commonSetUp(TestShrinkerBuilder<?, ?, ?, ?, ?> builder) { + builder + .addProgramClasses(TestClass.class, SemiKept.class, NonKept.class) + .addKeepMainRule(TestClass.class) + .addKeepRules("-keep,allowshrinking class " + SemiKept.class.getName() + " { *; }"); + } + + private void checkSourceFileIsRemoved(SingleTestRunResult<?> result) throws Exception { + // TODO(b/202368282): We should likely emit a "default" source file attribute rather than strip. + checkSourceFile(result, null, null, null); + } + + private void checkSourceFileIsOriginal(SingleTestRunResult<?> result) throws Exception { + String originalSourceFile = getOriginalSourceFile(); + checkSourceFile(result, originalSourceFile, originalSourceFile, originalSourceFile); + } + + private void checkSourceFile( + SingleTestRunResult<?> result, String keptValue, String semiKeptValue, String nonKeptValue) + throws Exception { + result.assertFailure(); + result.inspectOriginalStackTrace( + stackTrace -> { + StackTraceLine nonKeptLine = stackTrace.get(0); + StackTraceLine semiKeptLine = stackTrace.get(1); + StackTraceLine keptLine = stackTrace.get(4); + assertEquals(getExpectedSourceFile(nonKeptValue), nonKeptLine.fileName); + assertEquals(getExpectedSourceFile(semiKeptValue), semiKeptLine.fileName); + assertEquals(getExpectedSourceFile(keptValue), keptLine.fileName); + }); + result.inspectFailure( + inspector -> { + ClassSubject testClass = inspector.clazz(TestClass.class); + ClassSubject semiKept = inspector.clazz(SemiKept.class); + ClassSubject nonKept = inspector.clazz(NonKept.class); + assertEquals(keptValue, getSourceFileString(testClass)); + assertEquals(semiKeptValue, getSourceFileString(semiKept)); + assertEquals(nonKeptValue, getSourceFileString(nonKept)); + }); + } + + private String getSourceFileString(ClassSubject subject) { + DexString sourceFile = subject.getDexProgramClass().getSourceFile(); + return sourceFile == null ? null : sourceFile.toString(); + } + + private String getExpectedSourceFile(String expectedSourceFileValue) { + return expectedSourceFileValue == null ? "Unknown Source" : expectedSourceFileValue; + } + + private <RR extends SingleTestRunResult<RR>> void testJustKeepMain( + TestShrinkerBuilder<?, ?, ?, RR, ?> builder, boolean fullMode) throws Exception { + // If the source file attribute is not kept then all compilers will strip it throughout. + commonSetUp(builder); + builder.run(parameters.getRuntime(), TestClass.class).apply(this::checkSourceFileIsRemoved); + } + + private <RR extends SingleTestRunResult<RR>> void testDontObfuscate( + TestShrinkerBuilder<?, ?, ?, RR, ?> builder, boolean fullMode) throws Exception { + // If minification is off then compat compilers retain it, full mode will remove it. + commonSetUp(builder); + builder + .addKeepRules("-dontobfuscate") + .run(parameters.getRuntime(), TestClass.class) + .applyIf(fullMode, this::checkSourceFileIsRemoved, this::checkSourceFileIsOriginal); + } + + private <RR extends SingleTestRunResult<RR>> void testDontOptimize( + TestShrinkerBuilder<?, ?, ?, RR, ?> builder, boolean fullMode) throws Exception { + // No effect from -dontoptimize + commonSetUp(builder); + builder + .addKeepRules("-dontoptimize") + .run(parameters.getRuntime(), TestClass.class) + .apply(this::checkSourceFileIsRemoved); + } + + private <RR extends SingleTestRunResult<RR>> void testDontShrink( + TestShrinkerBuilder<?, ?, ?, RR, ?> builder, boolean fullMode) throws Exception { + // No effect from -dontshrink + commonSetUp(builder); + builder + .addKeepRules("-dontshrink") + .run(parameters.getRuntime(), TestClass.class) + .apply(this::checkSourceFileIsRemoved); + } + + private <RR extends SingleTestRunResult<RR>> void testKeepSourceFileAttribute( + TestShrinkerBuilder<?, ?, ?, RR, ?> builder, boolean fullMode) throws Exception { + // If the source file attribute is kept, then PG and compat R8 will preserve it in original + // form for every input class. R8 will only preserve it for (soft) pinned classes. Others will + // be replaced by 'SourceFile'. The use of 'SourceFile' is to ensure VMs still print lines. + // TODO(b/202367773): R8 (non-compat) should rather replace it for all classes like line opt. + String originalSourceFile = getOriginalSourceFile(); + String residualSourceFile = fullMode ? "SourceFile" : originalSourceFile; + commonSetUp(builder); + builder + .addKeepAttributeSourceFile() + .run(parameters.getRuntime(), TestClass.class) + .apply( + result -> + checkSourceFile( + result, originalSourceFile, originalSourceFile, residualSourceFile)); + } + + private <RR extends SingleTestRunResult<RR>> void runAllTests( + Supplier<TestShrinkerBuilder<?, ?, ?, RR, ?>> builder, boolean fullMode) throws Exception { + testJustKeepMain(builder.get(), fullMode); + testDontObfuscate(builder.get(), fullMode); + testDontOptimize(builder.get(), fullMode); + testDontShrink(builder.get(), fullMode); + testKeepSourceFileAttribute(builder.get(), fullMode); + } + + @Test + public void testR8() throws Exception { + runAllTests(() -> testForR8(parameters.getBackend()), true); + } + + @Test + public void testCompatR8() throws Exception { + runAllTests(() -> testForR8Compat(parameters.getBackend()), false); + } + + @Test + public void testPG() throws Exception { + runAllTests(() -> testForProguard(ProguardVersion.V7_0_0).addDontWarn(getClass()), false); + } + + static class NonKept { + @Override + public String toString() { + throw new RuntimeException("BOOM!"); + } + } + + static class SemiKept { + final Object o; + + public SemiKept(Object o) { + this.o = o; + } + + @Override + public String toString() { + return o.toString(); + } + } + + static class TestClass { + public static void main(String[] args) { + System.out.println( + System.nanoTime() > 0 + ? new SemiKept(System.nanoTime() > 0 ? new NonKept() : null) + : null); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UpwardsInterfacePropagationToLibraryOrClasspathMethodTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UpwardsInterfacePropagationToLibraryOrClasspathMethodTest.java new file mode 100644 index 0000000..aa57ae6 --- /dev/null +++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/UpwardsInterfacePropagationToLibraryOrClasspathMethodTest.java
@@ -0,0 +1,202 @@ +// 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.optimize.argumentpropagation; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +import com.google.common.collect.ImmutableList; +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; + +// This is a regression test for b/202074964. +@RunWith(Parameterized.class) +public class UpwardsInterfacePropagationToLibraryOrClasspathMethodTest extends TestBase { + + private enum LibraryOrClasspath { + LIBRARY, + CLASSPATH; + + private boolean isLibrary() { + return this == LIBRARY; + } + } + + @Parameter(0) + public TestParameters parameters; + + @Parameter(1) + public LibraryOrClasspath libraryOrClasspath; + + @Parameters(name = "{0} {1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(), + LibraryOrClasspath.values()); + } + + private static final String EXPECTED_OUTPUT = + StringUtils.lines("LibraryClass::libraryMethod(false)", "ProgramClass2::libraryMethod(true)"); + private static final List<Class<?>> LIBRARY_CLASSES = ImmutableList.of(LibraryClass.class); + private static final List<Class<?>> PROGRAM_CLASSES = + ImmutableList.of( + ProgramClass.class, + Delegate.class, + Delegater.class, + AnotherProgramClass.class, + AnotherDelegate.class, + AnotherDelegator.class, + TestClass.class); + + @Test + public void testRuntime() throws Exception { + testForRuntime(parameters) + .addProgramClasses(LIBRARY_CLASSES) + .addProgramClasses(PROGRAM_CLASSES) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.S)) + .apply( + b -> { + if (libraryOrClasspath.isLibrary()) { + b.addLibraryClasses(LIBRARY_CLASSES); + } else { + b.addClasspathClasses(LIBRARY_CLASSES); + } + }) + .addProgramClasses(PROGRAM_CLASSES) + .addKeepMainRule(TestClass.class) + .setMinApi(parameters.getApiLevel()) + .enableNoVerticalClassMergingAnnotations() + .enableNoHorizontalClassMergingAnnotations() + .enableNeverClassInliningAnnotations() + .enableInliningAnnotations() + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .compile() + .addRunClasspathClasses(LibraryClass.class) + .run(parameters.getRuntime(), TestClass.class) + .inspect( + inspector -> { + assertThat( + inspector.clazz(Delegate.class).method("void", "libraryMethod", "boolean"), + isPresent()); + // Check that boolean argument to libraryMethod was removed for AnotherProgramClass. + inspector + .clazz(AnotherProgramClass.class) + .forAllMethods( + method -> assertEquals(method.getFinalSignature().toDescriptor(), "()V")); + }) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + public static class LibraryClass { + public void libraryMethod(boolean visible) { + System.out.println("LibraryClass::libraryMethod(" + visible + ")"); + } + } + + @NoVerticalClassMerging + public interface Delegate { + void libraryMethod(boolean visible); + } + + @NeverClassInline + public static class ProgramClass extends LibraryClass implements Delegate { + Delegater delegater; + + public ProgramClass() { + delegater = new Delegater(this); + } + + @NeverInline + public void m() { + delegater.m(); + } + } + + @NoVerticalClassMerging + @NoHorizontalClassMerging + @NeverClassInline + public static class Delegater { + Delegate delegate; + + Delegater(Delegate delegate) { + this.delegate = delegate; + } + + public void m() { + delegate.libraryMethod(false); + } + } + + @NoVerticalClassMerging + @NoHorizontalClassMerging + public interface AnotherDelegate { + void libraryMethod(boolean visible); + } + + @NeverClassInline + @NoHorizontalClassMerging + public static class AnotherProgramClass implements AnotherDelegate { + AnotherDelegator delegater; + + public AnotherProgramClass() { + delegater = new AnotherDelegator(this); + } + + @NeverInline + public void libraryMethod(boolean visible) { + System.out.println("ProgramClass2::libraryMethod(" + visible + ")"); + } + + @NeverInline + public void m() { + delegater.m(); + } + } + + @NoVerticalClassMerging + @NeverClassInline + public static class AnotherDelegator { + AnotherDelegate delegate; + + AnotherDelegator(AnotherDelegate delegate) { + this.delegate = delegate; + } + + public void m() { + delegate.libraryMethod(true); + } + } + + public static class TestClass { + + public static void main(String[] args) { + new ProgramClass().m(); + new AnotherProgramClass().m(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java index 3bdef43..17ea86d 100644 --- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java +++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -27,10 +27,12 @@ import com.android.tools.r8.retrace.stacktraces.AmbiguousWithSignatureStackTrace; import com.android.tools.r8.retrace.stacktraces.AutoStackTrace; import com.android.tools.r8.retrace.stacktraces.CircularReferenceStackTrace; +import com.android.tools.r8.retrace.stacktraces.ClassWithDashStackTrace; import com.android.tools.r8.retrace.stacktraces.ColonInFileNameStackTrace; import com.android.tools.r8.retrace.stacktraces.DifferentLineNumberSpanStackTrace; import com.android.tools.r8.retrace.stacktraces.FileNameExtensionStackTrace; import com.android.tools.r8.retrace.stacktraces.FoundMethodVerboseStackTrace; +import com.android.tools.r8.retrace.stacktraces.IdentityMappingStackTrace; import com.android.tools.r8.retrace.stacktraces.InlineFileNameStackTrace; import com.android.tools.r8.retrace.stacktraces.InlineFileNameWithInnerClassesStackTrace; import com.android.tools.r8.retrace.stacktraces.InlineInOutlineStackTrace; @@ -100,6 +102,11 @@ } @Test + public void testClassWithDashStackTrace() throws Exception { + runRetraceTest(new ClassWithDashStackTrace()); + } + + @Test public void testCanMapExceptionClass() throws Exception { runRetraceTest(new ObfucatedExceptionClassStackTrace()); } @@ -352,6 +359,11 @@ runRetraceTest(new OutsideLineRangeStackTraceTest()); } + @Test + public void testIdentityMappingStackTrace() throws Exception { + runRetraceTest(new IdentityMappingStackTrace()); + } + private void inspectRetraceTest( StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) { inspection.accept(
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 77b8aea..4deb785 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
@@ -80,6 +80,11 @@ } @Override + public List<Class<?>> getPendingAdditionalClassesForTests() { + return ImmutableList.of(); + } + + @Override public List<String> getVmArgs() { return ImmutableList.of(); }
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/ClassWithDashStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/ClassWithDashStackTrace.java new file mode 100644 index 0000000..07fb551 --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/ClassWithDashStackTrace.java
@@ -0,0 +1,52 @@ +// 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.stacktraces; + +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.util.List; + +public class ClassWithDashStackTrace implements StackTraceForTest { + + @Override + public List<String> obfuscatedStackTrace() { + return ImmutableList.of( + "java.lang.NullPointerException", + "\tat I$-CC.staticMethod(I.java:66)", + "\tat Main.main(Main.java:73)"); + } + + @Override + public String mapping() { + return StringUtils.lines( + "# {\"id\":\"com.android.tools.r8.mapping\",\"version\":\"1.0\"}", + "Unused -> I$-CC:", + "# {\"id\":\"com.android.tools.r8.synthesized\"}", + " 66:66:void I.staticMethod() -> staticMethod", + " 66:66:void staticMethod():0 -> staticMethod", + " # {\"id\":\"com.android.tools.r8.synthesized\"}"); + } + + @Override + public List<String> retracedStackTrace() { + return ImmutableList.of( + "java.lang.NullPointerException", + "\tat I.staticMethod(I.java:66)", + "\tat Main.main(Main.java:73)"); + } + + @Override + public List<String> retraceVerboseStackTrace() { + return ImmutableList.of( + "java.lang.NullPointerException", + "\tat I.void staticMethod()(I.java:66)", + "\tat Main.main(Main.java:73)"); + } + + @Override + public int expectedWarnings() { + return 0; + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/IdentityMappingStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/IdentityMappingStackTrace.java new file mode 100644 index 0000000..40992ae --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/IdentityMappingStackTrace.java
@@ -0,0 +1,59 @@ +// 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.stacktraces; + +import com.android.tools.r8.utils.StringUtils; +import java.util.Arrays; +import java.util.List; + +public class IdentityMappingStackTrace implements StackTraceForTest { + + @Override + public List<String> obfuscatedStackTrace() { + return Arrays.asList( + "java.lang.IndexOutOfBoundsException", "\tat a.a(:10)", "\tat b.a(:11)", "\tat c.a(:12)"); + } + + @Override + public String mapping() { + return StringUtils.lines( + "com.android.tools.r8.One -> a:", + " 10:10:void foo(int) -> a", + "com.android.tools.r8.Other -> b:", + " 11:11:void bar(int, int) -> a", // This is an inline frame + " 11:11:boolean baz(int, int) -> a", + "com.android.tools.r8.Third -> c:", + " 12:12:void qux(int) -> a", // This is also an inline frame + " 12:12:void other(int, int) -> b", + " 12:12:boolean quux(int, int) -> a"); + } + + @Override + public List<String> retracedStackTrace() { + return Arrays.asList( + "java.lang.IndexOutOfBoundsException", + "\tat com.android.tools.r8.One.foo(One.java:10)", + "\tat com.android.tools.r8.Other.bar(Other.java:11)", + "\tat com.android.tools.r8.Other.baz(Other.java:11)", + "\tat com.android.tools.r8.Third.qux(Third.java:12)", + "\tat com.android.tools.r8.Third.quux(Third.java:12)"); + } + + @Override + public List<String> retraceVerboseStackTrace() { + return Arrays.asList( + "java.lang.IndexOutOfBoundsException", + "\tat com.android.tools.r8.One.void foo(int)(One.java:10)", + "\tat com.android.tools.r8.Other.void bar(int,int)(Other.java:11)", + "\tat com.android.tools.r8.Other.boolean baz(int,int)(Other.java:11)", + "\tat com.android.tools.r8.Third.void qux(int)(Third.java:12)", + "\tat com.android.tools.r8.Third.boolean quux(int,int)(Third.java:12)"); + } + + @Override + public int expectedWarnings() { + return 0; + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java index 277e4b3..ec69283 100644 --- a/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java +++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/OverloadSameLineTest.java
@@ -31,23 +31,18 @@ return Arrays.asList( "Exception in thread \"main\" java.lang.NullPointerException", "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:7)", - "<OR> Exception in thread \"main\" java.lang.NullPointerException", - "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:15)", - "<OR> Exception in thread \"main\" java.lang.NullPointerException", - "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:13)"); + "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:13)", + "\tat com.android.tools.r8.naming.retrace.Main.overload(Main.java:15)"); } @Override public List<String> retraceVerboseStackTrace() { return Arrays.asList( - "There are 3 ambiguous stack traces.", "Exception in thread \"main\" java.lang.NullPointerException", "\tat com.android.tools.r8.naming.retrace.Main.void overload()(Main.java:7)", - "<OR> Exception in thread \"main\" java.lang.NullPointerException", - "\tat com.android.tools.r8.naming.retrace.Main.void overload(int)(Main.java:15)", - "<OR> Exception in thread \"main\" java.lang.NullPointerException", "\tat com.android.tools.r8.naming.retrace.Main.void" - + " overload(java.lang.String)(Main.java:13)"); + + " overload(java.lang.String)(Main.java:13)", + "\tat com.android.tools.r8.naming.retrace.Main.void overload(int)(Main.java:15)"); } @Override
diff --git a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java index b9580ed..147cc34 100644 --- a/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java +++ b/src/test/java/com/android/tools/r8/utils/structural/StructuralItemsTest.java
@@ -88,7 +88,9 @@ } private String getHash(StructuralItem<?> item) { - return item.hashForTesting(); + HasherWrapper hasherWrapper = HasherWrapper.sha256Hasher(); + item.hash(hasherWrapper); + return hasherWrapper.hashCodeAsString(); } @Test
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 index 759844b..ffee6ee 100644 --- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 +++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@ -8ddcb2b3cd52273413a538b22438e996e5c0dfcb \ No newline at end of file +36741d08e769bd2c5c201698161b3407507c7535 \ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py index 504496e..fb3b901 100755 --- a/tools/compiledump.py +++ b/tools/compiledump.py
@@ -250,6 +250,22 @@ return True return None +def determine_properties(build_properties): + args = [] + for key, value in build_properties.items(): + # When writing dumps all system properties starting with com.android.tools.r8 + # are written to the build.properties file in the format + # system-property-com.android.tools.r8.XXX=<value> + if key.startswith('system-property-'): + name = key[len('system-property-'):] + if name.endswith('dumpinputtofile') or name.endswith('dumpinputtodirectory'): + continue + if len(value) == 0: + args.append('-D' + name) + else: + args.append('-D' + name + '=' + value) + return args + def download_distribution(args, version, temp): if version == 'main': return utils.R8_JAR if args.nolib else utils.R8LIB_JAR @@ -327,6 +343,7 @@ cmd.append('-Dcom.android.tools.r8.printtimes=1') if hasattr(args, 'properties'): cmd.extend(args.properties); + cmd.extend(determine_properties(build_properties)) cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)]) if compiler == 'd8': cmd.append('com.android.tools.r8.D8')
diff --git a/tools/run_on_app.py b/tools/run_on_app.py index 73c932b..440cd97 100755 --- a/tools/run_on_app.py +++ b/tools/run_on_app.py
@@ -13,7 +13,7 @@ import time import archive -import as_utils +import gradle import gmail_data import gmscore_data import golem @@ -653,7 +653,7 @@ args.extend(inputs) - t0 = time.time() + t0 = None if options.dump_args_file: with open(options.dump_args_file, 'w') as args_file: args_file.writelines([arg + os.linesep for arg in args]) @@ -694,8 +694,11 @@ if options.hash: jar = os.path.join(utils.LIBS, 'r8-' + options.hash + '.jar') main = 'com.android.tools.r8.' + options.compiler.upper() + if should_build(options): + gradle.RunGradle(['r8lib' if tool.startswith('r8lib') else 'r8']) + t0 = time.time() exit_code = toolhelper.run(tool, args, - build=should_build(options), + build=False, debug=not options.no_debug, profile=options.profile, track_memory_file=options.track_memory_to_file,