blob: f33c9c3ebc55c7c0e54022ed0f0bb8bed02acfd1 [file] [log] [blame]
// 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;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
public class GenericSignatureTypeVariableRemover {
private final AppView<?> appView;
private final Predicate<InnerClassAttribute> innerClassPruned;
private final Predicate<EnclosingMethodAttribute> enclosingClassOrMethodPruned;
public GenericSignatureTypeVariableRemover(
AppView<?> appView,
Predicate<InnerClassAttribute> innerClassPruned,
Predicate<EnclosingMethodAttribute> enclosingClassOrMethodPruned) {
this.appView = appView;
this.innerClassPruned = innerClassPruned;
this.enclosingClassOrMethodPruned = enclosingClassOrMethodPruned;
}
public void removeDeadGenericSignatureTypeVariables(DexProgramClass clazz) {
if (clazz.getClassSignature().hasNoSignature() || clazz.getClassSignature().isInvalid()) {
return;
}
Map<String, DexType> substitutions = new HashMap<>();
getPrunedTypeParameters(clazz, substitutions, false);
if (substitutions.isEmpty()) {
return;
}
GenericSignaturePartialTypeArgumentApplier genericSignatureTypeArgumentApplier =
GenericSignaturePartialTypeArgumentApplier.build(
appView, clazz.getClassSignature(), substitutions);
clazz.setClassSignature(
genericSignatureTypeArgumentApplier.visitClassSignature(clazz.getClassSignature()));
clazz
.methods()
.forEach(
method -> {
if (method.getGenericSignature().hasSignature()
&& method.getGenericSignature().isValid()
&& method.isVirtualMethod()) {
method.setGenericSignature(
genericSignatureTypeArgumentApplier.visitMethodSignature(
method.getGenericSignature()));
}
});
clazz
.instanceFields()
.forEach(
field -> {
if (field.getGenericSignature().hasSignature()
&& field.getGenericSignature().isValid()) {
field.setGenericSignature(
genericSignatureTypeArgumentApplier.visitFieldTypeSignature(
field.getGenericSignature()));
}
});
}
private void getPrunedTypeParameters(
DexClass clazz, Map<String, DexType> substitutions, boolean seenPruned) {
InnerClassAttribute innerClassAttribute = clazz.getInnerClassAttributeForThisClass();
if (innerClassAttribute != null
&& innerClassAttribute.getOuter() != null
&& (seenPruned || innerClassPruned.test(innerClassAttribute))) {
DexClass outerClass = appView.definitionFor(innerClassAttribute.getOuter());
if (outerClass != null && outerClass.getClassSignature().isValid()) {
updateMap(outerClass.getClassSignature().getFormalTypeParameters(), substitutions);
getPrunedTypeParameters(outerClass, substitutions, true);
}
}
if (clazz.getEnclosingMethodAttribute() != null
&& (seenPruned || enclosingClassOrMethodPruned.test(clazz.getEnclosingMethodAttribute()))) {
DexClass outerClass =
appView.definitionFor(clazz.getEnclosingMethodAttribute().getEnclosingType());
if (outerClass == null) {
return;
}
if (clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
DexEncodedMethod enclosingMethod =
outerClass.lookupMethod(clazz.getEnclosingMethodAttribute().getEnclosingMethod());
if (enclosingMethod != null) {
updateMap(enclosingMethod.getGenericSignature().getFormalTypeParameters(), substitutions);
if (enclosingMethod.isStatic()) {
return;
}
}
}
if (outerClass.getClassSignature().isValid()) {
updateMap(outerClass.getClassSignature().getFormalTypeParameters(), substitutions);
}
getPrunedTypeParameters(outerClass, substitutions, true);
}
}
private void updateMap(
List<FormalTypeParameter> formalTypeParameters, Map<String, DexType> substitutions) {
// We are updating the map going from inner most to outer, thus the any overriding formal type
// parameters will be in the substitution map already.
formalTypeParameters.forEach(
parameter -> {
if (substitutions.containsKey(parameter.getName())) {
return;
}
// The null substitution will use the wildcard as argument, which is smaller than using
// Ljava/lang/Object;
DexType substitution = null;
FieldTypeSignature classBound = parameter.getClassBound();
if (classBound != null
&& classBound.hasSignature()
&& classBound.isClassTypeSignature()) {
substitution = classBound.asClassTypeSignature().type();
}
substitutions.put(parameter.getName(), substitution);
});
}
}