Merge commit '073b18530351f39db03e7181dafcbba6558de885' into dev-release
Change-Id: If418b5d8e07f9b6ce9f0b0fbbe610bd094dce019
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
index 1be40fe..167d305 100644
--- a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
@@ -50,7 +50,7 @@
extends TreeFixerBase {
private final ClassMergerSharedData classMergerSharedData;
- private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
+ protected final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
protected final LB lensBuilder;
protected final MC mergedClasses;
@@ -81,14 +81,17 @@
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
preprocess();
Collection<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
- Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
+ Set<DexProgramClass> seen = Sets.newIdentityHashSet();
+ Iterables.filter(classes, DexProgramClass::isInterface)
+ .forEach(itf -> fixupInterfaceClass(itf, seen));
classes.forEach(this::fixupAttributes);
classes.forEach(this::fixupProgramClassSuperTypes);
// TODO(b/170078037): parallelize this code segment.
for (DexProgramClass root : getRoots()) {
- traverseProgramClassesDepthFirst(root, new DexMethodSignatureBiMap<>());
+ traverseProgramClassesDepthFirst(root, seen, new DexMethodSignatureBiMap<>());
}
+ assert seen.containsAll(appView.appInfo().classes());
postprocess();
GL lens = lensBuilder.build(appViewWithLiveness, mergedClasses);
new AnnotationFixer(appView, lens).run(appView.appInfo().classes(), executorService);
@@ -99,35 +102,27 @@
private List<DexProgramClass> getRoots() {
List<DexProgramClass> roots = new ArrayList<>();
for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (!clazz.isInterface() && !mergedClasses.isMergeSource(clazz.getType())) {
- DexProgramClass superClass =
- asProgramClassOrNull(appView.definitionFor(clazz.getSuperType(), clazz));
- if (superClass == null) {
- roots.add(clazz);
- }
+ if (isRoot(clazz)) {
+ roots.add(clazz);
}
}
return roots;
}
- private void traverseProgramClassesDepthFirst(
- DexProgramClass clazz, DexMethodSignatureBiMap<DexMethodSignature> state) {
- DexMethodSignatureBiMap<DexMethodSignature> newState = fixupProgramClass(clazz, state);
- for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(clazz)) {
- traverseProgramClassesDepthFirst(subclass, newState);
+ protected boolean isRoot(DexProgramClass clazz) {
+ if (clazz.isInterface()) {
+ return false;
}
- if (mergedClasses.isMergeTarget(clazz.getType())) {
- for (DexType sourceType : mergedClasses.getSourcesFor(clazz.getType())) {
- DexProgramClass sourceClass = appView.definitionFor(sourceType).asProgramClass();
- for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(sourceClass)) {
- if (subclass != clazz) {
- traverseProgramClassesDepthFirst(subclass, newState);
- }
- }
- }
- }
+ DexProgramClass superClass =
+ asProgramClassOrNull(appView.definitionFor(clazz.getSuperType(), clazz));
+ return superClass == null;
}
+ protected abstract void traverseProgramClassesDepthFirst(
+ DexProgramClass clazz,
+ Set<DexProgramClass> seen,
+ DexMethodSignatureBiMap<DexMethodSignature> state);
+
public void preprocess() {
// Intentionally empty.
}
@@ -161,7 +156,7 @@
clazz.setInterfaces(fixupInterfaces(clazz, clazz.getInterfaces()));
}
- private DexMethodSignatureBiMap<DexMethodSignature> fixupProgramClass(
+ protected DexMethodSignatureBiMap<DexMethodSignature> fixupProgramClass(
DexProgramClass clazz, DexMethodSignatureBiMap<DexMethodSignature> remappedVirtualMethods) {
assert !clazz.isInterface();
@@ -235,7 +230,8 @@
: method;
}
- private void fixupInterfaceClass(DexProgramClass iface) {
+ private void fixupInterfaceClass(DexProgramClass iface, Set<DexProgramClass> seen) {
+ assert seen.add(iface);
DexMethodSignatureBiMap<DexMethodSignature> remappedVirtualMethods =
DexMethodSignatureBiMap.empty();
MutableBidirectionalOneToOneMap<DexEncodedMethod, DexMethodSignature> newMethodSignatures =
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 3f3aa1e..7288292 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -146,7 +146,7 @@
if ((b & 0xC0) == 0x80) {
return (char) (((a & 0x1F) << 6) | (b & 0x3F));
}
- throw new UTFDataFormatException("bad second byte");
+ throw badSecondByteException(a, b);
}
if ((a & 0xf0) == 0xe0) {
int b = content[i++] & 0xff;
@@ -154,9 +154,9 @@
if ((b & 0xC0) == 0x80 && (c & 0xC0) == 0x80) {
return (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
}
- throw new UTFDataFormatException("bad second or third byte");
+ throw badSecondOrThirdByteException(a, b, c);
}
- throw new UTFDataFormatException("bad byte");
+ throw badByteException(a);
}
@Override
@@ -221,7 +221,7 @@
} else if ((a & 0xe0) == 0xc0) {
int b = content[p++] & 0xff;
if ((b & 0xC0) != 0x80) {
- throw new UTFDataFormatException("bad second byte");
+ throw badSecondByteException(a, b);
}
out[s] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
if (++s == prefixLength) {
@@ -231,14 +231,14 @@
int b = content[p++] & 0xff;
int c = content[p++] & 0xff;
if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
- throw new UTFDataFormatException("bad second or third byte");
+ throw badSecondOrThirdByteException(a, b, c);
}
out[s] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
if (++s == prefixLength) {
return s;
}
} else {
- throw new UTFDataFormatException("bad byte");
+ throw badByteException(a);
}
}
}
@@ -260,18 +260,18 @@
} else if ((a & 0xe0) == 0xc0) {
int b = content[p++] & 0xff;
if ((b & 0xC0) != 0x80) {
- throw new UTFDataFormatException("bad second byte");
+ throw badSecondByteException(a, b);
}
h = 31 * h + (char) (((a & 0x1F) << 6) | (b & 0x3F));
} else if ((a & 0xf0) == 0xe0) {
int b = content[p++] & 0xff;
int c = content[p++] & 0xff;
if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
- throw new UTFDataFormatException("bad second or third byte");
+ throw badSecondOrThirdByteException(a, b, c);
}
h = 31 * h + (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
} else {
- throw new UTFDataFormatException("bad byte");
+ throw badByteException(a);
}
}
@@ -279,6 +279,30 @@
return h;
}
+ private UTFDataFormatException badByteException(int a) {
+ return new UTFDataFormatException("bad byte: " + Integer.toHexString((char) (a & 0xff)) + ")");
+ }
+
+ private UTFDataFormatException badSecondByteException(int a, int b) {
+ return new UTFDataFormatException(
+ "bad second byte (first: "
+ + Integer.toHexString((char) (a & 0xff))
+ + ", second: "
+ + Integer.toHexString((char) (b & 0xff))
+ + ")");
+ }
+
+ private UTFDataFormatException badSecondOrThirdByteException(int a, int b, int c) {
+ return new UTFDataFormatException(
+ "bad second or third byte (first: "
+ + Integer.toHexString((char) (a & 0xff))
+ + ", second: "
+ + Integer.toHexString((char) (b & 0xff))
+ + ", third: "
+ + Integer.toHexString((char) (c & 0xff))
+ + ")");
+ }
+
// Inspired from /dex/src/main/java/com/android/dex/Mutf8.java
private static int countBytes(String string) {
// We need an extra byte for the terminating '0'.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
index 24efc9a..6c85ffa 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
@@ -39,7 +39,7 @@
public class ConstructorEntryPoint extends SyntheticSourceCode {
private final DexField classIdField;
- private final int extraNulls;
+ private final int numberOfUnusedArguments;
private final ProgramMethod method;
private final Int2ReferenceSortedMap<DexMethod> typeConstructors;
@@ -47,11 +47,11 @@
Int2ReferenceSortedMap<DexMethod> typeConstructors,
ProgramMethod method,
DexField classIdField,
- int extraNulls,
+ int numberOfUnusedArguments,
Position position) {
super(method, position);
this.classIdField = classIdField;
- this.extraNulls = extraNulls;
+ this.numberOfUnusedArguments = numberOfUnusedArguments;
this.method = method;
this.typeConstructors = typeConstructors;
}
@@ -67,7 +67,7 @@
builder.hasArgumentValues()
? (builder.getArgumentValues().size()
- BooleanUtils.intValue(typeConstructors.size() > 1)
- - extraNulls)
+ - numberOfUnusedArguments)
: 0;
int newNumberOfNonReceiverArguments = typeConstructor.getArity();
List<Value> arguments = new ArrayList<>(newNumberOfNonReceiverArguments + 1);
@@ -106,7 +106,7 @@
protected void prepareMultiConstructorInstructions() {
int typeConstructorCount = typeConstructors.size();
// The class id register is always the first synthetic argument.
- int classIdRegister = getParamRegister(method.getArity() - 1 - extraNulls);
+ int classIdRegister = getParamRegister(method.getArity() - 1 - numberOfUnusedArguments);
if (hasClassIdField()) {
addRegisterClassIdAssignment(classIdRegister);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
index 794ca05..e089509 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
@@ -7,8 +7,13 @@
import com.android.tools.r8.classmerging.ClassMergerSharedData;
import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
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.DexType;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.DexMethodSignatureBiMap;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -17,7 +22,7 @@
* have been remapped to new classes by the class merger. While doing so, all updated changes are
* tracked in {@link HorizontalClassMergerTreeFixer#lensBuilder}.
*/
-class HorizontalClassMergerTreeFixer
+public class HorizontalClassMergerTreeFixer
extends ClassMergerTreeFixer<
HorizontalClassMergerGraphLens.Builder,
HorizontalClassMergerGraphLens,
@@ -90,4 +95,26 @@
throws ExecutionException {
return super.run(executorService, timing);
}
+
+ @Override
+ protected void traverseProgramClassesDepthFirst(
+ DexProgramClass clazz,
+ Set<DexProgramClass> seen,
+ DexMethodSignatureBiMap<DexMethodSignature> state) {
+ assert seen.add(clazz);
+ if (mergedClasses.isMergeSource(clazz.getType())) {
+ assert !clazz.hasMethodsOrFields();
+ return;
+ }
+ DexMethodSignatureBiMap<DexMethodSignature> newState = fixupProgramClass(clazz, state);
+ for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(clazz)) {
+ traverseProgramClassesDepthFirst(subclass, seen, newState);
+ }
+ for (DexType sourceType : mergedClasses.getSourcesFor(clazz.getType())) {
+ DexProgramClass sourceClass = appView.definitionFor(sourceType).asProgramClass();
+ for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(sourceClass)) {
+ traverseProgramClassesDepthFirst(subclass, seen, newState);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
index d727424..c006a41 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
@@ -41,7 +41,7 @@
public class IncompleteMergedInstanceInitializerCode extends IncompleteHorizontalClassMergerCode {
private final DexField classIdField;
- private int extraNulls;
+ private int numberOfUnusedArguments;
private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre;
private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost;
@@ -51,13 +51,13 @@
IncompleteMergedInstanceInitializerCode(
DexField classIdField,
- int extraNulls,
+ int numberOfUnusedArguments,
Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre,
Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost,
DexMethod parentConstructor,
List<InstanceFieldInitializationInfo> parentConstructorArguments) {
this.classIdField = classIdField;
- this.extraNulls = extraNulls;
+ this.numberOfUnusedArguments = numberOfUnusedArguments;
this.instanceFieldAssignmentsPre = instanceFieldAssignmentsPre;
this.instanceFieldAssignmentsPost = instanceFieldAssignmentsPost;
this.parentConstructor = parentConstructor;
@@ -66,7 +66,7 @@
@Override
public void addExtraUnusedArguments(int numberOfUnusedArguments) {
- this.extraNulls += numberOfUnusedArguments;
+ this.numberOfUnusedArguments += numberOfUnusedArguments;
}
@Override
@@ -107,7 +107,7 @@
// Assign class id.
if (classIdField != null) {
- Value classIdValue = argumentValues.get(argumentValues.size() - 1 - extraNulls);
+ Value classIdValue = argumentValues.get(argumentValues.size() - 1 - numberOfUnusedArguments);
lirBuilder.addInstancePut(
lens.getNextFieldSignature(classIdField), receiverValue, classIdValue);
instructionIndex++;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index aa4bce4..f40e17e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -67,12 +67,10 @@
}
public IncompleteMergedInstanceInitializerCode createCode(
- HorizontalMergeGroup group,
- boolean hasClassId,
- int extraNulls) {
+ HorizontalMergeGroup group, boolean hasClassId, int numberOfUnusedArguments) {
return new IncompleteMergedInstanceInitializerCode(
hasClassId ? group.getClassIdField() : null,
- extraNulls,
+ numberOfUnusedArguments,
instanceFieldAssignmentsPre,
instanceFieldAssignmentsPost,
parentConstructor,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 559f14f..256971c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -251,17 +251,16 @@
Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, true);
}
- private Code getNewCode(
- boolean needsClassId,
- int extraNulls) {
+ private Code getNewCode(boolean needsClassId, int numberOfUnusedArguments) {
if (hasInstanceInitializerDescription()) {
- return instanceInitializerDescription.createCode(group, needsClassId, extraNulls);
+ return instanceInitializerDescription.createCode(
+ group, needsClassId, numberOfUnusedArguments);
}
assert useSyntheticMethod();
return new ConstructorEntryPointSynthesizedCode(
createClassIdToInstanceInitializerMap(),
group.hasClassIdField() ? group.getClassIdField() : null,
- extraNulls);
+ numberOfUnusedArguments);
}
private boolean isSingleton() {
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 b0cfa19..7d8a826 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
@@ -37,21 +37,21 @@
public class ConstructorEntryPointSynthesizedCode extends IncompleteHorizontalClassMergerCode {
private final DexField classIdField;
- private int extraNulls;
+ private int numberOfUnusedArguments;
private final Int2ReferenceSortedMap<DexMethod> typeConstructors;
public ConstructorEntryPointSynthesizedCode(
Int2ReferenceSortedMap<DexMethod> typeConstructors,
DexField classIdField,
- int extraNulls) {
+ int numberOfUnusedArguments) {
this.typeConstructors = typeConstructors;
this.classIdField = classIdField;
- this.extraNulls = extraNulls;
+ this.numberOfUnusedArguments = numberOfUnusedArguments;
}
@Override
public void addExtraUnusedArguments(int numberOfUnusedArguments) {
- this.extraNulls += numberOfUnusedArguments;
+ this.numberOfUnusedArguments += numberOfUnusedArguments;
}
private void registerReachableDefinitions(UseRegistry<?> registry) {
@@ -120,7 +120,8 @@
.setIsD8R8Synthesized(true)
.build();
SourceCode sourceCode =
- new ConstructorEntryPoint(typeConstructors, method, classIdField, extraNulls, position);
+ new ConstructorEntryPoint(
+ typeConstructors, method, classIdField, numberOfUnusedArguments, position);
return IRBuilder.create(method, appView, sourceCode).build(method, conversionOptions);
}
@@ -135,7 +136,7 @@
RewrittenPrototypeDescription protoChanges) {
SourceCode sourceCode =
new ConstructorEntryPoint(
- typeConstructors, method, classIdField, extraNulls, callerPosition);
+ typeConstructors, method, classIdField, numberOfUnusedArguments, callerPosition);
return IRBuilder.createForInlining(
method, appView, codeLens, sourceCode, valueNumberGenerator, protoChanges)
.build(context, MethodConversionOptions.nonConverting());
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
index b601485..e7627a3 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
@@ -3,13 +3,20 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.verticalclassmerging;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+
import com.android.tools.r8.classmerging.ClassMergerSharedData;
import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
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.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.DexMethodSignatureBiMap;
+import com.google.common.collect.Iterables;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -62,4 +69,33 @@
public void postprocess() {
lensBuilder.fixupContextualVirtualToDirectMethodMaps();
}
+
+ @Override
+ protected boolean isRoot(DexProgramClass clazz) {
+ if (!super.isRoot(clazz)) {
+ return false;
+ }
+ return !Iterables.any(
+ mergedClasses.getSourcesFor(clazz.getType()),
+ source -> isRoot(asProgramClassOrNull(appView.definitionFor(source))));
+ }
+
+ @Override
+ protected void traverseProgramClassesDepthFirst(
+ DexProgramClass clazz,
+ Set<DexProgramClass> seen,
+ DexMethodSignatureBiMap<DexMethodSignature> state) {
+ assert seen.add(clazz) : clazz.getTypeName();
+ if (mergedClasses.isMergeSource(clazz.getType())) {
+ assert !clazz.hasMethodsOrFields();
+ DexProgramClass target =
+ Iterables.getOnlyElement(immediateSubtypingInfo.getSubclasses(clazz));
+ traverseProgramClassesDepthFirst(target, seen, state);
+ } else {
+ DexMethodSignatureBiMap<DexMethodSignature> newState = fixupProgramClass(clazz, state);
+ for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(clazz)) {
+ traverseProgramClassesDepthFirst(subclass, seen, newState);
+ }
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/horizontalclassmerging/ReserveInterfaceMethodSignaturesInFixupTest.java b/src/test/java/com/android/tools/r8/horizontalclassmerging/ReserveInterfaceMethodSignaturesInFixupTest.java
new file mode 100644
index 0000000..7a29a69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/horizontalclassmerging/ReserveInterfaceMethodSignaturesInFixupTest.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2024, 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.horizontalclassmerging;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+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;
+
+/** Regression test for b/328899963. */
+@RunWith(Parameterized.class)
+public class ReserveInterfaceMethodSignaturesInFixupTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(B.class, C.class).assertNoOtherClassesMerged())
+ .addVerticallyMergedClassesInspector(
+ VerticallyMergedClassesInspector::assertNoClassesMerged)
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("BSub", "CSub");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ I b = System.currentTimeMillis() > 0 ? new BSub() : new CSub();
+ b.foo(new CSub());
+ I c = System.currentTimeMillis() > 0 ? new CSub() : new BSub();
+ c.foo(new CSub());
+ }
+ }
+
+ @NoVerticalClassMerging
+ interface I {
+
+ void foo(C c);
+ }
+
+ @NoVerticalClassMerging
+ abstract static class A implements I {}
+
+ @NoVerticalClassMerging
+ abstract static class B extends A {}
+
+ @NoVerticalClassMerging
+ abstract static class C extends A {}
+
+ @NoHorizontalClassMerging
+ static class BSub extends B {
+
+ @NeverInline
+ @NoParameterTypeStrengthening
+ @Override
+ public void foo(C c) {
+ System.out.println("BSub");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ static class CSub extends C {
+
+ @NeverInline
+ @NoParameterTypeStrengthening
+ @Override
+ public void foo(C c) {
+ System.out.println(c);
+ }
+
+ @Override
+ public String toString() {
+ return "CSub";
+ }
+ }
+}
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 a152459..e60c8b6 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
@@ -94,6 +94,15 @@
private void inspect(
HorizontallyMergedClassesInspector inspector, KotlinLambdasInInput lambdasInInput) {
+ if (hasKotlinCGeneratedLambdaClasses()
+ && kotlinParameters.getCompilerVersion().isLessThan(KOTLINC_1_5_0)) {
+ // Don't check exactly how J-style Kotlin lambdas are merged for kotlinc before 1.5.0.
+ assertEquals(
+ parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods() ? 2 : 10,
+ inspector.getMergeGroups().size());
+ return;
+ }
+
if (!allowAccessModification && hasKotlinCGeneratedLambdaClasses()) {
// Only a subset of all J-style Kotlin lambdas are merged without -allowaccessmodification.
Set<ClassReference> unmergedLambdas =
@@ -214,7 +223,13 @@
lambdasInOutput.add(classReference);
}
}
- assertEquals(0, lambdasInOutput.size());
+ assertEquals(
+ kotlinParameters.getCompilerVersion().isGreaterThanOrEqualTo(KOTLINC_1_5_0)
+ ? 0
+ : (parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods()
+ ? 2
+ : 8),
+ lambdasInOutput.size());
}
private boolean hasKotlinCGeneratedLambdaClasses() {
diff --git a/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java b/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java
index 9290be4..1464cff 100644
--- a/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/MappingFileAfterRepackagingTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import java.util.List;
@@ -38,8 +39,11 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertIsCompleteMergeGroup(A.class, B.class))
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
.applyIf(repackage, testBuilder -> testBuilder.addKeepRules("-repackageclasses"))
.enableInliningAnnotations()
.setMinApi(parameters)
@@ -61,10 +65,16 @@
StringUtils.splitLines(runResult.proguardMap()).stream()
.filter(line -> line.contains("java.lang.String toString()"))
.count();
- assertEquals(repackage ? 1 : 3, unqualifiedMatches);
+ assertEquals(
+ (repackage ? 1 : 3) + BooleanUtils.intValue(isPc2pc()), unqualifiedMatches);
});
}
+ private boolean isPc2pc() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O);
+ }
+
static class Main {
public static void main(String[] args) {