Reland "Only use source supertype for generic signature if not interface"
This reverts commit c485a2fb054c63906d1e370b694af1128805d6d5.
Bug: 191871201
Bug: 191349005
Bug: 191327911
Bug: 192049671
Bug: 191871201
Change-Id: Ie41e50efbe93e0a60ca1ded4d6ce293eecb704ee
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 094a4c7..c2ee174 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -660,6 +660,14 @@
new GenericSignatureRewriter(
appView, NamingLens.getIdentityLens(), genericContextBuilder)
.run(appView.appInfo().classes(), executorService);
+ assert appView.checkForTesting(
+ () ->
+ GenericSignatureCorrectnessHelper.createForVerification(
+ appView,
+ GenericSignatureContextBuilder.create(appView.appInfo().classes()))
+ .run(appView.appInfo().classes())
+ .isValid())
+ : "Could not validate generic signatures";
// Synthesize fields for triggering class initializers.
new ClassInitFieldSynthesizer(appViewWithLiveness).run(executorService);
@@ -739,6 +747,9 @@
// need to build IR.
appView.dexItemFactory().clearTypeElementsCache();
+ GenericSignatureContextBuilder genericContextBuilderBeforeFinalMerging =
+ GenericSignatureContextBuilder.create(appView.appInfo().classes());
+
// Run horizontal class merging. This runs even if shrinking is disabled to ensure synthetics
// are always merged.
HorizontalClassMerger.createForFinalClassMerging(appView)
@@ -813,6 +824,17 @@
options.syntheticProguardRulesConsumer.accept(synthesizedProguardRules);
}
+ NamingLens prefixRewritingNamingLens =
+ PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView, namingLens);
+
+ timing.begin("MinifyKotlinMetadata");
+ new KotlinMetadataRewriter(appView, prefixRewritingNamingLens).runForR8(executorService);
+ timing.end();
+
+ new GenericSignatureRewriter(
+ appView, prefixRewritingNamingLens, genericContextBuilderBeforeFinalMerging)
+ .run(appView.appInfo().classes(), executorService);
+
assert appView.checkForTesting(
() ->
!options.isShrinking()
@@ -823,16 +845,6 @@
.isValid())
: "Could not validate generic signatures";
- NamingLens prefixRewritingNamingLens =
- PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView, namingLens);
-
- timing.begin("MinifyKotlinMetadata");
- new KotlinMetadataRewriter(appView, prefixRewritingNamingLens).runForR8(executorService);
- timing.end();
-
- new GenericSignatureRewriter(appView, prefixRewritingNamingLens)
- .run(appView.appInfo().classes(), executorService);
-
new DesugaredLibraryKeepRuleGenerator(appView, prefixRewritingNamingLens)
.runIfNecessary(timing);
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index 419125d..920fbc1 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -15,6 +15,7 @@
import com.google.common.collect.ImmutableList;
import java.lang.reflect.GenericSignatureFormatError;
import java.nio.CharBuffer;
+import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
@@ -293,6 +294,53 @@
public ClassSignature toObjectBoundWithSameFormals(ClassTypeSignature objectBound) {
return new ClassSignature(formalTypeParameters, objectBound, getEmptySuperInterfaces());
}
+
+ public List<FieldTypeSignature> getGenericArgumentsToSuperType(DexType type) {
+ assert hasSignature();
+ if (superClassSignature.type == type) {
+ return superClassSignature.typeArguments;
+ }
+ for (ClassTypeSignature superInterfaceSignature : superInterfaceSignatures) {
+ if (superInterfaceSignature.type == type) {
+ return superInterfaceSignature.typeArguments;
+ }
+ }
+ return null;
+ }
+
+ public static ClassSignatureBuilder builder() {
+ return new ClassSignatureBuilder();
+ }
+
+ public static class ClassSignatureBuilder {
+
+ private List<FormalTypeParameter> formalTypeParameters = new ArrayList<>();
+ private ClassTypeSignature superClassSignature = null;
+ private List<ClassTypeSignature> superInterfaceSignatures = new ArrayList<>();
+
+ private ClassSignatureBuilder() {}
+
+ public ClassSignatureBuilder addFormalTypeParameters(List<FormalTypeParameter> formals) {
+ formalTypeParameters.addAll(formals);
+ return this;
+ }
+
+ public ClassSignatureBuilder setSuperClassSignature(ClassTypeSignature superClassSignature) {
+ this.superClassSignature = superClassSignature;
+ return this;
+ }
+
+ public ClassSignatureBuilder addInterface(ClassTypeSignature iface) {
+ superInterfaceSignatures.add(iface);
+ return this;
+ }
+
+ public ClassSignature build() {
+ ClassSignature classSignature =
+ new ClassSignature(formalTypeParameters, superClassSignature, superInterfaceSignatures);
+ return classSignature;
+ }
+ }
}
private static class InvalidClassSignature extends ClassSignature {
@@ -585,7 +633,7 @@
return null;
}
List<FieldTypeSignature> rewrittenArguments =
- visitor.visitTypeArguments(visitedType, typeArguments);
+ visitor.visitTypeArguments(type, visitedType, typeArguments);
ClassTypeSignature rewrittenOuter = null;
if (enclosingTypeSignature != null) {
rewrittenOuter = visitor.visitEnclosing(enclosingTypeSignature, this);
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
index 2a0636b..1b1eabf 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureContextBuilder.java
@@ -6,8 +6,10 @@
import static com.android.tools.r8.graph.GenericSignatureContextBuilder.TypeParameterContext.empty;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.utils.WorkList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -25,26 +27,26 @@
private static class TypeParameterSubstitutions {
- private final Map<String, DexType> parametersWithBounds;
+ private final Map<String, FieldTypeSignature> parametersWithBounds;
- private TypeParameterSubstitutions(Map<String, DexType> parametersWithBounds) {
+ private TypeParameterSubstitutions(Map<String, FieldTypeSignature> parametersWithBounds) {
this.parametersWithBounds = parametersWithBounds;
}
private static TypeParameterSubstitutions create(List<FormalTypeParameter> formals) {
- Map<String, DexType> map = new IdentityHashMap<>();
+ Map<String, FieldTypeSignature> map = new HashMap<>();
formals.forEach(
formal -> {
- DexType bound = null;
if (formal.getClassBound() != null
&& formal.getClassBound().hasSignature()
&& formal.getClassBound().isClassTypeSignature()) {
- bound = formal.getClassBound().asClassTypeSignature().type;
+ map.put(formal.getName(), formal.getClassBound());
} else if (!formal.getInterfaceBounds().isEmpty()
&& formal.getInterfaceBounds().get(0).isClassTypeSignature()) {
- bound = formal.getInterfaceBounds().get(0).asClassTypeSignature().type;
+ map.put(formal.getName(), formal.getInterfaceBounds().get(0));
+ } else {
+ map.put(formal.getName(), null);
}
- map.put(formal.getName(), bound);
});
return new TypeParameterSubstitutions(map);
}
@@ -55,11 +57,11 @@
private static final TypeParameterContext EMPTY =
new TypeParameterContext(Collections.emptyMap(), Collections.emptySet());
- private final Map<String, DexType> prunedParametersWithBounds;
+ private final Map<String, FieldTypeSignature> prunedParametersWithBounds;
private final Set<String> liveParameters;
private TypeParameterContext(
- Map<String, DexType> prunedParametersWithBounds, Set<String> liveParameters) {
+ Map<String, FieldTypeSignature> prunedParametersWithBounds, Set<String> liveParameters) {
this.prunedParametersWithBounds = prunedParametersWithBounds;
this.liveParameters = liveParameters;
}
@@ -81,7 +83,7 @@
return liveParameters.contains(parameterName);
}
- public DexType getPrunedSubstitution(String parameterName) {
+ public FieldTypeSignature getPrunedSubstitution(String parameterName) {
assert !isLiveParameter(parameterName);
return prunedParametersWithBounds.get(parameterName);
}
@@ -93,7 +95,7 @@
HashSet<String> newLiveParameters = new HashSet<>();
newLiveParameters.addAll(liveParameters);
newLiveParameters.addAll(typeParameters);
- HashMap<String, DexType> newPruned = new HashMap<>();
+ HashMap<String, FieldTypeSignature> newPruned = new HashMap<>();
prunedParametersWithBounds.forEach(
(name, type) -> {
if (!typeParameters.contains(name)) {
@@ -103,11 +105,12 @@
return new TypeParameterContext(newPruned, newLiveParameters);
}
- public TypeParameterContext addPrunedSubstitutions(Map<String, DexType> substitutions) {
+ public TypeParameterContext addPrunedSubstitutions(
+ Map<String, FieldTypeSignature> substitutions) {
if (substitutions.isEmpty()) {
return this;
}
- HashMap<String, DexType> newPruned = new HashMap<>();
+ HashMap<String, FieldTypeSignature> newPruned = new HashMap<>();
newPruned.putAll(prunedParametersWithBounds);
newPruned.putAll(substitutions);
HashSet<String> newLiveParameters = new HashSet<>();
@@ -121,38 +124,6 @@
}
}
- public static class AlwaysLiveTypeParameterContext extends TypeParameterContext {
-
- private AlwaysLiveTypeParameterContext() {
- super(Collections.emptyMap(), Collections.emptySet());
- }
-
- public static AlwaysLiveTypeParameterContext create() {
- return new AlwaysLiveTypeParameterContext();
- }
-
- @Override
- public boolean isLiveParameter(String parameterName) {
- return true;
- }
-
- @Override
- public DexType getPrunedSubstitution(String parameterName) {
- assert false;
- return null;
- }
-
- @Override
- public TypeParameterContext addLiveParameters(Collection<String> typeParameters) {
- return this;
- }
-
- @Override
- public TypeParameterContext addPrunedSubstitutions(Map<String, DexType> substitutions) {
- return this;
- }
- }
-
private GenericSignatureContextBuilder(
Map<DexReference, TypeParameterSubstitutions> formalsInfo,
Map<DexReference, DexReference> enclosingInfo) {
@@ -160,7 +131,7 @@
this.enclosingInfo = enclosingInfo;
}
- public static GenericSignatureContextBuilder create(List<DexProgramClass> programClasses) {
+ public static GenericSignatureContextBuilder create(Collection<DexProgramClass> programClasses) {
Map<DexReference, TypeParameterSubstitutions> formalsInfo = new IdentityHashMap<>();
Map<DexReference, DexReference> enclosingInfo = new IdentityHashMap<>();
programClasses.forEach(
@@ -201,6 +172,24 @@
return new GenericSignatureContextBuilder(formalsInfo, enclosingInfo);
}
+ public static GenericSignatureContextBuilder createForSingleClass(
+ AppView<?> appView, DexProgramClass clazz) {
+ WorkList<DexProgramClass> workList = WorkList.newIdentityWorkList(clazz);
+ while (workList.hasNext()) {
+ DexProgramClass current = workList.next();
+ DexClass outer = null;
+ if (current.getEnclosingMethodAttribute() != null) {
+ outer = appView.definitionFor(current.getEnclosingMethodAttribute().getEnclosingType());
+ } else if (current.getInnerClassAttributeForThisClass() != null) {
+ outer = appView.definitionFor(current.getInnerClassAttributeForThisClass().getOuter());
+ }
+ if (outer != null && outer.isProgramClass()) {
+ workList.addIfNotSeen(outer.asProgramClass());
+ }
+ }
+ return create(workList.getSeenSet());
+ }
+
public TypeParameterContext computeTypeParameterContext(
AppView<?> appView, DexReference reference, Predicate<DexType> wasPruned) {
assert !wasPruned.test(reference.getContextType()) : "Building context for pruned type";
@@ -218,27 +207,42 @@
DexType contextType = reference.getContextType();
// TODO(b/187035453): We should visit generic signatures in the enqueuer.
DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(contextType);
- boolean prunedHere = seenPruned || clazz == null;
- if (appView.hasLiveness()
- && appView.withLiveness().appInfo().getMissingClasses().contains(contextType)) {
- prunedHere = seenPruned;
- }
+ boolean prunedHere = seenPruned;
+ // If the class cannot be looked up and it is not missing it was pruned here.
+ prunedHere |=
+ clazz == null
+ && appView.hasLiveness()
+ && !appView.withLiveness().appInfo().getMissingClasses().contains(contextType);
// Lookup the formals in the enclosing context.
+ TypeParameterSubstitutions formalsInfo = this.formalsInfo.get(contextType);
+ // If formals has been pruned then the context is also pruned here.
+ prunedHere |=
+ clazz != null
+ && formalsInfo != null
+ && !formalsInfo.parametersWithBounds.isEmpty()
+ && clazz.getClassSignature().getFormalTypeParameters().isEmpty();
+ DexReference enclosingReference = enclosingInfo.get(contextType);
TypeParameterContext typeParameterContext =
computeTypeParameterContext(
appView,
- enclosingInfo.get(contextType),
+ enclosingReference,
wasPruned,
prunedHere
- || hasPrunedRelationship(
- appView, enclosingInfo.get(contextType), contextType, wasPruned))
+ || hasPrunedRelationship(appView, enclosingReference, contextType, wasPruned))
// Add formals for the context
- .combine(formalsInfo.get(contextType), prunedHere);
+ .combine(formalsInfo, prunedHere);
if (!reference.isDexMethod()) {
return typeParameterContext;
}
- prunedHere = prunedHere || clazz == null || clazz.lookupMethod(reference.asDexMethod()) == null;
- return typeParameterContext.combine(formalsInfo.get(reference), prunedHere);
+ TypeParameterSubstitutions methodFormals = this.formalsInfo.get(reference);
+ if (clazz != null && !prunedHere) {
+ DexEncodedMethod method = clazz.lookupMethod(reference.asDexMethod());
+ prunedHere =
+ method == null
+ || (!methodFormals.parametersWithBounds.isEmpty()
+ && method.getGenericSignature().getFormalTypeParameters().isEmpty());
+ }
+ return typeParameterContext.combine(methodFormals, prunedHere);
}
public boolean hasPrunedRelationship(
@@ -257,15 +261,9 @@
}
// TODO(b/187035453): We should visit generic signatures in the enqueuer.
DexClass enclosingClass =
- appView
- .appInfo()
- .definitionForWithoutExistenceAssert(
- appView.graphLens().lookupClassType(enclosingReference.getContextType()));
+ appView.appInfo().definitionForWithoutExistenceAssert(enclosingReference.getContextType());
DexClass enclosedClass =
- appView
- .appInfo()
- .definitionForWithoutExistenceAssert(
- appView.graphLens().lookupClassType(enclosedClassType));
+ appView.appInfo().definitionForWithoutExistenceAssert(enclosedClassType);
if (enclosingClass == null || enclosedClass == null) {
return true;
}
@@ -279,12 +277,12 @@
}
}
- public boolean hasGenericTypeVariables(
+ public static boolean hasGenericTypeVariables(
AppView<?> appView, DexType type, Predicate<DexType> wasPruned) {
if (wasPruned.test(type)) {
return false;
}
- DexClass clazz = appView.definitionFor(appView.graphLens().lookupClassType(type));
+ DexClass clazz = appView.definitionFor(type);
if (clazz == null || clazz.isNotProgramClass() || clazz.getClassSignature().isInvalid()) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
index 03f4259..1946512 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -234,8 +234,7 @@
if (context.superType == appView.dexItemFactory().objectType
&& classSignature.superClassSignature().hasNoSignature()) {
// We represent no signature as object.
- } else if (context.superType
- != appView.graphLens().lookupClassType(classSignature.superClassSignature().type())) {
+ } else if (context.superType != classSignature.superClassSignature().type()) {
assert mode.doNotVerify() : "Super type inconsistency in generic signature";
return INVALID_SUPER_TYPE;
}
@@ -393,10 +392,7 @@
}
}
// TODO(b/187035453): We should visit generic signatures in the enqueuer.
- DexClass clazz =
- appView
- .appInfo()
- .definitionForWithoutExistenceAssert(appView.graphLens().lookupClassType(type));
+ DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(type);
if (clazz == null) {
// We do not know if the application of arguments works or not.
return VALID;
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
index 56a6496..12ca041 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
@@ -26,6 +26,8 @@
private final BiPredicate<DexType, DexType> enclosingPruned;
private final Predicate<DexType> hasGenericTypeParameters;
private final AppView<?> appView;
+ private final ClassTypeSignature objectArgument;
+ private boolean makeAllTypeArgumentsObject = false;
private GenericSignaturePartialTypeArgumentApplier(
AppView<?> appView,
@@ -36,6 +38,9 @@
this.typeParameterContext = typeParameterContext;
this.enclosingPruned = enclosingPruned;
this.hasGenericTypeParameters = hasGenericTypeParameters;
+ objectArgument =
+ new ClassTypeSignature(appView.dexItemFactory().objectType)
+ .asArgument(WildcardIndicator.NONE);
}
public static GenericSignaturePartialTypeArgumentApplier build(
@@ -78,6 +83,10 @@
@Override
public DexType visitType(DexType type) {
+ // It is important that the type is not looked up in the applier. The type-parameter context is
+ // a mapping from fully applied -> old references, which may seem a bit odd, but that is simply
+ // because we do not rewrite the signatures in lock step with rewriting the app.
+ // The actual lookup will be performed in the GenericSignatureTypeRewriter.
return type;
}
@@ -117,8 +126,12 @@
@Override
public List<FieldTypeSignature> visitTypeArguments(
- DexType type, List<FieldTypeSignature> typeArguments) {
- if (typeArguments.isEmpty() || !hasGenericTypeParameters.test(type)) {
+ DexType originalType, DexType lookedUpType, List<FieldTypeSignature> typeArguments) {
+ assert originalType == lookedUpType;
+ if (typeArguments.isEmpty()) {
+ return typeArguments;
+ }
+ if (!hasGenericTypeParameters.test(appView.graphLens().lookupType(originalType))) {
return getEmptyTypeArguments();
}
return ListUtils.mapOrElse(typeArguments, this::visitFieldTypeSignature);
@@ -145,7 +158,9 @@
@Override
public ClassTypeSignature visitEnclosing(
ClassTypeSignature enclosingSignature, ClassTypeSignature enclosedSignature) {
- if (enclosingPruned.test(enclosingSignature.type(), enclosedSignature.type())) {
+ DexType enclosingType = appView.graphLens().lookupType(enclosingSignature.type());
+ DexType enclosedType = appView.graphLens().lookupType(enclosedSignature.type());
+ if (enclosingPruned.test(enclosingType, enclosedType)) {
return null;
} else {
return enclosingSignature.visit(this);
@@ -208,15 +223,26 @@
return fieldSignature.asArrayTypeSignature().visit(this);
} else {
assert fieldSignature.isTypeVariableSignature();
- String typeVariableName = fieldSignature.asTypeVariableSignature().typeVariable();
- if (!typeParameterContext.isLiveParameter(typeVariableName)) {
- DexType substitution = typeParameterContext.getPrunedSubstitution(typeVariableName);
- if (substitution == null) {
- substitution = appView.dexItemFactory().objectType;
- }
- return new ClassTypeSignature(substitution).asArgument(WildcardIndicator.NONE);
+ // TODO(b/b/191871201): If we track where type-variables are introduced, we can move this
+ // past typeParameterContext.isLiveParameter(typeVariableName) and get more precision.
+ if (makeAllTypeArgumentsObject) {
+ return objectArgument;
}
- return fieldSignature;
+ String typeVariableName = fieldSignature.asTypeVariableSignature().typeVariable();
+ if (typeParameterContext.isLiveParameter(typeVariableName)) {
+ return fieldSignature;
+ }
+ FieldTypeSignature substitution =
+ typeParameterContext.getPrunedSubstitution(typeVariableName);
+ if (substitution == null) {
+ return objectArgument;
+ }
+ makeAllTypeArgumentsObject = true;
+ substitution = visitFieldTypeSignature(substitution);
+ makeAllTypeArgumentsObject = false;
+ return substitution.isArgument()
+ ? substitution
+ : substitution.asArgument(WildcardIndicator.NONE);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
index b111c57..9d54705 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -146,7 +146,7 @@
@Override
public List<FieldTypeSignature> visitTypeArguments(
- DexType type, List<FieldTypeSignature> typeArguments) {
+ DexType originalType, DexType lookedUpType, List<FieldTypeSignature> typeArguments) {
if (typeArguments.isEmpty()) {
return typeArguments;
}
@@ -207,7 +207,7 @@
}
sb.append(".").append(innerClassName);
}
- visitTypeArguments(null, classTypeSignature.typeArguments);
+ visitTypeArguments(null, null, classTypeSignature.typeArguments);
if (!printingOuter) {
sb.append(";");
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index 332aa8d..7f224bb 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -29,26 +29,31 @@
private final DexProgramClass context;
private final ClassTypeSignature objectTypeSignature;
+ private final Predicate<DexType> hasGenericTypeVariables;
- public GenericSignatureTypeRewriter(AppView<?> appView, DexProgramClass context) {
+ public GenericSignatureTypeRewriter(
+ AppView<?> appView, DexProgramClass context, Predicate<DexType> hasGenericTypeVariables) {
this(
appView.dexItemFactory(),
appView.appInfo().hasLiveness()
? appView.appInfo().withLiveness()::wasPruned
: alwaysFalse(),
appView.graphLens()::lookupType,
- context);
+ context,
+ hasGenericTypeVariables);
}
public GenericSignatureTypeRewriter(
DexItemFactory factory,
Predicate<DexType> wasPruned,
Function<DexType, DexType> lookupType,
- DexProgramClass context) {
+ DexProgramClass context,
+ Predicate<DexType> hasGenericTypeVariables) {
this.factory = factory;
this.wasPruned = wasPruned;
this.lookupType = lookupType;
this.context = context;
+ this.hasGenericTypeVariables = hasGenericTypeVariables;
objectTypeSignature = new ClassTypeSignature(factory.objectType, getEmptyTypeArguments());
}
@@ -138,10 +143,13 @@
@Override
public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
+ if (context.superType == factory.objectType) {
+ return classTypeSignature.type == factory.objectType
+ ? classTypeSignature
+ : objectTypeSignature;
+ }
ClassTypeSignature rewritten = classTypeSignature.visit(this);
- return rewritten == null || rewritten.type() == context.type
- ? objectTypeSignature
- : rewritten;
+ return rewritten == null ? objectTypeSignature : rewritten;
}
@Override
@@ -239,10 +247,17 @@
@Override
public List<FieldTypeSignature> visitTypeArguments(
- DexType type, List<FieldTypeSignature> typeArguments) {
+ DexType originalType, DexType lookedUpType, List<FieldTypeSignature> typeArguments) {
+ assert lookedUpType != null;
if (typeArguments.isEmpty()) {
return typeArguments;
}
+ // If the original type has been pruned it must be because the old type has been merged into
+ // the looked up type. We can therefore not guarantee the type arguments to be consistent and
+ // have to remove them.
+ if (wasPruned.test(originalType) || !hasGenericTypeVariables.test(lookedUpType)) {
+ return getEmptyTypeArguments();
+ }
return ListUtils.mapOrElse(
typeArguments,
fieldTypeSignature -> {
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
index 59cd879..a619129 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
@@ -144,7 +144,7 @@
@Override
public List<FieldTypeSignature> visitTypeArguments(
- DexType type, List<FieldTypeSignature> typeArguments) {
+ DexType originalType, DexType lookedUpType, List<FieldTypeSignature> typeArguments) {
typeArguments.forEach(this::visitFieldTypeSignature);
return typeArguments;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
index b321ad1..37b3cbe 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
@@ -84,7 +84,7 @@
}
default List<FieldTypeSignature> visitTypeArguments(
- DexType type, List<FieldTypeSignature> typeArguments) {
+ DexType originalType, DexType lookedUpType, List<FieldTypeSignature> typeArguments) {
throw new Unreachable("Implement if visited");
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index db0178c..7483522 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.code.ClassInitializerMerger;
@@ -325,6 +326,8 @@
mergeInterfaces();
mergeFields();
mergeMethods(syntheticArgumentClass, syntheticInitializerConverterBuilder);
+ group.getTarget().clearClassSignature();
+ group.getTarget().forEachProgramMember(ProgramMember::clearGenericSignature);
}
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index 16b6508..b719c89 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.GenericSignaturePartialTypeArgumentApplier;
import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -50,13 +51,15 @@
// ClassNameMinifier.
Predicate<DexType> wasPruned =
appView.hasLiveness() ? appView.withLiveness().appInfo()::wasPruned : alwaysFalse();
+ Predicate<DexType> hasGenericTypeVariables =
+ type -> GenericSignatureContextBuilder.hasGenericTypeVariables(appView, type, wasPruned);
BiPredicate<DexType, DexType> hasPrunedRelationship =
(enclosing, enclosed) ->
contextBuilder.hasPrunedRelationship(appView, enclosing, enclosed, wasPruned);
- Predicate<DexType> hasGenericTypeVariables =
- type -> contextBuilder.hasGenericTypeVariables(appView, type, wasPruned);
ThreadUtils.processItems(
- classes,
+ // Final merging of classes can introduce pruned types that still exists in classes, we
+ // therefore prune them from work here.
+ IterableUtils.filter(classes, clazz -> !wasPruned.test(clazz.getType())),
clazz -> {
GenericSignaturePartialTypeArgumentApplier classArgumentApplier =
contextBuilder != null
@@ -68,7 +71,7 @@
hasGenericTypeVariables)
: null;
GenericSignatureTypeRewriter genericSignatureTypeRewriter =
- new GenericSignatureTypeRewriter(appView, clazz);
+ new GenericSignatureTypeRewriter(appView, clazz, hasGenericTypeVariables);
clazz.setClassSignature(
genericSignatureTypeRewriter.rewrite(
classArgumentApplier != null
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 77b119d..061eb02 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -29,7 +29,16 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature.ClassSignatureBuilder;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignatureContextBuilder;
+import com.android.tools.r8.graph.GenericSignatureContextBuilder.TypeParameterContext;
+import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
+import com.android.tools.r8.graph.GenericSignaturePartialTypeArgumentApplier;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
@@ -56,6 +65,7 @@
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.FieldSignatureEquivalence;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.OptionalBool;
@@ -1079,6 +1089,9 @@
return false;
}
+ // Rewrite generic signatures before we merge fields.
+ rewriteGenericSignatures(target, source, directMethods.values(), virtualMethods.values());
+
// Step 2: Merge fields
Set<DexString> existingFieldNames = new HashSet<>();
for (DexEncodedField field : target.fields()) {
@@ -1139,9 +1152,148 @@
// Step 4: Record merging.
mergedClasses.put(source.type, target.type);
assert !abortMerge;
+ assert GenericSignatureCorrectnessHelper.createForVerification(
+ appView, GenericSignatureContextBuilder.createForSingleClass(appView, target))
+ .evaluateSignaturesForClass(target)
+ .isValid();
return true;
}
+ /**
+ * The rewriting of generic signatures is pretty simple, but require some bookkeeping. We take
+ * the arguments to the base type:
+ *
+ * <pre>
+ * class Sub<X> extends Base<X, String>
+ * </pre>
+ *
+ * for
+ *
+ * <pre>
+ * class Base<T,R> extends OtherBase<T> implements I<R> {
+ * T t() { ... };
+ * }
+ * </pre>
+ *
+ * and substitute T -> X and R -> String
+ */
+ private void rewriteGenericSignatures(
+ DexProgramClass target,
+ DexProgramClass source,
+ Collection<DexEncodedMethod> directMethods,
+ Collection<DexEncodedMethod> virtualMethods) {
+ ClassSignature targetSignature = target.getClassSignature();
+ if (targetSignature.hasNoSignature()) {
+ // Null out all source signatures that is moved, but do not clear out the class since this
+ // could be referred to by other generic signatures.
+ // TODO(b/147504070): If merging classes with enclosing/innerclasses, this needs to be
+ // reconsidered.
+ directMethods.forEach(DexEncodedMethod::clearGenericSignature);
+ virtualMethods.forEach(DexEncodedMethod::clearGenericSignature);
+ source.fields().forEach(DexEncodedMember::clearGenericSignature);
+ return;
+ }
+ GenericSignaturePartialTypeArgumentApplier classApplier =
+ getGenericSignatureArgumentApplier(target, source);
+ if (classApplier == null) {
+ target.clearClassSignature();
+ target.members().forEach(DexEncodedMember::clearGenericSignature);
+ return;
+ }
+ // We could generate a substitution map.
+ ClassSignature rewrittenSource = classApplier.visitClassSignature(source.getClassSignature());
+ // The variables in the class signature is now rewritten to use the targets argument.
+ ClassSignatureBuilder builder = ClassSignature.builder();
+ builder.addFormalTypeParameters(targetSignature.getFormalTypeParameters());
+ if (!source.isInterface()) {
+ if (rewrittenSource.hasSignature()) {
+ builder.setSuperClassSignature(rewrittenSource.superClassSignature());
+ } else {
+ builder.setSuperClassSignature(new ClassTypeSignature(source.superType));
+ }
+ } else {
+ builder.setSuperClassSignature(targetSignature.superClassSignature());
+ }
+ // Compute the seen set for interfaces to add. This is similar to the merging of interfaces
+ // but allow us to maintain the type arguments.
+ Set<DexType> seenInterfaces = new HashSet<>();
+ if (source.isInterface()) {
+ seenInterfaces.add(source.type);
+ }
+ for (ClassTypeSignature iFace : targetSignature.superInterfaceSignatures()) {
+ if (seenInterfaces.add(iFace.type())) {
+ builder.addInterface(iFace);
+ }
+ }
+ if (rewrittenSource.hasSignature()) {
+ for (ClassTypeSignature iFace : rewrittenSource.superInterfaceSignatures()) {
+ if (!seenInterfaces.contains(iFace.type())) {
+ builder.addInterface(iFace);
+ }
+ }
+ } else {
+ // Synthesize raw uses of interfaces to align with the actual class
+ for (DexType iFace : source.interfaces) {
+ if (!seenInterfaces.contains(iFace)) {
+ builder.addInterface(new ClassTypeSignature(iFace));
+ }
+ }
+ }
+ target.setClassSignature(builder.build());
+
+ // Go through all type-variable references for members and update them.
+ CollectionUtils.forEach(
+ method -> {
+ MethodTypeSignature methodSignature = method.getGenericSignature();
+ if (methodSignature.hasNoSignature()) {
+ return;
+ }
+ method.setGenericSignature(
+ classApplier
+ .buildForMethod(methodSignature.getFormalTypeParameters())
+ .visitMethodSignature(methodSignature));
+ },
+ directMethods,
+ virtualMethods);
+
+ source.forEachField(
+ field -> {
+ if (field.getGenericSignature().hasNoSignature()) {
+ return;
+ }
+ field.setGenericSignature(
+ classApplier.visitFieldTypeSignature(field.getGenericSignature()));
+ });
+ }
+
+ private GenericSignaturePartialTypeArgumentApplier getGenericSignatureArgumentApplier(
+ DexProgramClass target, DexProgramClass source) {
+ assert target.getClassSignature().hasSignature();
+ // We can assert proper structure below because the generic signature validator has run
+ // before and pruned invalid signatures.
+ List<FieldTypeSignature> genericArgumentsToSuperType =
+ target.getClassSignature().getGenericArgumentsToSuperType(source.type);
+ if (genericArgumentsToSuperType == null) {
+ assert false : "Type should be present in generic signature";
+ return null;
+ }
+ List<FormalTypeParameter> formals = source.getClassSignature().getFormalTypeParameters();
+ if (genericArgumentsToSuperType.size() != formals.size()) {
+ assert false : "Invalid argument count to formals";
+ return null;
+ }
+ Map<String, FieldTypeSignature> substitutionMap = new HashMap<>();
+ for (int i = 0; i < formals.size(); i++) {
+ // It is OK to override a generic type variable so we just use put.
+ substitutionMap.put(formals.get(i).getName(), genericArgumentsToSuperType.get(i));
+ }
+ return GenericSignaturePartialTypeArgumentApplier.build(
+ appView,
+ TypeParameterContext.empty().addPrunedSubstitutions(substitutionMap),
+ (type1, type2) -> true,
+ type -> true);
+ }
+
private boolean restoreDebuggingState(Stream<DexEncodedMethod> toBeDiscarded) {
toBeDiscarded.forEach(
method -> {
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 6584f77..91b0638 100644
--- a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -7,12 +7,21 @@
import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.Set;
+import java.util.function.Consumer;
public class CollectionUtils {
+
public static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
ImmutableSet.Builder<T> builder = ImmutableSet.builder();
builder.addAll(first);
builder.addAll(second);
return builder.build();
}
+
+ @SafeVarargs
+ public static <T> void forEach(Consumer<T> consumer, Collection<T>... collections) {
+ for (Collection<T> collection : collections) {
+ collection.forEach(consumer);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 5781f91..91bfbe6 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -48,6 +48,17 @@
return result != null ? result : defaultValue;
}
+ public static <T> List<T> filter(List<T> list, Predicate<? super T> predicate) {
+ ArrayList<T> filtered = new ArrayList<>(list.size());
+ list.forEach(
+ t -> {
+ if (predicate.test(t)) {
+ filtered.add(t);
+ }
+ });
+ return filtered;
+ }
+
public static <T> T first(List<T> list) {
return list.get(0);
}
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index ba04d86..2460a3c 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -47,21 +47,21 @@
",", map.entrySet(), entry -> entry.getKey() + ":" + entry.getValue(), BraceType.TUBORG);
}
- public static <K, V> Map<K, V> transform(
- Map<K, V> map,
- IntFunction<Map<K, V>> factory,
- Function<K, K> keyMapping,
- Function<V, V> valueMapping,
- BiFunction<V, V, V> valueMerger) {
- Map<K, V> result = factory.apply(map.size());
+ public static <K1, V1, K2, V2> Map<K2, V2> transform(
+ Map<K1, V1> map,
+ IntFunction<Map<K2, V2>> factory,
+ Function<K1, K2> keyMapping,
+ Function<V1, V2> valueMapping,
+ BiFunction<V2, V2, V2> valueMerger) {
+ Map<K2, V2> result = factory.apply(map.size());
map.forEach(
(key, value) -> {
- K newKey = keyMapping.apply(key);
+ K2 newKey = keyMapping.apply(key);
if (newKey == null) {
return;
}
- V newValue = valueMapping.apply(value);
- V existingValue = result.put(newKey, newValue);
+ V2 newValue = valueMapping.apply(value);
+ V2 existingValue = result.put(newKey, newValue);
if (existingValue != null) {
result.put(newKey, valueMerger.apply(existingValue, newValue));
}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
index 75306f7..dd835a2 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.google.common.base.Predicates.alwaysFalse;
+import static com.google.common.base.Predicates.alwaysTrue;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -179,7 +180,8 @@
factory,
dexType -> dexType.toDescriptorString().equals("Lj$/util/Spliterator$OfPrimitive;"),
Function.identity(),
- null);
+ null,
+ alwaysTrue());
MethodTypeSignature rewritten = rewriter.rewrite(parsedMethodSignature);
assertNotNull(rewritten);
assertTrue(rewritten.hasSignature());
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
index c17ea6b..aafeadf 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
@@ -122,7 +122,8 @@
"A", "Lfoo/bar/Baz;", Origin.unknown(), factory, new Reporter());
assertTrue(parsed.hasSignature());
GenericSignatureTypeRewriter rewriter =
- new GenericSignatureTypeRewriter(factory, alwaysTrue(), Function.identity(), null);
+ new GenericSignatureTypeRewriter(
+ factory, alwaysTrue(), Function.identity(), null, alwaysTrue());
FieldTypeSignature rewrittenType = rewriter.rewrite(parsed);
assertNotNull(rewrittenType);
assertTrue(rewrittenType.hasNoSignature());
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java
index b091f5e..fe4a189 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepAttributesTest.java
@@ -111,7 +111,11 @@
assertEquals("(TO;TM;)TI;", testMethod.getFinalSignatureAttribute());
} else {
assertEquals(
- "(" + descriptor(Supplier.class) + descriptor(Predicate.class) + ")TI;",
+ "(L"
+ + binaryName(Supplier.class)
+ + "<*>;L"
+ + binaryName(Predicate.class)
+ + "<Ljava/lang/Object;>;)TI;",
testMethod.getFinalSignatureAttribute());
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepReferencesPruneTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepReferencesPruneTest.java
index 177ea63..f412cdf 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepReferencesPruneTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepReferencesPruneTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.genericsignature.testclasses.Foo;
@@ -29,68 +30,73 @@
@RunWith(Parameterized.class)
public class GenericSignatureKeepReferencesPruneTest extends TestBase {
- private final String[] EXPECTED =
- new String[] {
- Foo.class.getTypeName() + "<java.lang.String>",
- I.class.getTypeName()
- + "<java.lang.Integer, "
- + Foo.class.getTypeName()
- + "<java.lang.Integer>>",
- I.class.getTypeName()
- + "<java.lang.String, "
- + Foo.class.getTypeName()
- + "<java.lang.String>>",
- "Hello world"
- };
-
- private final String[] EXPECTED_FULL_MODE =
- new String[] {
- "class " + Foo.class.getTypeName(),
- "interface " + I.class.getTypeName(),
- "interface " + I.class.getTypeName(),
- "Hello world"
- };
-
private final TestParameters parameters;
private final boolean isCompat;
+ private final boolean minify;
- @Parameters(name = "{0}, isCompat: {1}")
+ @Parameters(name = "{0}, isCompat: {1}, minify: {2}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values());
}
- public GenericSignatureKeepReferencesPruneTest(TestParameters parameters, boolean isCompat) {
+ public GenericSignatureKeepReferencesPruneTest(
+ TestParameters parameters, boolean isCompat, boolean minify) {
this.parameters = parameters;
this.isCompat = isCompat;
+ this.minify = minify;
}
@Test
public void testJvm() throws Exception {
assumeTrue(parameters.isCfRuntime());
+ assumeTrue(isCompat);
+ assumeTrue(!minify);
testForJvm()
.addProgramClasses(I.class, Foo.class, J.class, K.class, L.class)
.addProgramClassesAndInnerClasses(Main.class)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(EXPECTED);
+ .assertSuccessWithOutputLines(getExpected(Foo.class.getTypeName(), I.class.getTypeName()));
}
@Test
public void testR8() throws Exception {
- (isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend()))
- .addProgramClasses(I.class, Foo.class, J.class, K.class, L.class)
- .addProgramClassesAndInnerClasses(Main.class)
- .addKeepClassAndMembersRules(Main.class)
- .setMinApi(parameters.getApiLevel())
- .addKeepAttributeSignature()
- .addKeepAttributeInnerClassesAndEnclosingMethod()
- .enableInliningAnnotations()
- .enableNeverClassInliningAnnotations()
- .noMinification()
- .compile()
- .inspect(this::inspectSignatures)
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(isCompat ? EXPECTED : EXPECTED_FULL_MODE);
+ R8TestRunResult runResult =
+ (isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend()))
+ .addProgramClasses(I.class, Foo.class, J.class, K.class, L.class)
+ .addProgramClassesAndInnerClasses(Main.class)
+ .addKeepClassAndMembersRules(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeSignature()
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .compile()
+ .inspect(this::inspectSignatures)
+ .run(parameters.getRuntime(), Main.class);
+ runResult.assertSuccess();
+ CodeInspector inspector = runResult.inspector();
+ // Foo and I exists due to the assertions in the inspection.
+ runResult.assertSuccessWithOutputLines(
+ getExpected(
+ inspector.clazz(Foo.class).getFinalName(), inspector.clazz(I.class).getFinalName()));
+ }
+
+ private String[] getExpected(String fooName, String iName) {
+ if (isCompat) {
+ return new String[] {
+ fooName + "<java.lang.String>",
+ iName + "<java.lang.Integer, " + fooName + "<java.lang.Integer>>",
+ iName + "<java.lang.String, " + fooName + "<java.lang.String>>",
+ "Hello world"
+ };
+ } else {
+ return new String[] {
+ "class " + fooName, "interface " + iName, "interface " + iName, "Hello world"
+ };
+ }
}
private void inspectSignatures(CodeInspector inspector) {
@@ -104,7 +110,7 @@
assertEquals(
isCompat
? "<T::Ljava/lang/Comparable<TT;>;R:L"
- + binaryName(Foo.class)
+ + fooClass.getFinalBinaryName()
+ "<TT;>;>Ljava/lang/Object;"
: null,
iClass.getFinalSignatureAttribute());
@@ -113,9 +119,9 @@
assertEquals(
isCompat
? "Ljava/lang/Object;L"
- + binaryName(I.class)
+ + iClass.getFinalBinaryName()
+ "<Ljava/lang/String;L"
- + binaryName(Foo.class)
+ + fooClass.getFinalBinaryName()
+ "<Ljava/lang/String;>;>;"
: null,
fooInnerClass.getFinalSignatureAttribute());
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
index 1fb82ab..a040b37 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePartialTypeArgumentApplierTest.java
@@ -13,19 +13,23 @@
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignatureContextBuilder.TypeParameterContext;
import com.android.tools.r8.graph.GenericSignaturePartialTypeArgumentApplier;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BiPredicateUtils;
+import com.android.tools.r8.utils.MapUtils;
import com.google.common.collect.ImmutableMap;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiPredicate;
@@ -113,7 +117,15 @@
GenericSignaturePartialTypeArgumentApplier.build(
appView,
TypeParameterContext.empty()
- .addPrunedSubstitutions(substitutions)
+ .addPrunedSubstitutions(
+ MapUtils.transform(
+ substitutions,
+ HashMap::new,
+ s -> s,
+ ClassTypeSignature::new,
+ (val1, val2) -> {
+ throw new Unreachable("No keys should be merged");
+ }))
.addLiveParameters(liveVariables),
removedLink,
hasFormalTypeParameters);
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterRecursiveTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterRecursiveTest.java
new file mode 100644
index 0000000..79534a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterRecursiveTest.java
@@ -0,0 +1,94 @@
+// 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.genericsignature;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GenericSignaturePrunedOuterRecursiveTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean isCompat;
+
+ @Parameters(name = "{0}, isCompat: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public GenericSignaturePrunedOuterRecursiveTest(TestParameters parameters, boolean isCompat) {
+ this.parameters = parameters;
+ this.isCompat = isCompat;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ (isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend()))
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Foo.class)
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSignature()
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(options -> options.horizontalClassMergerOptions().disable())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Bar::enclosingMethod")
+ .inspect(this::checkSignatures);
+ }
+
+ public void checkSignatures(CodeInspector inspector) {
+ checkSignature(
+ inspector.clazz(Bar.class.getTypeName() + "$1"),
+ "L" + binaryName(Foo.class) + "<Ljava/util/List<Ljava/lang/Object;>;>;");
+ }
+
+ private void checkSignature(ClassSubject classSubject, String expectedSignature) {
+ assertThat(classSubject, isPresent());
+ assertEquals(isCompat ? expectedSignature : null, classSubject.getFinalSignatureAttribute());
+ }
+
+ public abstract static class Foo<T> {
+
+ void foo(T r) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public static class Bar {
+
+ public static <T extends List<T>> Foo<T> enclosingMethod() {
+ return new Foo<T>() {
+ @Override
+ void foo(T r) {
+ System.out.println("Bar::enclosingMethod");
+ }
+ };
+ }
+
+ public static void run() {
+ enclosingMethod().foo(null);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Bar.run();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java
index b8d02f6..a1cce6b 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java
@@ -4,15 +4,17 @@
package com.android.tools.r8.graph.genericsignature;
-import static org.hamcrest.CoreMatchers.containsString;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
+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.codeinspector.ClassSubject;
import java.lang.reflect.Type;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,26 +35,32 @@
this.parameters = parameters;
}
- @Test(expected = CompilationFailedException.class)
+ @Test
public void testR8() throws Exception {
- testForR8Compat(parameters.getBackend())
- .addInnerClasses(getClass())
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
- .addKeepClassRules(I.class, J.class)
- .addKeepClassAndMembersRulesWithAllowObfuscation(Base.class)
- .addKeepAttributeInnerClassesAndEnclosingMethod()
- .addKeepAttributeSignature()
- .enableInliningAnnotations()
- .enableNeverClassInliningAnnotations()
- .enableNoVerticalClassMergingAnnotations()
- .addVerticallyMergedClassesInspector(
- inspector -> inspector.assertMergedIntoSubtype(A.class))
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorMessageThatMatches(
- containsString("Super type inconsistency in generic signature"));
- });
+ R8TestRunResult runResult =
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(I.class, J.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(Base.class)
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addKeepAttributeSignature()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(A.class))
+ .run(parameters.getRuntime(), Main.class);
+ ClassSubject baseSubject = runResult.inspector().clazz(Base.class);
+ assertThat(baseSubject, isPresent());
+ runResult.assertSuccessWithOutputLines(
+ baseSubject.getFinalName() + "<" + String.class.getTypeName() + ">",
+ J.class.getTypeName() + "<X>",
+ // TODO(b/191871201): This could be `X` if we tracked where type-variables are introduced.
+ I.class.getTypeName() + "<java.lang.Object>",
+ "I::t",
+ "B::r");
}
public interface I<T> {
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index bd10054..a3ae2e9 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -16,7 +16,6 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -47,9 +46,7 @@
}
private void checkSignature(CodeInspector inspector, String signature) {
- String fooImplFinalDescriptor =
- DescriptorUtils.javaTypeToDescriptor(inspector.clazz(FooImpl.class).getFinalName());
- assertEquals("()" + fooImplFinalDescriptor, signature);
+ assertEquals("()" + inspector.clazz(FooImpl.class).getFinalDescriptor(), signature);
}
@Test
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 46b09b8..83bc2aa 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -300,6 +300,7 @@
cmd.append('-Xmx' + args.xmx)
if args.ea:
cmd.append('-ea')
+ cmd.append('-Dcom.android.tools.r8.enableTestAssertions=1')
if args.print_times:
cmd.append('-Dcom.android.tools.r8.printtimes=1')
if hasattr(args, 'properties'):