blob: 9a9ccc0245c70fafe5d9bd3caf347f48032aecda [file] [log] [blame]
// 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 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.KeepPackagePattern;
import com.android.tools.r8.keepanno.ast.KeepQualifiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
import com.android.tools.r8.keepanno.keeprules.KeepEdgeSplitter.Holder;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
public abstract class PgRule {
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 (StringBuilder builder, KeepClassReference classReference) -> {
assert classReference.isBindingReference()
|| classReference.asClassNamePattern().equals(classNamePattern);
KeepRuleExtractor.printClassName(builder, classNamePattern);
};
}
void printKeepOptions(StringBuilder builder) {
KeepRuleExtractor.printKeepOptions(builder, options);
}
public void printRule(StringBuilder builder) {
KeepRuleExtractor.printHeader(builder, metaInfo);
printCondition(builder);
printConsequence(builder);
}
void printCondition(StringBuilder builder) {
if (hasCondition()) {
builder.append("-if ");
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.
*
* <pre>
* -keep class <holder>
* </pre>
*
* and with no dependencies / back-references.
*/
static class PgUnconditionalClassRule extends PgRule {
final KeepQualifiedClassNamePattern holderNamePattern;
final KeepItemPattern holderPattern;
public PgUnconditionalClassRule(KeepEdgeMetaInfo metaInfo, KeepOptions options, Holder holder) {
super(metaInfo, options);
this.holderNamePattern = holder.namePattern;
this.holderPattern = holder.itemPattern;
}
@Override
String getConsequenceKeepType() {
return "-keep";
}
@Override
List<String> getTargetMembers() {
return Collections.emptyList();
}
@Override
void printTargetHolder(StringBuilder builder) {
KeepRuleExtractor.printClassHeader(
builder, holderPattern, classReferencePrinter(holderNamePattern));
}
@Override
void printTargetMember(StringBuilder builder, String memberReference) {
throw new KeepEdgeException("Unreachable");
}
}
abstract static class PgConditionalRuleBase extends PgRule {
final KeepItemPattern classCondition;
final KeepItemPattern classTarget;
final Map<String, KeepMemberPattern> memberPatterns;
final List<String> memberConditions;
public PgConditionalRuleBase(
KeepEdgeMetaInfo metaInfo,
KeepOptions options,
Holder classCondition,
Holder classTarget,
Map<String, KeepMemberPattern> memberPatterns,
List<String> memberConditions) {
super(metaInfo, options);
this.classCondition = classCondition.itemPattern;
this.classTarget = classTarget.itemPattern;
this.memberPatterns = memberPatterns;
this.memberConditions = memberConditions;
}
@Override
boolean hasCondition() {
return true;
}
@Override
List<String> getConditionMembers() {
return memberConditions;
}
@Override
void printConditionHolder(StringBuilder builder) {
KeepRuleExtractor.printClassHeader(builder, classCondition, this::printClassName);
}
@Override
void printConditionMember(StringBuilder builder, String member) {
KeepMemberPattern memberPattern = memberPatterns.get(member);
KeepRuleExtractor.printMemberClause(builder, memberPattern);
}
@Override
void printTargetHolder(StringBuilder builder) {
KeepRuleExtractor.printClassHeader(builder, classTarget, this::printClassName);
}
void printClassName(StringBuilder builder, KeepClassReference clazz) {
KeepRuleExtractor.printClassName(builder, clazz.asClassNamePattern());
}
}
/**
* Representation of conditional rules but without dependencies between condition and target.
*
* <pre>
* -if class <class-condition> { <member-conditions> }
* -keep class <class-target>
* </pre>
*
* and with no dependencies / back-references.
*/
static class PgConditionalClassRule extends PgConditionalRuleBase {
public PgConditionalClassRule(
KeepEdgeMetaInfo metaInfo,
KeepOptions options,
Holder classCondition,
Holder classTarget,
Map<String, KeepMemberPattern> memberPatterns,
List<String> memberConditions) {
super(metaInfo, options, classCondition, classTarget, memberPatterns, memberConditions);
}
@Override
String getConsequenceKeepType() {
return "-keep";
}
@Override
List<String> getTargetMembers() {
return Collections.emptyList();
}
@Override
void printTargetMember(StringBuilder builder, String member) {
throw new KeepEdgeException("Unreachable");
}
}
/**
* Representation of conditional rules but without dependencies between condition and target.
*
* <pre>
* -if class <class-condition> { <member-conditions> }
* -keep[classmembers] class <class-target> { <member-targets> }
* </pre>
*
* and with no dependencies / back-references.
*/
static class PgConditionalMemberRule extends PgConditionalRuleBase {
private final List<String> memberTargets;
public PgConditionalMemberRule(
KeepEdgeMetaInfo metaInfo,
KeepOptions options,
Holder classCondition,
Holder classTarget,
Map<String, KeepMemberPattern> memberPatterns,
List<String> memberConditions,
List<String> memberTargets) {
super(metaInfo, options, classCondition, classTarget, memberPatterns, memberConditions);
this.memberTargets = memberTargets;
}
@Override
String getConsequenceKeepType() {
return "-keepclassmembers";
}
@Override
List<String> getTargetMembers() {
return memberTargets;
}
@Override
void printTargetMember(StringBuilder builder, String member) {
KeepMemberPattern memberPattern = memberPatterns.get(member);
KeepRuleExtractor.printMemberClause(builder, memberPattern);
}
}
abstract static class PgDependentRuleBase extends PgRule {
final KeepQualifiedClassNamePattern holderNamePattern;
final KeepItemPattern holderPattern;
final Map<String, KeepMemberPattern> memberPatterns;
final List<String> memberConditions;
public PgDependentRuleBase(
KeepEdgeMetaInfo metaInfo,
Holder holder,
KeepOptions options,
Map<String, KeepMemberPattern> memberPatterns,
List<String> memberConditions) {
super(metaInfo, options);
this.holderNamePattern = holder.namePattern;
this.holderPattern = holder.itemPattern;
this.memberPatterns = memberPatterns;
this.memberConditions = memberConditions;
}
int nextBackReferenceNumber = 1;
String holderBackReferencePattern;
// TODO(b/248408342): Support back-ref to members too.
private StringBuilder addBackRef(StringBuilder backReferenceBuilder) {
return backReferenceBuilder.append('<').append(nextBackReferenceNumber++).append('>');
}
@Override
List<String> getConditionMembers() {
return memberConditions;
}
@Override
void printConditionHolder(StringBuilder b) {
KeepRuleExtractor.printClassHeader(
b,
holderPattern,
(builder, classReference) -> {
StringBuilder backReference = new StringBuilder();
if (holderNamePattern.isAny()) {
addBackRef(backReference);
builder.append('*');
} else {
printPackagePrefix(builder, holderNamePattern.getPackagePattern(), backReference);
printSimpleClassName(builder, holderNamePattern.getNamePattern(), backReference);
}
holderBackReferencePattern = backReference.toString();
});
}
@Override
void printConditionMember(StringBuilder builder, String member) {
// TODO(b/248408342): Support back-ref to member instances too.
KeepMemberPattern memberPattern = memberPatterns.get(member);
KeepRuleExtractor.printMemberClause(builder, memberPattern);
}
@Override
void printTargetHolder(StringBuilder builder) {
KeepRuleExtractor.printClassHeader(
builder,
holderPattern,
(b, reference) -> {
assert reference.isBindingReference()
|| reference.asClassNamePattern().equals(holderNamePattern);
b.append(holderBackReferencePattern);
});
}
private StringBuilder printPackagePrefix(
StringBuilder builder,
KeepPackagePattern packagePattern,
StringBuilder backReferenceBuilder) {
if (packagePattern.isAny()) {
addBackRef(backReferenceBuilder).append('.');
return builder.append("**.");
}
if (packagePattern.isTop()) {
return builder;
}
assert packagePattern.isExact();
String exactPackage = packagePattern.getExactPackageAsString();
backReferenceBuilder.append(exactPackage).append('.');
return builder.append(exactPackage).append('.');
}
private StringBuilder printSimpleClassName(
StringBuilder builder,
KeepUnqualfiedClassNamePattern namePattern,
StringBuilder backReferenceBuilder) {
if (namePattern.isAny()) {
addBackRef(backReferenceBuilder);
return builder.append('*');
}
assert namePattern.isExact();
String exactName = namePattern.asExact().getExactNameAsString();
backReferenceBuilder.append(exactName);
return builder.append(exactName);
}
}
/**
* Representation of a conditional class rule that is match/instance dependent.
*
* <pre>
* -if class <class-pattern> { <member-condition>* }
* -keep class <class-backref>
* </pre>
*/
static class PgDependentClassRule extends PgDependentRuleBase {
public PgDependentClassRule(
KeepEdgeMetaInfo metaInfo,
Holder holder,
KeepOptions options,
Map<String, KeepMemberPattern> memberPatterns,
List<String> memberConditions) {
super(metaInfo, holder, options, memberPatterns, memberConditions);
}
@Override
String getConsequenceKeepType() {
return "-keep";
}
@Override
boolean hasCondition() {
return true;
}
@Override
List<String> getTargetMembers() {
return Collections.emptyList();
}
@Override
void printTargetMember(StringBuilder builder, String member) {
throw new KeepEdgeException("Unreachable");
}
}
/**
* Representation of a conditional member rule that is match/instance dependent.
*
* <pre>
* -if class <class-pattern> { <member-condition>* }
* -keepclassmembers class <class-backref> { <member-target | member-backref>* }
* </pre>
*
* or if the only condition is the class itself, just:
*
* <pre>
* -keepclassmembers <class-pattern> { <member-target> }
* </pre>
*/
static class PgDependentMembersRule extends PgDependentRuleBase {
final List<String> memberTargets;
public PgDependentMembersRule(
KeepEdgeMetaInfo metaInfo,
Holder holder,
KeepOptions options,
Map<String, KeepMemberPattern> memberPatterns,
List<String> memberConditions,
List<String> memberTargets) {
super(metaInfo, holder, options, memberPatterns, memberConditions);
assert !memberTargets.isEmpty();
this.memberTargets = memberTargets;
}
@Override
boolean hasCondition() {
return !memberConditions.isEmpty();
}
@Override
String getConsequenceKeepType() {
return "-keepclassmembers";
}
@Override
List<String> getTargetMembers() {
return memberTargets;
}
@Override
void printTargetHolder(StringBuilder builder) {
if (hasCondition()) {
super.printTargetHolder(builder);
} else {
KeepRuleExtractor.printClassHeader(
builder, holderPattern, classReferencePrinter(holderNamePattern));
}
}
@Override
void printTargetMember(StringBuilder builder, String member) {
// TODO(b/248408342): Support back-ref to member instances too.
KeepMemberPattern memberPattern = memberPatterns.get(member);
KeepRuleExtractor.printMemberClause(builder, memberPattern);
}
}
}