blob: c262658631be3cf0d91237cfb61d40a86a1843de [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.keepradius;
import com.android.tools.r8.shaking.ProguardClassNameList;
import com.android.tools.r8.shaking.ProguardKeepRuleBase;
import com.android.tools.r8.shaking.ProguardKeepRuleType;
import com.android.tools.r8.shaking.ProguardMemberRule;
import com.android.tools.r8.shaking.ProguardMemberType;
import com.android.tools.r8.shaking.ProguardTypeMatcher;
import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchTypePattern;
import com.android.tools.r8.utils.internal.TraversalContinuation;
import java.util.List;
public class KeepRadiusKeepRuleClassifier {
public static boolean isPackageWideKeepRule(ProguardKeepRuleBase rule) {
if (rule.isProguardIfRule()) {
return isPackageWideKeepRule(rule.asProguardIfRule().getSubsequentRule());
}
if (rule.hasClassAnnotations() || rule.hasInheritanceClassName()) {
return false;
}
// Must have exactly one class name that ends in .* or .**.
ProguardClassNameList classNames = rule.getClassNames();
if (classNames == null) {
return false;
}
TraversalContinuation<?, ?> traversalContinuation =
classNames.traverseTypeMatchers(
matcher -> {
if (!matcher.isNegated() && matcher.getMatcher() instanceof MatchTypePattern) {
String pattern = ((MatchTypePattern) matcher.getMatcher()).getPattern();
return TraversalContinuation.breakIf(
pattern.endsWith(".*") || pattern.endsWith(".**"));
}
return TraversalContinuation.doContinue();
});
if (traversalContinuation.isContinue()) {
return false;
}
// If it's a -keep rule, then this is already bad.
if (rule.getType() == ProguardKeepRuleType.KEEP) {
return true;
}
// If it's a -keepclassmembers rule, then the member rules must be sufficiently broad for the
// rule to be classified as package wide.
if (rule.getType() == ProguardKeepRuleType.KEEP_CLASS_MEMBERS) {
for (ProguardMemberRule memberRule : rule.getMemberRules()) {
ProguardMemberType memberType = memberRule.getRuleType();
// Package wide if type is all.
if (memberType == ProguardMemberType.ALL
|| memberType == ProguardMemberType.ALL_FIELDS
|| memberType == ProguardMemberType.ALL_METHODS) {
return true;
}
// Package wide if it matches all class initializers.
if (memberType == ProguardMemberType.CLINIT) {
return true;
}
// Package wide if it matches at least all default instance initializers.
if (memberType == ProguardMemberType.INIT) {
List<ProguardTypeMatcher> arguments = memberRule.getArguments();
if (arguments == null || arguments.isEmpty() || arguments.get(0).isTripleDotPattern()) {
return true;
}
}
}
}
// If it's a -keepclasseswithmembers rule, then report it.
if (rule.getType() == ProguardKeepRuleType.KEEP_CLASSES_WITH_MEMBERS) {
return true;
}
return false;
}
}