check-flagged-apis: add more details to Symbol class
Change Symbol from a wrapper around a String to a more fleshed out data class; symbols now encode if they represent a class, or a class member (including a reference to the containing class). Bug: 334870672 Test: atest --host check-flagged-apis-test Test: croot && ./build/tools/check-flagged-apis/check-flagged-apis.sh # with and without this CL; the output should be the same Change-Id: I003535c721c45d559d00fb3e008325e1db0e18c0
This commit is contained in:
parent
02525a88de
commit
a1fe37137e
2 changed files with 65 additions and 52 deletions
|
@ -95,20 +95,20 @@ class CheckFlaggedApisTest {
|
|||
fun testParseApiSignature() {
|
||||
val expected =
|
||||
setOf(
|
||||
Pair(Symbol("android/Clazz"), Flag("android.flag.foo")),
|
||||
Pair(Symbol("android/Clazz/Clazz()"), Flag("android.flag.foo")),
|
||||
Pair(Symbol("android/Clazz/FOO"), Flag("android.flag.foo")),
|
||||
Pair(Symbol("android/Clazz/getErrorCode()"), Flag("android.flag.foo")),
|
||||
Pair(Symbol.createClass("android/Clazz"), Flag("android.flag.foo")),
|
||||
Pair(Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")),
|
||||
Pair(Symbol.createField("android/Clazz", "FOO"), Flag("android.flag.foo")),
|
||||
Pair(Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")),
|
||||
Pair(
|
||||
Symbol("android/Clazz/setData(I[[ILandroid/util/Utility;)"),
|
||||
Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
|
||||
Flag("android.flag.foo")),
|
||||
Pair(
|
||||
Symbol("android/Clazz/setVariableData(I[Landroid/util/Atom;)"),
|
||||
Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
|
||||
Flag("android.flag.foo")),
|
||||
Pair(
|
||||
Symbol("android/Clazz/innerClassArg(Landroid/Clazz/Builder;)"),
|
||||
Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
|
||||
Flag("android.flag.foo")),
|
||||
Pair(Symbol("android/Clazz/Builder"), Flag("android.flag.bar")),
|
||||
Pair(Symbol.createClass("android/Clazz/Builder"), Flag("android.flag.bar")),
|
||||
)
|
||||
val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream())
|
||||
assertEquals(expected, actual)
|
||||
|
@ -126,14 +126,14 @@ class CheckFlaggedApisTest {
|
|||
fun testParseApiVersions() {
|
||||
val expected: Set<Symbol> =
|
||||
setOf(
|
||||
Symbol("android/Clazz"),
|
||||
Symbol("android/Clazz/Clazz()"),
|
||||
Symbol("android/Clazz/FOO"),
|
||||
Symbol("android/Clazz/getErrorCode()"),
|
||||
Symbol("android/Clazz/setData(I[[ILandroid/util/Utility;)"),
|
||||
Symbol("android/Clazz/setVariableData(I[Landroid/util/Atom;)"),
|
||||
Symbol("android/Clazz/innerClassArg(Landroid/Clazz/Builder;)"),
|
||||
Symbol("android/Clazz/Builder"),
|
||||
Symbol.createClass("android/Clazz"),
|
||||
Symbol.createMethod("android/Clazz", "Clazz()"),
|
||||
Symbol.createField("android/Clazz", "FOO"),
|
||||
Symbol.createMethod("android/Clazz", "getErrorCode()"),
|
||||
Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
|
||||
Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
|
||||
Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
|
||||
Symbol.createClass("android/Clazz/Builder"),
|
||||
)
|
||||
val actual = parseApiVersions(API_VERSIONS.byteInputStream())
|
||||
assertEquals(expected, actual)
|
||||
|
@ -153,8 +153,8 @@ class CheckFlaggedApisTest {
|
|||
.trim()
|
||||
val expected: Set<Symbol> =
|
||||
setOf(
|
||||
Symbol("android/Clazz/Foo/Bar"),
|
||||
Symbol("android/Clazz/Foo/Bar/Bar()"),
|
||||
Symbol.createClass("android/Clazz/Foo/Bar"),
|
||||
Symbol.createMethod("android/Clazz/Foo/Bar", "Bar()"),
|
||||
)
|
||||
val actual = parseApiVersions(apiVersions.byteInputStream())
|
||||
assertEquals(expected, actual)
|
||||
|
@ -175,23 +175,25 @@ class CheckFlaggedApisTest {
|
|||
fun testFindErrorsDisabledFlaggedApiIsPresent() {
|
||||
val expected =
|
||||
setOf<ApiError>(
|
||||
DisabledFlaggedApiIsPresentError(Symbol("android/Clazz"), Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol("android/Clazz/Clazz()"), Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(Symbol("android/Clazz/FOO"), Flag("android.flag.foo")),
|
||||
Symbol.createClass("android/Clazz"), Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol("android/Clazz/getErrorCode()"), Flag("android.flag.foo")),
|
||||
Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol("android/Clazz/setData(I[[ILandroid/util/Utility;)"),
|
||||
Symbol.createField("android/Clazz", "FOO"), Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
|
||||
Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol("android/Clazz/setVariableData(I[Landroid/util/Atom;)"),
|
||||
Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
|
||||
Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol("android/Clazz/innerClassArg(Landroid/Clazz/Builder;)"),
|
||||
Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
|
||||
Flag("android.flag.foo")),
|
||||
DisabledFlaggedApiIsPresentError(
|
||||
Symbol("android/Clazz/Builder"), Flag("android.flag.bar")),
|
||||
Symbol.createClass("android/Clazz/Builder"), Flag("android.flag.bar")),
|
||||
)
|
||||
val actual =
|
||||
findErrors(
|
||||
|
|
|
@ -54,29 +54,41 @@ import org.w3c.dom.Node
|
|||
*
|
||||
* 1. https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2
|
||||
*/
|
||||
@JvmInline
|
||||
internal value class Symbol(val name: String) {
|
||||
internal sealed class Symbol {
|
||||
companion object {
|
||||
private val FORBIDDEN_CHARS = listOf('#', '$', '.')
|
||||
|
||||
/** Create a new Symbol from a String that may include delimiters other than dot. */
|
||||
fun create(name: String): Symbol {
|
||||
var sanitizedName = name
|
||||
fun createClass(clazz: String): Symbol {
|
||||
return ClassSymbol(toInternalFormat(clazz))
|
||||
}
|
||||
|
||||
fun createField(clazz: String, field: String): Symbol {
|
||||
require(!field.contains("(") && !field.contains(")"))
|
||||
return MemberSymbol(toInternalFormat(clazz), toInternalFormat(field))
|
||||
}
|
||||
|
||||
fun createMethod(clazz: String, method: String): Symbol {
|
||||
return MemberSymbol(toInternalFormat(clazz), toInternalFormat(method))
|
||||
}
|
||||
|
||||
protected fun toInternalFormat(name: String): String {
|
||||
var internalName = name
|
||||
for (ch in FORBIDDEN_CHARS) {
|
||||
sanitizedName = sanitizedName.replace(ch, '/')
|
||||
internalName = internalName.replace(ch, '/')
|
||||
}
|
||||
return Symbol(sanitizedName)
|
||||
return internalName
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
require(!name.isEmpty()) { "empty string" }
|
||||
for (ch in FORBIDDEN_CHARS) {
|
||||
require(!name.contains(ch)) { "$name: contains $ch" }
|
||||
}
|
||||
}
|
||||
abstract fun toPrettyString(): String
|
||||
}
|
||||
|
||||
override fun toString(): String = name.toString()
|
||||
internal data class ClassSymbol(val clazz: String) : Symbol() {
|
||||
override fun toPrettyString(): String = "$clazz"
|
||||
}
|
||||
|
||||
internal data class MemberSymbol(val clazz: String, val member: String) : Symbol() {
|
||||
override fun toPrettyString(): String = "$clazz/$member"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,7 +114,7 @@ internal data class EnabledFlaggedApiNotPresentError(
|
|||
override val flag: Flag
|
||||
) : ApiError() {
|
||||
override fun toString(): String {
|
||||
return "error: enabled @FlaggedApi not present in built artifact: symbol=$symbol flag=$flag"
|
||||
return "error: enabled @FlaggedApi not present in built artifact: symbol=${symbol.toPrettyString()} flag=$flag"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,14 +123,14 @@ internal data class DisabledFlaggedApiIsPresentError(
|
|||
override val flag: Flag
|
||||
) : ApiError() {
|
||||
override fun toString(): String {
|
||||
return "error: disabled @FlaggedApi is present in built artifact: symbol=$symbol flag=$flag"
|
||||
return "error: disabled @FlaggedApi is present in built artifact: symbol=${symbol.toPrettyString()} flag=$flag"
|
||||
}
|
||||
}
|
||||
|
||||
internal data class UnknownFlagError(override val symbol: Symbol, override val flag: Flag) :
|
||||
ApiError() {
|
||||
override fun toString(): String {
|
||||
return "error: unknown flag: symbol=$symbol flag=$flag"
|
||||
return "error: unknown flag: symbol=${symbol.toPrettyString()} flag=$flag"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,29 +195,28 @@ internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbo
|
|||
object : BaseItemVisitor() {
|
||||
override fun visitClass(cls: ClassItem) {
|
||||
getFlagOrNull(cls)?.let { flag ->
|
||||
val symbol = Symbol.create(cls.baselineElementId())
|
||||
val symbol = Symbol.createClass(cls.baselineElementId())
|
||||
output.add(Pair(symbol, flag))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitField(field: FieldItem) {
|
||||
getFlagOrNull(field)?.let { flag ->
|
||||
val symbol = Symbol.create(field.baselineElementId())
|
||||
val symbol =
|
||||
Symbol.createField(field.containingClass().baselineElementId(), field.name())
|
||||
output.add(Pair(symbol, flag))
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitMethod(method: MethodItem) {
|
||||
getFlagOrNull(method)?.let { flag ->
|
||||
val name = buildString {
|
||||
append(method.containingClass().qualifiedName())
|
||||
append(".")
|
||||
val methodName = buildString {
|
||||
append(method.name())
|
||||
append("(")
|
||||
method.parameters().joinTo(this, separator = "") { it.type().internalName() }
|
||||
append(")")
|
||||
}
|
||||
val symbol = Symbol.create(name)
|
||||
val symbol = Symbol.createMethod(method.containingClass().qualifiedName(), methodName)
|
||||
output.add(Pair(symbol, flag))
|
||||
}
|
||||
}
|
||||
|
@ -246,7 +257,7 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> {
|
|||
requireNotNull(cls.getAttribute("name")) {
|
||||
"Bad XML: <class> element without name attribute"
|
||||
}
|
||||
output.add(Symbol.create(className.replace("/", ".")))
|
||||
output.add(Symbol.createClass(className))
|
||||
}
|
||||
|
||||
val fields = document.getElementsByTagName("field")
|
||||
|
@ -261,7 +272,7 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> {
|
|||
requireNotNull(field.getParentNode()?.getAttribute("name")) {
|
||||
"Bad XML: top level <field> element"
|
||||
}
|
||||
output.add(Symbol.create("${className.replace("/", ".")}.$fieldName"))
|
||||
output.add(Symbol.createField(className, fieldName))
|
||||
}
|
||||
|
||||
val methods = document.getElementsByTagName("method")
|
||||
|
@ -285,7 +296,7 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> {
|
|||
if (methodName == "<init>") {
|
||||
methodName = packageAndClassName.split("/").last()
|
||||
}
|
||||
output.add(Symbol.create("$packageAndClassName/$methodName($methodArgs)"))
|
||||
output.add(Symbol.createMethod(packageAndClassName, "$methodName($methodArgs)"))
|
||||
}
|
||||
|
||||
return output
|
||||
|
|
Loading…
Reference in a new issue