check-flagged-apis: record super class when parsing classes

Extend ClassSymbol with a nullable reference to the class' superclass.

Bug: 334870672
Test: atest --host check-flagged-apis-test
Change-Id: Ia2741a4d7fb5de908a03ef640f5fcd38d0ce0e28
This commit is contained in:
Mårten Kongstad
2024-05-06 21:42:15 +02:00
parent d2c707613e
commit c3f05a6d92
2 changed files with 44 additions and 17 deletions

View File

@@ -49,6 +49,7 @@ private val API_VERSIONS =
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<api version="3"> <api version="3">
<class name="android/Clazz" since="1"> <class name="android/Clazz" since="1">
<extends name="java/lang/Object"/>
<method name="&lt;init>()V"/> <method name="&lt;init>()V"/>
<field name="FOO"/> <field name="FOO"/>
<method name="getErrorCode()I"/> <method name="getErrorCode()I"/>
@@ -57,6 +58,7 @@ private val API_VERSIONS =
<method name="innerClassArg(Landroid/Clazz${"$"}Builder;)"/> <method name="innerClassArg(Landroid/Clazz${"$"}Builder;)"/>
</class> </class>
<class name="android/Clazz${"$"}Builder" since="2"> <class name="android/Clazz${"$"}Builder" since="2">
<extends name="java/lang/Object"/>
</class> </class>
</api> </api>
""" """
@@ -95,7 +97,9 @@ class CheckFlaggedApisTest {
fun testParseApiSignature() { fun testParseApiSignature() {
val expected = val expected =
setOf( setOf(
Pair(Symbol.createClass("android/Clazz", setOf()), Flag("android.flag.foo")), Pair(
Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
Flag("android.flag.foo")),
Pair(Symbol.createMethod("android/Clazz", "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.createField("android/Clazz", "FOO"), Flag("android.flag.foo")),
Pair(Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")), Pair(Symbol.createMethod("android/Clazz", "getErrorCode()"), Flag("android.flag.foo")),
@@ -108,7 +112,9 @@ class CheckFlaggedApisTest {
Pair( Pair(
Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"), Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
Flag("android.flag.foo")), Flag("android.flag.foo")),
Pair(Symbol.createClass("android/Clazz/Builder", setOf()), Flag("android.flag.bar")), Pair(
Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
Flag("android.flag.bar")),
) )
val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream()) val actual = parseApiSignature("in-memory", API_SIGNATURE.byteInputStream())
assertEquals(expected, actual) assertEquals(expected, actual)
@@ -126,14 +132,14 @@ class CheckFlaggedApisTest {
fun testParseApiVersions() { fun testParseApiVersions() {
val expected: Set<Symbol> = val expected: Set<Symbol> =
setOf( setOf(
Symbol.createClass("android/Clazz", setOf()), Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
Symbol.createMethod("android/Clazz", "Clazz()"), Symbol.createMethod("android/Clazz", "Clazz()"),
Symbol.createField("android/Clazz", "FOO"), Symbol.createField("android/Clazz", "FOO"),
Symbol.createMethod("android/Clazz", "getErrorCode()"), Symbol.createMethod("android/Clazz", "getErrorCode()"),
Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"), Symbol.createMethod("android/Clazz", "setData(I[[ILandroid/util/Utility;)"),
Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"), Symbol.createMethod("android/Clazz", "setVariableData(I[Landroid/util/Atom;)"),
Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"), Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
Symbol.createClass("android/Clazz/Builder", setOf()), Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
) )
val actual = parseApiVersions(API_VERSIONS.byteInputStream()) val actual = parseApiVersions(API_VERSIONS.byteInputStream())
assertEquals(expected, actual) assertEquals(expected, actual)
@@ -146,6 +152,7 @@ class CheckFlaggedApisTest {
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<api version="3"> <api version="3">
<class name="android/Clazz${'$'}Foo${'$'}Bar" since="1"> <class name="android/Clazz${'$'}Foo${'$'}Bar" since="1">
<extends name="java/lang/Object"/>
<method name="&lt;init>()V"/> <method name="&lt;init>()V"/>
</class> </class>
</api> </api>
@@ -153,7 +160,7 @@ class CheckFlaggedApisTest {
.trim() .trim()
val expected: Set<Symbol> = val expected: Set<Symbol> =
setOf( setOf(
Symbol.createClass("android/Clazz/Foo/Bar", setOf()), Symbol.createClass("android/Clazz/Foo/Bar", "java/lang/Object", setOf()),
Symbol.createMethod("android/Clazz/Foo/Bar", "Bar()"), Symbol.createMethod("android/Clazz/Foo/Bar", "Bar()"),
) )
val actual = parseApiVersions(apiVersions.byteInputStream()) val actual = parseApiVersions(apiVersions.byteInputStream())
@@ -193,6 +200,7 @@ class CheckFlaggedApisTest {
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<api version="3"> <api version="3">
<class name="android/Clazz" since="1"> <class name="android/Clazz" since="1">
<extends name="java/lang/Object"/>
<implements name="android/Interface"/> <implements name="android/Interface"/>
<method name="foo()Z"/> <method name="foo()Z"/>
</class> </class>
@@ -217,7 +225,8 @@ class CheckFlaggedApisTest {
val expected = val expected =
setOf<ApiError>( setOf<ApiError>(
DisabledFlaggedApiIsPresentError( DisabledFlaggedApiIsPresentError(
Symbol.createClass("android/Clazz", setOf()), Flag("android.flag.foo")), Symbol.createClass("android/Clazz", "java/lang/Object", setOf()),
Flag("android.flag.foo")),
DisabledFlaggedApiIsPresentError( DisabledFlaggedApiIsPresentError(
Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")), Symbol.createMethod("android/Clazz", "Clazz()"), Flag("android.flag.foo")),
DisabledFlaggedApiIsPresentError( DisabledFlaggedApiIsPresentError(
@@ -234,7 +243,8 @@ class CheckFlaggedApisTest {
Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"), Symbol.createMethod("android/Clazz", "innerClassArg(Landroid/Clazz/Builder;)"),
Flag("android.flag.foo")), Flag("android.flag.foo")),
DisabledFlaggedApiIsPresentError( DisabledFlaggedApiIsPresentError(
Symbol.createClass("android/Clazz/Builder", setOf()), Flag("android.flag.bar")), Symbol.createClass("android/Clazz/Builder", "java/lang/Object", setOf()),
Flag("android.flag.bar")),
) )
val actual = val actual =
findErrors( findErrors(

View File

@@ -58,8 +58,11 @@ internal sealed class Symbol {
companion object { companion object {
private val FORBIDDEN_CHARS = listOf('#', '$', '.') private val FORBIDDEN_CHARS = listOf('#', '$', '.')
fun createClass(clazz: String, interfaces: Set<String>): Symbol { fun createClass(clazz: String, superclass: String?, interfaces: Set<String>): Symbol {
return ClassSymbol(toInternalFormat(clazz), interfaces.map { toInternalFormat(it) }.toSet()) return ClassSymbol(
toInternalFormat(clazz),
superclass?.let { toInternalFormat(it) },
interfaces.map { toInternalFormat(it) }.toSet())
} }
fun createField(clazz: String, field: String): Symbol { fun createField(clazz: String, field: String): Symbol {
@@ -83,7 +86,11 @@ internal sealed class Symbol {
abstract fun toPrettyString(): String abstract fun toPrettyString(): String
} }
internal data class ClassSymbol(val clazz: String, val interfaces: Set<String>) : Symbol() { internal data class ClassSymbol(
val clazz: String,
val superclass: String?,
val interfaces: Set<String>
) : Symbol() {
override fun toPrettyString(): String = "$clazz" override fun toPrettyString(): String = "$clazz"
} }
@@ -198,6 +205,7 @@ internal fun parseApiSignature(path: String, input: InputStream): Set<Pair<Symbo
val symbol = val symbol =
Symbol.createClass( Symbol.createClass(
cls.baselineElementId(), cls.baselineElementId(),
cls.superClass()?.baselineElementId(),
cls.allInterfaces() cls.allInterfaces()
.map { it.baselineElementId() } .map { it.baselineElementId() }
.filter { it != cls.baselineElementId() } .filter { it != cls.baselineElementId() }
@@ -263,19 +271,28 @@ internal fun parseApiVersions(input: InputStream): Set<Symbol> {
requireNotNull(cls.getAttribute("name")) { requireNotNull(cls.getAttribute("name")) {
"Bad XML: <class> element without name attribute" "Bad XML: <class> element without name attribute"
} }
var superclass: String? = null
val interfaces = mutableSetOf<String>() val interfaces = mutableSetOf<String>()
val children = cls.getChildNodes() val children = cls.getChildNodes()
for (j in 0.rangeUntil(children.getLength())) { for (j in 0.rangeUntil(children.getLength())) {
val child = children.item(j) val child = children.item(j)
if (child.getNodeName() == "implements") { when (child.getNodeName()) {
val interfaceName = "extends" -> {
requireNotNull(child.getAttribute("name")) { superclass =
"Bad XML: <implements> element without name attribute" requireNotNull(child.getAttribute("name")) {
} "Bad XML: <extends> element without name attribute"
interfaces.add(interfaceName) }
}
"implements" -> {
val interfaceName =
requireNotNull(child.getAttribute("name")) {
"Bad XML: <implements> element without name attribute"
}
interfaces.add(interfaceName)
}
} }
} }
output.add(Symbol.createClass(className, interfaces)) output.add(Symbol.createClass(className, superclass, interfaces))
} }
val fields = document.getElementsByTagName("field") val fields = document.getElementsByTagName("field")