Revert "Revert "Verify mapping of field and method signatures to original program""
This reverts commit 9f401d2968bd87ac04b2f6c91c644bf9c5bc1221.
Reason for revert: Reland "Verify mapping of field and method signatures to original program"
Bug: 120118197
Change-Id: I313f02711cac6c7c06a45cb9c5d73e25ae8beda3
diff --git a/src/main/java/com/android/tools/r8/JarSizeCompare.java b/src/main/java/com/android/tools/r8/JarSizeCompare.java
index aa16b1f..2aa124a 100644
--- a/src/main/java/com/android/tools/r8/JarSizeCompare.java
+++ b/src/main/java/com/android/tools/r8/JarSizeCompare.java
@@ -332,7 +332,7 @@
MethodSignature originalSignature =
proguardMap == null
? null
- : ((MethodSignature) proguardMap.originalSignatureOf(dexEncodedMethod.method));
+ : proguardMap.originalSignatureOf(dexEncodedMethod.method);
MethodSignature signature = MethodSignature.fromDexMethod(dexEncodedMethod.method);
consumer.accept(
originalSignature == null ? signature : originalSignature, dexEncodedMethod);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index ee45cf4..737505c 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -630,6 +630,13 @@
// Validity checks.
assert application.classes().stream().allMatch(DexClass::isValid);
assert rootSet.verifyKeptItemsAreKept(application, appView.appInfo(), options);
+ assert appView
+ .graphLense()
+ .verifyMappingToOriginalProgram(
+ application.classesWithDeterministicOrder(),
+ new ApplicationReader(inputApp.withoutMainDexList(), options, timing)
+ .read(executorService),
+ appView.dexItemFactory());
// Report synthetic rules (only for testing).
// TODO(b/120959039): Move this to being reported through the graph consumer.
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 79ea482..5be17c6 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -716,7 +716,7 @@
private void appendMethod(DexMethod method) {
if (mapper != null) {
- MethodSignature signature = (MethodSignature) mapper.originalSignatureOf(method);
+ MethodSignature signature = mapper.originalSignatureOf(method);
builder.append(mapper.originalNameOf(method.holder)).append('.');
builder.append(signature.name).append(signature.toDescriptor());
return;
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 1d7555d..31edee9 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -3,10 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.DISPATCH_CLASS_NAME_SUFFIX;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
+
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -441,6 +447,15 @@
return isPrimitiveType((char) descriptor.content[1]);
}
+ public boolean isD8R8SynthesizedClassType() {
+ String name = toSourceString();
+ return name.contains(COMPANION_CLASS_NAME_SUFFIX)
+ || name.contains(DISPATCH_CLASS_NAME_SUFFIX)
+ || name.contains(LAMBDA_CLASS_NAME_PREFIX)
+ || name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX)
+ || name.contains(OutlineOptions.CLASS_NAME);
+ }
+
public int elementSizeForPrimitiveArrayType() {
assert isPrimitiveArrayType();
switch (descriptor.content[1]) {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 247a5fe..50c91eb 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -12,9 +12,12 @@
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import java.util.ArrayDeque;
import java.util.Collections;
+import java.util.Deque;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
@@ -521,6 +524,82 @@
return builder.build();
}
+ public boolean verifyMappingToOriginalProgram(
+ Iterable<DexProgramClass> classes,
+ DexApplication originalApplication,
+ DexItemFactory dexItemFactory) {
+ // Collect all original fields and methods for efficient querying.
+ Set<DexField> originalFields = Sets.newIdentityHashSet();
+ Set<DexMethod> originalMethods = Sets.newIdentityHashSet();
+ for (DexProgramClass clazz : originalApplication.classes()) {
+ for (DexEncodedField field : clazz.fields()) {
+ originalFields.add(field.field);
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ originalMethods.add(method.method);
+ }
+ }
+
+ // Check that all fields and methods in the generated program can be mapped back to one of the
+ // original fields or methods.
+ for (DexProgramClass clazz : classes) {
+ if (clazz.type.isD8R8SynthesizedClassType()) {
+ continue;
+ }
+ for (DexEncodedField field : clazz.fields()) {
+ DexField originalField = getOriginalFieldSignature(field.field);
+ assert originalFields.contains(originalField)
+ : "Unable to map field `" + field.field.toSourceString() + "` back to original program";
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.accessFlags.isSynthetic()) {
+ // This could be a bridge that has been inserted, for example, as a result of member
+ // rebinding. Consider only skipping the check below for methods that have been
+ // synthesized by R8.
+ continue;
+ }
+ DexMethod originalMethod = getOriginalMethodSignature(method.method);
+ assert originalMethods.contains(originalMethod)
+ || verifyIsBridgeMethod(
+ originalMethod, originalApplication, originalMethods, dexItemFactory)
+ : "Unable to map method `"
+ + originalMethod.toSourceString()
+ + "` back to original program";
+ }
+ }
+
+ return true;
+ }
+
+ // Check if `method` is a bridge method for a method that is in the original application.
+ // This is needed because member rebinding synthesizes bridge methods for visibility.
+ private static boolean verifyIsBridgeMethod(
+ DexMethod method,
+ DexApplication originalApplication,
+ Set<DexMethod> originalMethods,
+ DexItemFactory dexItemFactory) {
+ Deque<DexType> worklist = new ArrayDeque<>();
+ Set<DexType> visited = Sets.newIdentityHashSet();
+ worklist.add(method.holder);
+ while (!worklist.isEmpty()) {
+ DexType holder = worklist.removeFirst();
+ if (!visited.add(holder)) {
+ // Already visited previously.
+ continue;
+ }
+ DexMethod targetMethod = dexItemFactory.createMethod(holder, method.proto, method.name);
+ if (originalMethods.contains(targetMethod)) {
+ return true;
+ }
+ DexClass clazz = originalApplication.definitionFor(holder);
+ if (clazz != null) {
+ worklist.add(clazz.superType);
+ Collections.addAll(worklist, clazz.interfaces.values);
+ }
+ }
+ return false;
+ }
+
private static class IdentityGraphLense extends GraphLense {
private static IdentityGraphLense INSTANCE = new IdentityGraphLense();
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 4ecda95..e96acfb 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
@@ -1176,8 +1176,11 @@
}
if (paramsCheckedForNull.length() > 0) {
// Check if collected information conforms to non-null parameter hints in Kotlin metadata.
+ // These hints are on the original holder. To find the original holder, we first find the
+ // original method signature (this could have changed as a result of, for example, class
+ // merging). Then, we find the type that now corresponds to the the original holder.
DexMethod originalSignature = graphLense().getOriginalMethodSignature(method.method);
- DexClass originalHolder = definitionFor(originalSignature.holder);
+ DexClass originalHolder = definitionFor(graphLense().lookupType(originalSignature.holder));
if (originalHolder.hasKotlinInfo()) {
KotlinInfo kotlinInfo = originalHolder.getKotlinInfo();
if (kotlinInfo.hasNonNullParameterHints()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 08e4ee5..707078f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -52,6 +52,7 @@
// Public for testing.
public static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
+ public static final String LAMBDA_GROUP_CLASS_NAME_PREFIX = "-$$LambdaGroup$";
static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 9cdb95c..bcc0af3 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -244,7 +244,7 @@
return classNaming.originalName + " " + memberNaming.signature.toString();
}
- public Signature originalSignatureOf(DexMethod method) {
+ public MethodSignature originalSignatureOf(DexMethod method) {
String decoded = descriptorToJavaType(method.holder.descriptor.toString());
MethodSignature memberSignature = getRenamedMethodSignature(method);
ClassNaming classNaming = getClassNaming(decoded);
@@ -255,7 +255,7 @@
if (memberNaming == null) {
return memberSignature;
}
- return memberNaming.signature;
+ return (MethodSignature) memberNaming.signature;
}
public FieldSignature originalSignatureOf(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
index 6232fb3..d5add45 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
@@ -214,7 +214,7 @@
applyClassMappingOnTheFly(originalField.clazz),
applyClassMappingOnTheFly(originalField.type),
appInfo.dexItemFactory.createString(appliedSignature.name));
- lenseBuilder.map(originalField, appliedField);
+ lenseBuilder.move(originalField, appliedField);
}
private void applyMethodMapping(DexMethod originalMethod, MemberNaming memberNaming) {
@@ -224,7 +224,7 @@
applyClassMappingOnTheFly(originalMethod.holder),
applyClassMappingOnTheFly(originalMethod.proto),
appInfo.dexItemFactory.createString(appliedSignature.name));
- lenseBuilder.map(originalMethod, appliedMethod);
+ lenseBuilder.move(originalMethod, appliedMethod);
}
private DexType applyClassMappingOnTheFly(DexType from) {
@@ -336,7 +336,7 @@
if (newHolderType != appliedMethod.holder || newProto != appliedMethod.proto) {
newMethod = appInfo.dexItemFactory.createMethod(
substituteType(newHolderType, encodedMethod), newProto, appliedMethod.name);
- lenseBuilder.map(encodedMethod.method, newMethod);
+ lenseBuilder.move(encodedMethod.method, newMethod);
} else {
newMethod = appliedMethod;
}
@@ -359,7 +359,7 @@
if (newHolderType != appliedField.clazz || newFieldType != appliedField.type) {
newField = appInfo.dexItemFactory.createField(
substituteType(newHolderType, null), newFieldType, appliedField.name);
- lenseBuilder.map(encodedField.field, newField);
+ lenseBuilder.move(encodedField.field, newField);
} else {
newField = appliedField;
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 49573db..28d1628 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -279,6 +279,19 @@
return mainDexClasses;
}
+ /** Returns a copy of this AndroidApp that does not have a main dex list. */
+ public AndroidApp withoutMainDexList() {
+ return new AndroidApp(
+ programResourceProviders,
+ programResourcesMainDescriptor,
+ classpathResourceProviders,
+ libraryResourceProviders,
+ archiveProvidersToClose,
+ proguardMapOutputData,
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
/**
* Write the dex program resources and proguard resource to @code{output}.
*/
diff --git a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
index aeeb0ec..092d06e 100644
--- a/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
+++ b/src/test/java/com/android/tools/r8/cf/BootstrapCurrentEqualityTest.java
@@ -71,12 +71,13 @@
.compile()
.outputJar();
} else {
- jar = testFolder.newFolder().toPath().resolve("out.zip");
+ jar = testFolder.newFolder().toPath().resolve("out.jar");
R8.run(
R8Command.builder()
- .setMode(CompilationMode.RELEASE)
- .addLibraryFiles(runtimeJar(Backend.CF))
+ .setMode(mode)
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_JAR)
+ .addProguardConfigurationFiles(MAIN_KEEP)
+ .addLibraryFiles(runtimeJar(Backend.CF))
.setOutput(jar, OutputMode.ClassFile)
.build());
}