blob: 8b18105aad0a1d3ee93dd059b3fb7e9ac14d47ea [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.ast;
import com.android.tools.r8.keepanno.keeprules.RulePrinter;
import com.android.tools.r8.keepanno.keeprules.RulePrintingUtils;
import java.util.Set;
public class KeepMemberAccessPattern {
private static final KeepMemberAccessPattern ANY =
new KeepMemberAccessPattern(
AccessVisibility.all(),
ModifierPattern.any(),
ModifierPattern.any(),
ModifierPattern.any());
public static KeepMemberAccessPattern anyMemberAccess() {
return ANY;
}
public static Builder memberBuilder() {
return new Builder();
}
private final Set<AccessVisibility> allowedVisibilities;
private final ModifierPattern staticPattern;
private final ModifierPattern finalPattern;
private final ModifierPattern syntheticPattern;
public KeepMemberAccessPattern(
Set<AccessVisibility> allowedVisibilities,
ModifierPattern staticPattern,
ModifierPattern finalPattern,
ModifierPattern syntheticPattern) {
this.allowedVisibilities = allowedVisibilities;
this.staticPattern = staticPattern;
this.finalPattern = finalPattern;
this.syntheticPattern = syntheticPattern;
}
@Override
public String toString() {
if (isAny()) {
return "*";
}
StringBuilder builder = new StringBuilder();
RulePrinter printer = RulePrinter.withoutBackReferences(builder);
RulePrintingUtils.printMemberAccess(printer, this);
return builder.toString();
}
/** True if this matches any possible access flag. */
public boolean isAny() {
return isAnyVisibility()
&& staticPattern.isAny()
&& finalPattern.isAny()
&& syntheticPattern.isAny();
}
public boolean isAnyVisibility() {
return AccessVisibility.containsAll(allowedVisibilities);
}
public boolean isVisibilityAllowed(AccessVisibility visibility) {
return allowedVisibilities.contains(visibility);
}
public Set<AccessVisibility> getAllowedAccessVisibilities() {
return allowedVisibilities;
}
public ModifierPattern getStaticPattern() {
return staticPattern;
}
public ModifierPattern getFinalPattern() {
return finalPattern;
}
public ModifierPattern getSyntheticPattern() {
return syntheticPattern;
}
public static class Builder extends BuilderBase<KeepMemberAccessPattern, Builder> {
@Override
public Builder self() {
return this;
}
@Override
public KeepMemberAccessPattern build() {
Set<AccessVisibility> allowedVisibilities = getAllowedVisibilities();
ModifierPattern staticPattern = getStaticPattern();
ModifierPattern finalPattern = getFinalPattern();
ModifierPattern syntheticPattern = getSyntheticPattern();
if (AccessVisibility.containsAll(allowedVisibilities)
&& staticPattern.isAny()
&& finalPattern.isAny()
&& syntheticPattern.isAny()) {
return KeepMemberAccessPattern.anyMemberAccess();
}
KeepMemberAccessPattern pattern =
new KeepMemberAccessPattern(
allowedVisibilities, staticPattern, finalPattern, syntheticPattern);
assert !pattern.isAny();
return pattern;
}
}
public abstract static class BuilderBase<
M extends KeepMemberAccessPattern, B extends BuilderBase<M, B>> {
private final Set<AccessVisibility> allowed = AccessVisibility.createSet();
private final Set<AccessVisibility> disallowed = AccessVisibility.createSet();
private ModifierPattern staticPattern = ModifierPattern.any();
private ModifierPattern finalPattern = ModifierPattern.any();
private ModifierPattern syntheticPattern = ModifierPattern.any();
BuilderBase() {}
public abstract B self();
public abstract M build();
public B copyOfMemberAccess(KeepMemberAccessPattern accessPattern) {
allowed.clear();
disallowed.clear();
allowed.addAll(accessPattern.getAllowedAccessVisibilities());
staticPattern = accessPattern.getStaticPattern();
finalPattern = accessPattern.getFinalPattern();
return self();
}
public ModifierPattern getStaticPattern() {
return staticPattern;
}
public ModifierPattern getFinalPattern() {
return finalPattern;
}
public ModifierPattern getSyntheticPattern() {
return syntheticPattern;
}
public Set<AccessVisibility> getAllowedVisibilities() {
// Fast path for any visibility pattern.
if (allowed.isEmpty() && disallowed.isEmpty()) {
return AccessVisibility.all();
}
// If no explict "allows" have been set, all visibilities are allowed.
Set<AccessVisibility> result = AccessVisibility.createSet();
if (allowed.isEmpty()) {
result.addAll(AccessVisibility.all());
} else {
result.addAll(allowed);
}
// Any explict disallow narrows the allowed visibilities.
result.removeAll(disallowed);
if (result.isEmpty()) {
throw new KeepEdgeException("Empty access visibility pattern will never match a member");
}
return result;
}
public B setAccessVisibility(AccessVisibility visibility, boolean allow) {
Set<AccessVisibility> set = allow ? allowed : disallowed;
set.add(visibility);
return self();
}
public B setStatic(boolean allow) {
staticPattern = ModifierPattern.fromAllowValue(allow);
return self();
}
public B setFinal(boolean allow) {
finalPattern = ModifierPattern.fromAllowValue(allow);
return self();
}
public B setSynthetic(boolean allow) {
syntheticPattern = ModifierPattern.fromAllowValue(allow);
return self();
}
}
}