blob: 71ad5756ee8542f84c54ebb59d444f775cffb47b [file]
// Copyright (c) 2025, 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.partial;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.diagnostic.DefinitionContext;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Definition;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration;
import com.android.tools.r8.references.PackageReference;
import com.android.tools.r8.shaking.ProguardClassFilter;
import com.android.tools.r8.shaking.ProguardClassNameList;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.shaking.ProguardTypeMatcher;
import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
import com.android.tools.r8.shaking.reflectiveidentification.KeepAllReflectiveIdentificationEventConsumer;
import com.android.tools.r8.shaking.reflectiveidentification.ReflectiveIdentification;
import com.android.tools.r8.tracereferences.TraceReferencesConsumer;
import com.android.tools.r8.tracereferences.UseCollector;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.NopDiagnosticsHandler;
import com.android.tools.r8.utils.timing.Timing;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Predicate;
public abstract class R8PartialUseCollector extends UseCollector {
private final ReflectiveIdentification reflectiveIdentification;
private final Set<DexReference> seenAllowObfuscation = ConcurrentHashMap.newKeySet();
private final Set<DexReference> seenDisallowObfuscation = ConcurrentHashMap.newKeySet();
private final Set<String> packagesToKeep = ConcurrentHashMap.newKeySet();
public R8PartialUseCollector(AppView<? extends AppInfoWithClassHierarchy> appView) {
super(
appView,
new MissingReferencesConsumer(),
new NopDiagnosticsHandler(),
getTargetPredicate(appView));
this.reflectiveIdentification =
new ReflectiveIdentification(
appView, new KeepAllReflectiveIdentificationEventConsumer(this));
}
public static Predicate<DexType> getTargetPredicate(
AppView<? extends AppInfoWithClassHierarchy> appView) {
return type -> appView.definitionFor(type) != null;
}
public void run(ExecutorService executorService) throws ExecutionException {
R8PartialR8SubCompilationConfiguration r8SubCompilationConfiguration =
appView.options().partialSubCompilationConfiguration.asR8();
traceClasses(r8SubCompilationConfiguration.getDexingOutputClasses(), executorService);
reflectiveIdentification.processWorklist(Timing.empty());
commitPackagesToKeep();
}
private void commitPackagesToKeep() {
if (packagesToKeep.isEmpty()) {
return;
}
ProguardClassNameList.Builder packageNameList = ProguardClassNameList.builder();
for (String packageToKeep : ListUtils.sort(packagesToKeep, String::compareTo)) {
ProguardTypeMatcher packageNameMatcher =
ProguardTypeMatcher.create(
IdentifierPatternWithWildcards.withoutWildcards(packageToKeep),
ClassOrType.CLASS,
appView.dexItemFactory());
packageNameList.addClassName(false, packageNameMatcher);
}
ProguardConfiguration proguardConfiguration = appView.options().getProguardConfiguration();
proguardConfiguration.setKeepPackageNamesPatterns(
ProguardClassFilter.builder()
.addPattern(packageNameList.build())
.addPatterns(proguardConfiguration.getKeepPackageNamesPatterns().getPatterns())
.build());
}
public abstract void keep(
Definition definition, DefinitionContext referencedFrom, boolean allowObfuscation);
@Override
public void notifyPresentClass(DexClass clazz, DefinitionContext referencedFrom) {
notifyPresentItem(clazz, referencedFrom);
}
@Override
public void notifyPresentField(DexClassAndField field, DefinitionContext referencedFrom) {
notifyPresentItem(field, referencedFrom);
}
@Override
public void notifyPresentMethod(DexClassAndMethod method, DefinitionContext referencedFrom) {
notifyPresentItem(method, referencedFrom);
}
@Override
public void notifyPresentMethod(
DexClassAndMethod method, DefinitionContext referencedFrom, DexMethod reference) {
notifyPresentItem(method, referencedFrom);
}
@Override
public void notifyPresentMethodOverride(
DexClassAndMethod method, ProgramMethod override, DefinitionContext referencedFrom) {
if (seenDisallowObfuscation.add(method.getReference())) {
keep(method, referencedFrom, false);
}
}
private void notifyPresentItem(Definition definition, DefinitionContext referencedFrom) {
if (seenAllowObfuscation.add(definition.getReference())) {
keep(definition, referencedFrom, true);
}
}
@Override
public void notifyPackageOf(Definition definition) {
packagesToKeep.add(definition.getContextType().getPackageName());
}
@Override
protected void notifyReflectiveIdentification(DexMethod invokedMethod, ProgramMethod method) {
reflectiveIdentification.scanInvoke(invokedMethod, method);
}
private static class MissingReferencesConsumer implements TraceReferencesConsumer {
@Override
public void acceptType(TracedClass tracedClass, DiagnosticsHandler handler) {
assert false;
}
@Override
public void acceptField(TracedField tracedField, DiagnosticsHandler handler) {
assert tracedField.isMissingDefinition();
}
@Override
public void acceptMethod(TracedMethod tracedMethod, DiagnosticsHandler handler) {
assert tracedMethod.isMissingDefinition();
}
@Override
public void acceptPackage(PackageReference pkg, DiagnosticsHandler handler) {
assert false;
}
}
}