blob: 3a03ff7ea2a1d60484afff472b2b6eb05ff7ef74 [file]
// Copyright (c) 2026, 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.blastradius;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
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.shaking.Enqueuer;
import com.android.tools.r8.shaking.KeepClassInfo;
import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.shaking.KeepInfo;
import com.android.tools.r8.shaking.KeepInfoCollectionEventConsumer;
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.shaking.ProguardKeepRuleBase;
import com.android.tools.r8.utils.InternalOptions;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class RootSetBlastRadius {
private final Map<ProguardKeepRuleBase, RootSetBlastRadiusForRule> blastRadius;
private RootSetBlastRadius(Map<ProguardKeepRuleBase, RootSetBlastRadiusForRule> blastRadius) {
this.blastRadius = blastRadius;
}
public static Builder builder(
AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer.Mode mode) {
InternalOptions options = appView.options();
return options.hasProguardConfiguration()
&& options.getProguardConfiguration().isPrintBlastRadius()
&& mode.isFinalTreeShaking()
? new Builder()
: null;
}
public Collection<RootSetBlastRadiusForRule> getBlastRadius() {
return blastRadius.values();
}
public static class Builder implements KeepInfoCollectionEventConsumer {
private final Map<ProguardKeepRuleBase, RootSetBlastRadiusForRule> blastRadius =
new IdentityHashMap<>();
@Override
public void acceptKeepClassInfo(
DexType type, Consumer<? super KeepClassInfo.Joiner> keepInfoEffect) {
acceptKeepInfo(
type,
keepInfoEffect,
KeepClassInfo.newEmptyJoiner(),
RootSetBlastRadiusForRule::addMatchedClass);
}
@Override
public void acceptKeepFieldInfo(
DexField field, Consumer<? super KeepFieldInfo.Joiner> keepInfoEffect) {
acceptKeepInfo(
field,
keepInfoEffect,
KeepFieldInfo.newEmptyJoiner(),
RootSetBlastRadiusForRule::addMatchedField);
}
@Override
public void acceptKeepMethodInfo(
DexMethod method, Consumer<? super KeepMethodInfo.Joiner> keepInfoEffect) {
acceptKeepInfo(
method,
keepInfoEffect,
KeepMethodInfo.newEmptyJoiner(),
RootSetBlastRadiusForRule::addMatchedMethod);
}
private <R extends DexReference, J extends KeepInfo.Joiner<?, ?, ?>> void acceptKeepInfo(
R reference,
Consumer<? super J> keepInfoEffect,
J emptyJoiner,
BiConsumer<RootSetBlastRadiusForRule, R> addReferenceToRuleBlastRadius) {
keepInfoEffect.accept(emptyJoiner);
for (ProguardKeepRuleBase rule : emptyJoiner.getRules()) {
if (rule.isProguardIfRule()) {
// Perform attribution to the root -if rule.
rule = rule.asProguardIfRule().getParentOrThis();
}
RootSetBlastRadiusForRule ruleBlastRadius =
blastRadius.computeIfAbsent(rule, RootSetBlastRadiusForRule::new);
addReferenceToRuleBlastRadius.accept(ruleBlastRadius, reference);
}
}
public RootSetBlastRadius build() {
return new RootSetBlastRadius(blastRadius);
}
}
}