[IR] Support stable ordering in Kotlin-like IR dump

This is needed for signature tests to work with the same golden data
on both K1 and K2, since sometimes K1 and K2 produce IR with different
order of declarations and supertypes.
This commit is contained in:
Sergej Jaskiewicz
2023-05-04 18:34:21 +02:00
committed by Space Team
parent 71e81aa30e
commit bfa2c92c7f
2 changed files with 53 additions and 31 deletions
@@ -48,20 +48,13 @@ private fun IrFile.shouldSkipDump(): Boolean {
return entry.lineStartOffsetsAreEmpty return entry.lineStartOffsetsAreEmpty
} }
class DumpIrTreeVisitor( /**
out: Appendable, * Sorts the declarations in the list using the result of [IrDeclaration.render] as the sorting key.
normalizeNames: Boolean = false, *
private val stableOrder: Boolean = false * The exception is properties with backing fields and [IrAnonymousInitializer]s: their relative order is preserved.
) : IrElementVisitor<Unit, String> { */
internal fun List<IrDeclaration>.stableOrdered(): List<IrDeclaration> {
private val printer = Printer(out, " ") val strictOrder = hashMapOf<IrDeclaration, Int>()
private val elementRenderer = RenderIrElementVisitor(normalizeNames, !stableOrder)
private fun IrType.render() = elementRenderer.renderType(this)
private fun List<IrDeclaration>.ordered(): List<IrDeclaration> {
if (!stableOrder) return this
val strictOrder = mutableMapOf<IrDeclaration, Int>()
var idx = 0 var idx = 0
@@ -86,6 +79,18 @@ class DumpIrTreeVisitor(
} }
} }
class DumpIrTreeVisitor(
out: Appendable,
normalizeNames: Boolean = false,
private val stableOrder: Boolean = false
) : IrElementVisitor<Unit, String> {
private val printer = Printer(out, " ")
private val elementRenderer = RenderIrElementVisitor(normalizeNames, !stableOrder)
private fun IrType.render() = elementRenderer.renderType(this)
private fun List<IrDeclaration>.ordered(): List<IrDeclaration> = if (stableOrder) stableOrdered() else this
override fun visitElement(element: IrElement, data: String) { override fun visitElement(element: IrElement, data: String) {
element.dumpLabeledElementWith(data) { element.dumpLabeledElementWith(data) {
if (element is IrAnnotationContainer) { if (element is IrAnnotationContainer) {
@@ -66,6 +66,7 @@ class KotlinLikeDumpOptions(
val printFakeOverridesStrategy: FakeOverridesStrategy = FakeOverridesStrategy.ALL, val printFakeOverridesStrategy: FakeOverridesStrategy = FakeOverridesStrategy.ALL,
val bodyPrintingStrategy: BodyPrintingStrategy = BodyPrintingStrategy.PRINT_BODIES, val bodyPrintingStrategy: BodyPrintingStrategy = BodyPrintingStrategy.PRINT_BODIES,
val printElseAsTrue: Boolean = false, val printElseAsTrue: Boolean = false,
val stableOrder: Boolean = false,
/* /*
TODO add more options: TODO add more options:
always print visibility? always print visibility?
@@ -166,6 +167,22 @@ private class KotlinLikeDumper(val p: Printer, val options: KotlinLikeDumpOption
typeArg.printTypeArgumentWithNoIndent() typeArg.printTypeArgumentWithNoIndent()
} }
@JvmName("orderedDeclarations") // Prevent JVM signature clash
private fun List<IrDeclaration>.ordered() = if (options.stableOrder) stableOrdered() else this
@JvmName("orderedTypes") // Prevent JVM signature clash
private fun List<IrType>.ordered(): List<IrType> {
if (!options.stableOrder) return this
fun isNonInterfaceType(type: IrType) = type.classifierOrNull?.let {
it !is IrClassSymbol || !it.owner.isInterface
} ?: true
val (classTypes, interfaceTypes) = partition(::isNonInterfaceType)
return classTypes.sortedBy(IrType::render) + interfaceTypes.sortedBy(IrType::render)
}
override fun visitElement(element: IrElement, data: IrDeclaration?) { override fun visitElement(element: IrElement, data: IrDeclaration?) {
val e = "/* ERROR: unsupported element type: " + element.javaClass.simpleName + " */" val e = "/* ERROR: unsupported element type: " + element.javaClass.simpleName + " */"
if (element is IrExpression) { if (element is IrExpression) {
@@ -195,7 +212,7 @@ private class KotlinLikeDumper(val p: Printer, val options: KotlinLikeDumpOption
} }
if (!p.isEmpty) p.printlnWithNoIndent() if (!p.isEmpty) p.printlnWithNoIndent()
declaration.declarations.forEach { it.accept(this, null) } declaration.declarations.ordered().forEach { it.accept(this, null) }
if (options.printRegionsPerFile) p.println("//endregion") if (options.printRegionsPerFile) p.println("//endregion")
} }
@@ -240,7 +257,7 @@ private class KotlinLikeDumper(val p: Printer, val options: KotlinLikeDumpOption
// TODO no test // TODO no test
if (declaration.superTypes.isNotEmpty()) { if (declaration.superTypes.isNotEmpty()) {
var first = true var first = true
for (type in declaration.superTypes) { for (type in declaration.superTypes.ordered()) {
if (type.isAny()) continue if (type.isAny()) continue
if (!first) { if (!first) {
@@ -260,7 +277,7 @@ private class KotlinLikeDumper(val p: Printer, val options: KotlinLikeDumpOption
p.printlnWithNoIndent(" {") p.printlnWithNoIndent(" {")
p.pushIndent() p.pushIndent()
declaration.declarations.forEach { it.accept(this, declaration) } declaration.declarations.ordered().forEach { it.accept(this, declaration) }
p.popIndent() p.popIndent()
p.println("}") p.println("}")
@@ -431,7 +448,7 @@ private class KotlinLikeDumper(val p: Printer, val options: KotlinLikeDumpOption
private fun IrTypeParameter.printWhereClauseTypesWithNoIndent(first: Boolean): Boolean { private fun IrTypeParameter.printWhereClauseTypesWithNoIndent(first: Boolean): Boolean {
var myFirst = first var myFirst = first
superTypes.forEach { type -> superTypes.ordered().forEach { type ->
if (!myFirst) { if (!myFirst) {
p.printWithNoIndent(", ") p.printWithNoIndent(", ")
} else { } else {