blob: c709a314a2cd40511309c073446ce4c556984d08 [file] [log] [blame]
// Copyright (c) 2017, 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.graph;
import static kotlinx.metadata.FlagsKt.flagsOf;
import com.android.tools.r8.dex.Constants;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
import kotlinx.metadata.Flag;
/** Access flags common to classes, methods and fields. */
public abstract class AccessFlags<T extends AccessFlags<T>> {
protected Flag[] EMPTY_FLAG = {};
protected static final int BASE_FLAGS
= Constants.ACC_PUBLIC
| Constants.ACC_PRIVATE
| Constants.ACC_PROTECTED
| Constants.ACC_STATIC
| Constants.ACC_FINAL
| Constants.ACC_SYNTHETIC;
// Ordered list of flag names. Must be consistent with getPredicates.
private static final List<String> NAMES = ImmutableList.of(
"public",
"private",
"protected",
"static",
"final",
"synthetic"
);
// Get ordered list of flag predicates. Must be consistent with getNames.
protected List<BooleanSupplier> getPredicates() {
return ImmutableList.of(
this::isPublic,
this::isPrivate,
this::isProtected,
this::isStatic,
this::isFinal,
this::isSynthetic);
}
// Get ordered list of flag names. Must be consistent with getPredicates.
protected List<String> getNames() {
return NAMES;
}
protected int originalFlags;
protected int modifiedFlags;
protected AccessFlags(int originalFlags, int modifiedFlags) {
this.originalFlags = originalFlags;
this.modifiedFlags = modifiedFlags;
}
public abstract T copy();
public abstract T self();
public int materialize() {
return modifiedFlags;
}
public abstract int getAsCfAccessFlags();
public abstract int getAsDexAccessFlags();
public int getAsKotlinFlags() {
List<Flag> flags = new ArrayList<>();
if (isPrivate()) {
flags.add(Flag.IS_PRIVATE);
}
if (isProtected()) {
flags.add(Flag.IS_PROTECTED);
}
if (isPublic()) {
flags.add(Flag.IS_PUBLIC);
}
if (isFinal()) {
flags.add(Flag.IS_FINAL);
}
if (isOpen()) {
flags.add(Flag.IS_OPEN);
}
return flagsOf(flags.toArray(EMPTY_FLAG));
}
public final int getOriginalAccessFlags() {
return originalFlags;
}
@Override
public boolean equals(Object object) {
if (object instanceof AccessFlags) {
AccessFlags other = (AccessFlags) object;
return originalFlags == other.originalFlags && modifiedFlags == other.modifiedFlags;
}
return false;
}
@Override
public int hashCode() {
return originalFlags | modifiedFlags;
}
public boolean isMoreVisibleThan(
AccessFlags other, String packageNameThis, String packageNameOther) {
int visibilityOrdinal = visibilityOrdinal();
if (visibilityOrdinal > other.visibilityOrdinal()) {
return true;
}
if (visibilityOrdinal == other.visibilityOrdinal()
&& isVisibilityDependingOnPackage()
&& !packageNameThis.equals(packageNameOther)) {
return true;
}
return false;
}
public boolean isAtLeastAsVisibleAs(AccessFlags other) {
return visibilityOrdinal() >= other.visibilityOrdinal();
}
public boolean isSameVisibility(AccessFlags other) {
return visibilityOrdinal() == other.visibilityOrdinal();
}
private int visibilityOrdinal() {
// public > protected > package > private
if (isPublic()) {
return 3;
}
if (isProtected()) {
return 2;
}
if (isPrivate()) {
return 0;
}
// Package-private
return 1;
}
public boolean isVisibilityDependingOnPackage() {
return visibilityOrdinal() == 1 || visibilityOrdinal() == 2;
}
public boolean isPublic() {
return isSet(Constants.ACC_PUBLIC);
}
public void setPublic() {
assert !isPrivate() && !isProtected();
set(Constants.ACC_PUBLIC);
}
public void unsetPublic() {
unset(Constants.ACC_PUBLIC);
}
public boolean isPrivate() {
return isSet(Constants.ACC_PRIVATE);
}
public void setPrivate() {
assert !isPublic() && !isProtected();
set(Constants.ACC_PRIVATE);
}
public void unsetPrivate() {
unset(Constants.ACC_PRIVATE);
}
public boolean isProtected() {
return isSet(Constants.ACC_PROTECTED);
}
public void setProtected() {
assert !isPublic() && !isPrivate();
set(Constants.ACC_PROTECTED);
}
public void unsetProtected() {
unset(Constants.ACC_PROTECTED);
}
public boolean isStatic() {
return isSet(Constants.ACC_STATIC);
}
public void setStatic() {
set(Constants.ACC_STATIC);
}
public boolean isOpen() {
return !isFinal();
}
public boolean isFinal() {
return isSet(Constants.ACC_FINAL);
}
public void setFinal() {
set(Constants.ACC_FINAL);
}
public void unsetFinal() {
unset(Constants.ACC_FINAL);
}
public boolean isSynthetic() {
return isSet(Constants.ACC_SYNTHETIC);
}
public void setSynthetic() {
set(Constants.ACC_SYNTHETIC);
}
public void unsetSynthetic() {
unset(Constants.ACC_SYNTHETIC);
}
public void demoteFromSynthetic() {
demote(Constants.ACC_SYNTHETIC);
}
public void promoteToFinal() {
promote(Constants.ACC_FINAL);
}
public void demoteFromFinal() {
demote(Constants.ACC_FINAL);
}
public boolean isPromotedToPublic() {
return isPromoted(Constants.ACC_PUBLIC);
}
public void promoteToPublic() {
demote(Constants.ACC_PRIVATE | Constants.ACC_PROTECTED);
promote(Constants.ACC_PUBLIC);
}
public void promoteToStatic() {
promote(Constants.ACC_STATIC);
}
private boolean wasSet(int flag) {
return (originalFlags & flag) != 0;
}
protected boolean isSet(int flag) {
return (modifiedFlags & flag) != 0;
}
protected void set(int flag) {
originalFlags |= flag;
modifiedFlags |= flag;
}
protected void unset(int flag) {
originalFlags &= ~flag;
modifiedFlags &= ~flag;
}
protected boolean isPromoted(int flag) {
return !wasSet(flag) && isSet(flag);
}
protected void promote(int flag) {
modifiedFlags |= flag;
}
protected void demote(int flag) {
modifiedFlags &= ~flag;
}
public String toSmaliString() {
return toStringInternal(true);
}
@Override
public String toString() {
return toStringInternal(false);
}
private String toStringInternal(boolean ignoreSuper) {
List<String> names = getNames();
List<BooleanSupplier> predicates = getPredicates();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < names.size(); i++) {
if (predicates.get(i).getAsBoolean()) {
if (!ignoreSuper || !names.get(i).equals("super")) {
if (builder.length() > 0) {
builder.append(' ');
}
builder.append(names.get(i));
}
}
}
return builder.toString();
}
}