blob: 5ad1cfa6d6ff2ab26f912372036e0842823cf962 [file] [log] [blame]
// Copyright (c) 2020, 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.tracereferences;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Map;
import java.util.Set;
class Tracer {
private final Set<String> descriptors;
private Set<DexType> types = Sets.newIdentityHashSet();
private Map<DexType, Set<DexMethod>> methods = Maps.newIdentityHashMap();
private Map<DexType, Set<DexField>> fields = Maps.newIdentityHashMap();
private Set<String> keepPackageNames = Sets.newHashSet();
private Set<DexReference> missingDefinitions = Sets.newHashSet();
private final DirectMappedDexApplication application;
private final AppInfoWithClassHierarchy appInfo;
Tracer(Set<String> descriptors, AndroidApp inputApp) throws Exception {
this.descriptors = descriptors;
InternalOptions options = new InternalOptions();
application =
new ApplicationReader(inputApp, options, new Timing("ReferenceTrace")).read().toDirect();
appInfo =
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
application,
ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
MainDexClasses.createEmptyMainDexClasses());
}
Result run() {
UseCollector useCollector = new UseCollector(appInfo.dexItemFactory());
for (DexProgramClass clazz : application.classes()) {
useCollector.setContext(clazz);
useCollector.registerSuperType(clazz, clazz.superType);
for (DexType implementsType : clazz.interfaces.values) {
useCollector.registerSuperType(clazz, implementsType);
}
clazz.forEachProgramMethod(useCollector::registerMethod);
clazz.forEachField(useCollector::registerField);
}
return new Result(application, types, keepPackageNames, fields, methods, missingDefinitions);
}
private boolean isTargetType(DexType type) {
return descriptors.contains(type.toDescriptorString());
}
private void addType(DexType type) {
if (isTargetType(type) && types.add(type)) {
DexClass clazz = appInfo.definitionFor(type);
if (clazz != null && clazz.accessFlags.isVisibilityDependingOnPackage()) {
keepPackageNames.add(clazz.type.getPackageName());
}
methods.put(type, Sets.newIdentityHashSet());
fields.put(type, Sets.newIdentityHashSet());
}
}
private void addField(DexField field) {
addType(field.type);
DexEncodedField baseField = appInfo.resolveField(field).getResolvedField();
if (baseField != null && baseField.holder() != field.holder) {
field = baseField.field;
}
addType(field.holder);
if (isTargetType(field.holder)) {
Set<DexField> typeFields = fields.get(field.holder);
assert typeFields != null;
if (baseField != null) {
if (baseField.accessFlags.isVisibilityDependingOnPackage()) {
keepPackageNames.add(baseField.holder().getPackageName());
}
} else {
missingDefinitions.add(field);
}
typeFields.add(field);
}
}
private void addMethod(DexMethod method) {
addType(method.holder);
for (DexType parameterType : method.proto.parameters.values) {
addType(parameterType);
}
addType(method.proto.returnType);
if (isTargetType(method.holder)) {
Set<DexMethod> typeMethods = methods.get(method.holder);
assert typeMethods != null;
DexClass holder = appInfo.definitionForHolder(method);
DexEncodedMethod definition = method.lookupOnClass(holder);
if (definition != null) {
if (definition.accessFlags.isVisibilityDependingOnPackage()) {
keepPackageNames.add(definition.holder().getPackageName());
}
} else {
missingDefinitions.add(method);
}
typeMethods.add(method);
}
}
class UseCollector extends UseRegistry {
private DexProgramClass context;
UseCollector(DexItemFactory factory) {
super(factory);
}
public void setContext(DexProgramClass context) {
this.context = context;
}
@Override
public void registerInitClass(DexType clazz) {
addType(clazz);
}
@Override
public void registerInvokeVirtual(DexMethod method) {
ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method);
DexEncodedMethod target =
resolutionResult.isVirtualTarget() ? resolutionResult.getSingleTarget() : null;
if (target != null && target.method != method) {
addType(method.holder);
addMethod(target.method);
} else {
addMethod(method);
}
}
@Override
public void registerInvokeDirect(DexMethod method) {
addMethod(method);
}
@Override
public void registerInvokeStatic(DexMethod method) {
DexEncodedMethod target = appInfo.unsafeResolveMethodDueToDexFormat(method).getSingleTarget();
if (target != null && target.method != method) {
addType(method.holder);
addMethod(target.method);
} else {
addMethod(method);
}
}
@Override
public void registerInvokeInterface(DexMethod method) {
registerInvokeVirtual(method);
}
@Override
public void registerInvokeSuper(DexMethod method) {
DexEncodedMethod superTarget = appInfo.lookupSuperTarget(method, context);
if (superTarget != null) {
addMethod(superTarget.method);
} else {
addMethod(method);
}
}
@Override
public void registerInstanceFieldWrite(DexField field) {
addField(field);
}
@Override
public void registerInstanceFieldRead(DexField field) {
addField(field);
}
@Override
public void registerNewInstance(DexType type) {
addType(type);
}
@Override
public void registerStaticFieldRead(DexField field) {
addField(field);
}
@Override
public void registerStaticFieldWrite(DexField field) {
addField(field);
}
@Override
public void registerTypeReference(DexType type) {
addType(type);
}
@Override
public void registerInstanceOf(DexType type) {
addType(type);
}
private void registerField(DexEncodedField field) {
registerTypeReference(field.field.type);
}
private void registerMethod(ProgramMethod method) {
DexEncodedMethod superTarget =
appInfo
.resolveMethodOn(method.getHolder(), method.getReference())
.lookupInvokeSpecialTarget(context, appInfo);
if (superTarget != null) {
addMethod(superTarget.method);
}
for (DexType type : method.getDefinition().parameters().values) {
registerTypeReference(type);
}
for (DexAnnotation annotation : method.getDefinition().annotations().annotations) {
if (annotation.annotation.type == appInfo.dexItemFactory().annotationThrows) {
DexValueArray dexValues = annotation.annotation.elements[0].value.asDexValueArray();
for (DexValue dexValType : dexValues.getValues()) {
registerTypeReference(dexValType.asDexValueType().value);
}
}
}
registerTypeReference(method.getDefinition().returnType());
method.registerCodeReferences(this);
}
private void registerSuperType(DexProgramClass clazz, DexType superType) {
registerTypeReference(superType);
// If clazz overrides any methods in superType, we should keep those as well.
clazz.forEachMethod(
method -> {
ResolutionResult resolutionResult =
appInfo.resolveMethodOn(superType, method.method, superType != clazz.superType);
DexEncodedMethod dexEncodedMethod = resolutionResult.getSingleTarget();
if (dexEncodedMethod != null) {
addMethod(dexEncodedMethod.method);
}
});
}
@Override
public void registerCallSite(DexCallSite callSite) {
super.registerCallSite(callSite);
// For Lambda's, in order to find the correct use, we need to register the method for the
// functional interface.
List<DexType> directInterfaces = LambdaDescriptor.getInterfaces(callSite, appInfo);
if (directInterfaces != null) {
for (DexType directInterface : directInterfaces) {
DexProgramClass clazz = asProgramClassOrNull(appInfo.definitionFor(directInterface));
if (clazz != null) {
clazz.forEachProgramVirtualMethodMatching(
definition -> definition.getReference().name.equals(callSite.methodName),
this::registerMethod);
}
}
}
}
}
}