| // Copyright (c) 2023, 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.keepanno.keeprules; |
| |
| import static com.android.tools.r8.keepanno.keeprules.RulePrintingUtils.printClassHeader; |
| import static com.android.tools.r8.keepanno.keeprules.RulePrintingUtils.printMemberClause; |
| |
| import com.android.tools.r8.keepanno.ast.KeepClassReference; |
| import com.android.tools.r8.keepanno.ast.KeepEdgeException; |
| import com.android.tools.r8.keepanno.ast.KeepEdgeMetaInfo; |
| import com.android.tools.r8.keepanno.ast.KeepItemPattern; |
| import com.android.tools.r8.keepanno.ast.KeepMemberPattern; |
| import com.android.tools.r8.keepanno.ast.KeepOptions; |
| import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern; |
| import com.android.tools.r8.keepanno.keeprules.KeepRuleExtractor.Holder; |
| import com.android.tools.r8.keepanno.keeprules.RulePrinter.BackReferencePrinter; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.function.BiConsumer; |
| |
| public abstract class PgRule { |
| public enum TargetKeepKind { |
| JUST_MEMBERS(RulePrintingUtils.KEEP_CLASS_MEMBERS), |
| CLASS_OR_MEMBERS(RulePrintingUtils.KEEP), |
| CLASS_AND_MEMBERS(RulePrintingUtils.KEEP_CLASSES_WITH_MEMBERS); |
| |
| private final String ruleKind; |
| |
| TargetKeepKind(String ruleKind) { |
| this.ruleKind = ruleKind; |
| } |
| |
| String getKeepRuleKind() { |
| return ruleKind; |
| } |
| } |
| |
| private static void printNonEmptyMembersPatternAsDefaultInitWorkaround(StringBuilder builder) { |
| // If no members is given, compat R8 and legacy full mode will implicitly keep <init>(). |
| // Add a keep of finalize which is a library method that would be kept in any case. |
| builder.append(" { void finalize(); }"); |
| } |
| |
| private final KeepEdgeMetaInfo metaInfo; |
| private final KeepOptions options; |
| |
| private PgRule(KeepEdgeMetaInfo metaInfo, KeepOptions options) { |
| this.metaInfo = metaInfo; |
| this.options = options; |
| } |
| |
| // Helper to print the class-name pattern in a class-item. |
| // The item is assumed to either be a binding (where the binding is a class with |
| // the supplied class-name pattern), or a class-item that has the class-name pattern itself (e.g., |
| // without a binding indirection). |
| public static BiConsumer<StringBuilder, KeepClassReference> classReferencePrinter( |
| KeepQualifiedClassNamePattern classNamePattern) { |
| return (builder, classReference) -> { |
| assert classReference.isBindingReference() |
| || classReference.asClassNamePattern().equals(classNamePattern); |
| RulePrintingUtils.printClassName( |
| classNamePattern, RulePrinter.withoutBackReferences(builder)); |
| }; |
| } |
| |
| void printKeepOptions(StringBuilder builder) { |
| RulePrintingUtils.printKeepOptions(builder, options); |
| } |
| |
| public void printRule(StringBuilder builder) { |
| RulePrintingUtils.printHeader(builder, metaInfo); |
| printCondition(builder); |
| printConsequence(builder); |
| } |
| |
| void printCondition(StringBuilder builder) { |
| if (hasCondition()) { |
| builder.append(RulePrintingUtils.IF).append(' '); |
| printConditionHolder(builder); |
| List<String> members = getConditionMembers(); |
| if (!members.isEmpty()) { |
| builder.append(" {"); |
| for (String member : members) { |
| builder.append(' '); |
| printConditionMember(builder, member); |
| } |
| builder.append(" }"); |
| } |
| builder.append(' '); |
| } |
| } |
| |
| void printConsequence(StringBuilder builder) { |
| builder.append(getConsequenceKeepType()); |
| printKeepOptions(builder); |
| builder.append(' '); |
| printTargetHolder(builder); |
| List<String> members = getTargetMembers(); |
| if (!members.isEmpty()) { |
| builder.append(" {"); |
| for (String member : members) { |
| builder.append(' '); |
| printTargetMember(builder, member); |
| } |
| builder.append(" }"); |
| } |
| } |
| |
| boolean hasCondition() { |
| return false; |
| } |
| |
| List<String> getConditionMembers() { |
| throw new KeepEdgeException("Unreachable"); |
| } |
| |
| abstract String getConsequenceKeepType(); |
| |
| abstract List<String> getTargetMembers(); |
| |
| void printConditionHolder(StringBuilder builder) { |
| throw new KeepEdgeException("Unreachable"); |
| } |
| |
| void printConditionMember(StringBuilder builder, String member) { |
| throw new KeepEdgeException("Unreachable"); |
| } |
| |
| abstract void printTargetHolder(StringBuilder builder); |
| |
| abstract void printTargetMember(StringBuilder builder, String member); |
| |
| /** |
| * Representation of an unconditional rule to keep a class and methods. |
| * |
| * <pre> |
| * -keep[classeswithmembers] class <holder> [{ <members> }] |
| * </pre> |
| * |
| * and with no dependencies / back-references. |
| */ |
| static class PgUnconditionalRule extends PgRule { |
| private final KeepQualifiedClassNamePattern holderNamePattern; |
| private final KeepItemPattern holderPattern; |
| private final TargetKeepKind targetKeepKind; |
| private final List<String> targetMembers; |
| private final Map<String, KeepMemberPattern> memberPatterns; |
| |
| public PgUnconditionalRule( |
| KeepEdgeMetaInfo metaInfo, |
| Holder holder, |
| KeepOptions options, |
| Map<String, KeepMemberPattern> memberPatterns, |
| List<String> targetMembers, |
| TargetKeepKind targetKeepKind) { |
| super(metaInfo, options); |
| assert !targetKeepKind.equals(TargetKeepKind.JUST_MEMBERS); |
| this.holderNamePattern = holder.namePattern; |
| this.holderPattern = holder.itemPattern; |
| this.targetKeepKind = targetKeepKind; |
| this.memberPatterns = memberPatterns; |
| this.targetMembers = targetMembers; |
| } |
| |
| @Override |
| String getConsequenceKeepType() { |
| return targetKeepKind.getKeepRuleKind(); |
| } |
| |
| @Override |
| List<String> getTargetMembers() { |
| return targetMembers; |
| } |
| |
| @Override |
| void printTargetHolder(StringBuilder builder) { |
| printClassHeader(builder, holderPattern, classReferencePrinter(holderNamePattern)); |
| if (getTargetMembers().isEmpty()) { |
| printNonEmptyMembersPatternAsDefaultInitWorkaround(builder); |
| } |
| } |
| |
| @Override |
| void printTargetMember(StringBuilder builder, String memberReference) { |
| KeepMemberPattern memberPattern = memberPatterns.get(memberReference); |
| printMemberClause(memberPattern, RulePrinter.withoutBackReferences(builder)); |
| } |
| } |
| |
| /** |
| * Representation of conditional rules but without dependencies between condition and target. |
| * |
| * <pre> |
| * -if class <class-condition> [{ <member-conditions> }] |
| * -keepX class <class-target> [{ <member-targets> }] |
| * </pre> |
| * |
| * and with no dependencies / back-references. |
| */ |
| static class PgConditionalRule extends PgRule { |
| |
| final KeepItemPattern classCondition; |
| final KeepItemPattern classTarget; |
| final Map<String, KeepMemberPattern> memberPatterns; |
| final List<String> memberConditions; |
| private final List<String> memberTargets; |
| private final TargetKeepKind keepKind; |
| |
| public PgConditionalRule( |
| KeepEdgeMetaInfo metaInfo, |
| KeepOptions options, |
| Holder classCondition, |
| Holder classTarget, |
| Map<String, KeepMemberPattern> memberPatterns, |
| List<String> memberConditions, |
| List<String> memberTargets, |
| TargetKeepKind keepKind) { |
| super(metaInfo, options); |
| this.classCondition = classCondition.itemPattern; |
| this.classTarget = classTarget.itemPattern; |
| this.memberPatterns = memberPatterns; |
| this.memberConditions = memberConditions; |
| this.memberTargets = memberTargets; |
| this.keepKind = keepKind; |
| } |
| |
| @Override |
| boolean hasCondition() { |
| return true; |
| } |
| |
| @Override |
| List<String> getConditionMembers() { |
| return memberConditions; |
| } |
| |
| @Override |
| void printConditionHolder(StringBuilder builder) { |
| printClassHeader(builder, classCondition, this::printClassName); |
| } |
| |
| @Override |
| void printConditionMember(StringBuilder builder, String member) { |
| KeepMemberPattern memberPattern = memberPatterns.get(member); |
| printMemberClause(memberPattern, RulePrinter.withoutBackReferences(builder)); |
| } |
| |
| @Override |
| void printTargetHolder(StringBuilder builder) { |
| printClassHeader(builder, classTarget, this::printClassName); |
| if (getTargetMembers().isEmpty()) { |
| PgRule.printNonEmptyMembersPatternAsDefaultInitWorkaround(builder); |
| } |
| } |
| |
| @Override |
| String getConsequenceKeepType() { |
| return keepKind.getKeepRuleKind(); |
| } |
| |
| @Override |
| List<String> getTargetMembers() { |
| return memberTargets; |
| } |
| |
| @Override |
| void printTargetMember(StringBuilder builder, String member) { |
| KeepMemberPattern memberPattern = memberPatterns.get(member); |
| printMemberClause(memberPattern, RulePrinter.withoutBackReferences(builder)); |
| } |
| |
| private void printClassName(StringBuilder builder, KeepClassReference clazz) { |
| RulePrintingUtils.printClassName( |
| clazz.asClassNamePattern(), RulePrinter.withoutBackReferences(builder)); |
| } |
| } |
| |
| /** |
| * Representation of a conditional rule that is match/instance dependent. |
| * |
| * <pre> |
| * -if class <class-pattern> [{ <member-condition>* }] |
| * -keepX class <class-backref> [{ <member-target | member-backref>* }] |
| * </pre> |
| * |
| * or if the only condition is the class itself and targeting members, just: |
| * |
| * <pre> |
| * -keepclassmembers <class-pattern> { <member-target> } |
| * </pre> |
| */ |
| static class PgDependentMembersRule extends PgRule { |
| |
| private final KeepQualifiedClassNamePattern holderNamePattern; |
| private final KeepItemPattern holderPattern; |
| private final Map<String, KeepMemberPattern> memberPatterns; |
| private final List<String> memberConditions; |
| private final List<String> memberTargets; |
| private final TargetKeepKind keepKind; |
| |
| private int nextBackReferenceNumber = 1; |
| private String holderBackReferencePattern = null; |
| private Map<String, String> membersBackReferencePatterns = new HashMap<>(); |
| |
| public PgDependentMembersRule( |
| KeepEdgeMetaInfo metaInfo, |
| Holder holder, |
| KeepOptions options, |
| Map<String, KeepMemberPattern> memberPatterns, |
| List<String> memberConditions, |
| List<String> memberTargets, |
| TargetKeepKind keepKind) { |
| super(metaInfo, options); |
| this.holderNamePattern = holder.namePattern; |
| this.holderPattern = holder.itemPattern; |
| this.memberPatterns = memberPatterns; |
| this.memberConditions = memberConditions; |
| this.memberTargets = memberTargets; |
| this.keepKind = keepKind; |
| } |
| |
| private int getNextBackReferenceNumber() { |
| return nextBackReferenceNumber++; |
| } |
| |
| @Override |
| boolean hasCondition() { |
| // We can avoid an if-rule if the condition is simply the class and the target is just |
| // members. |
| boolean canUseDependentRule = |
| memberConditions.isEmpty() && keepKind == TargetKeepKind.JUST_MEMBERS; |
| return !canUseDependentRule; |
| } |
| |
| @Override |
| String getConsequenceKeepType() { |
| return keepKind.getKeepRuleKind(); |
| } |
| |
| @Override |
| List<String> getConditionMembers() { |
| return memberConditions; |
| } |
| |
| @Override |
| List<String> getTargetMembers() { |
| return memberTargets; |
| } |
| |
| @Override |
| void printConditionHolder(StringBuilder b) { |
| printClassHeader( |
| b, |
| holderPattern, |
| (builder, classReference) -> { |
| BackReferencePrinter printer = |
| RulePrinter.withBackReferences(b, this::getNextBackReferenceNumber); |
| RulePrintingUtils.printClassName(holderNamePattern, printer); |
| holderBackReferencePattern = printer.getBackReference(); |
| }); |
| } |
| |
| @Override |
| void printConditionMember(StringBuilder builder, String member) { |
| KeepMemberPattern memberPattern = memberPatterns.get(member); |
| BackReferencePrinter printer = |
| RulePrinter.withBackReferences(builder, this::getNextBackReferenceNumber); |
| printMemberClause(memberPattern, printer); |
| membersBackReferencePatterns.put(member, printer.getBackReference()); |
| } |
| |
| @Override |
| void printTargetHolder(StringBuilder builder) { |
| printClassHeader( |
| builder, |
| holderPattern, |
| (b, reference) -> { |
| assert reference.isBindingReference() |
| || reference.asClassNamePattern().equals(holderNamePattern); |
| if (hasCondition()) { |
| b.append(holderBackReferencePattern); |
| } else { |
| assert holderBackReferencePattern == null; |
| RulePrintingUtils.printClassName( |
| holderNamePattern, RulePrinter.withoutBackReferences(builder)); |
| } |
| }); |
| if (getTargetMembers().isEmpty()) { |
| PgRule.printNonEmptyMembersPatternAsDefaultInitWorkaround(builder); |
| } |
| } |
| |
| @Override |
| void printTargetMember(StringBuilder builder, String member) { |
| if (hasCondition()) { |
| String backref = membersBackReferencePatterns.get(member); |
| if (backref != null) { |
| builder.append(backref); |
| return; |
| } |
| } |
| KeepMemberPattern memberPattern = memberPatterns.get(member); |
| printMemberClause(memberPattern, RulePrinter.withoutBackReferences(builder)); |
| } |
| } |
| } |