diff --git a/.idea/runConfigurations/Generate_Compiler_Tests.xml b/.idea/runConfigurations/Generate_Compiler_Tests.xml
index 23c55d3aa91..515b76fb6a0 100644
--- a/.idea/runConfigurations/Generate_Compiler_Tests.xml
+++ b/.idea/runConfigurations/Generate_Compiler_Tests.xml
@@ -13,10 +13,11 @@
+
-
+
diff --git a/build.gradle.kts b/build.gradle.kts
index 3fdbb3d8983..2a7d158da1e 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -195,6 +195,7 @@ val fe10CompilerModules = arrayOf(
":kotlin-util-io",
":kotlin-util-klib",
":kotlin-util-klib-metadata",
+ ":kotlin-util-klib-abi",
":compiler:backend-common",
":compiler:backend",
":compiler:plugin-api",
@@ -745,6 +746,7 @@ tasks {
dependsOn(":core:descriptors.runtime:test")
dependsOn(":kotlin-util-io:test")
dependsOn(":kotlin-util-klib:test")
+ dependsOn(":kotlin-util-klib-abi:test")
dependsOn(":generators:test")
}
diff --git a/compiler/util-klib-abi/ReadMe.md b/compiler/util-klib-abi/ReadMe.md
new file mode 100644
index 00000000000..5285ba6db42
--- /dev/null
+++ b/compiler/util-klib-abi/ReadMe.md
@@ -0,0 +1,7 @@
+## API for extracting publicly visible ABI from KLIBs
+
+This is the API to extract and dump declarations from KLIBs that comprise publicly visible part of KLIB ABI. Can be used for implementing various KLIB-oriented build tools that do ABI compatibility validation, perform compilation avoidance with KLIBs, etc.
+
+There are two major entry points:
+* [LibraryAbiReader](src/org/jetbrains/kotlin/library/abi/LibraryAbiReader.kt) - extracts publicly visible ABI
+* [LibraryAbiRenderer](src/org/jetbrains/kotlin/library/abi/LibraryAbiRenderer.kt) - renders it to a human-readable textual representation
diff --git a/compiler/util-klib-abi/build.gradle.kts b/compiler/util-klib-abi/build.gradle.kts
new file mode 100644
index 00000000000..86a85e10253
--- /dev/null
+++ b/compiler/util-klib-abi/build.gradle.kts
@@ -0,0 +1,38 @@
+plugins {
+ kotlin("jvm")
+ id("jps-compatible")
+}
+
+dependencies {
+ api(kotlinStdlib())
+ implementation(project(":kotlin-util-klib"))
+ implementation(project(":core:compiler.common"))
+ implementation(project(":compiler:ir.serialization.common"))
+ compileOnly(commonDependency("org.jetbrains.intellij.deps.fastutil:intellij-deps-fastutil"))
+ testApiJUnit5()
+ testImplementation(intellijCore())
+ testImplementation(commonDependency("org.jetbrains.kotlin:kotlin-reflect")) { isTransitive = false }
+ testImplementation(projectTests(":compiler:tests-common-new"))
+ testImplementation(projectTests(":generators:test-generator"))
+}
+
+sourceSets {
+ "main" { projectDefault() }
+ "test" {
+ projectDefault()
+ generatedTestDir()
+ }
+}
+
+val testDataDir = projectDir.resolve("testData")
+
+projectTest(jUnitMode = JUnitMode.JUnit5) {
+ inputs.dir(testDataDir)
+ outputs.dir("$buildDir/t")
+
+ dependsOn(":dist")
+ workingDir = rootDir
+ useJUnitPlatform()
+}
+
+val generateTests by generator("org.jetbrains.kotlin.library.abi.GenerateLibraryAbiReaderTestsKt")
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/ExperimentalLibraryAbiReader.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/ExperimentalLibraryAbiReader.kt
new file mode 100644
index 00000000000..facc19e34d2
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/ExperimentalLibraryAbiReader.kt
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+/**
+ * This marker distinguishes the experimental Kotlin library ABI reader API and is used to opt-in.
+ *
+ * Any usage of a declaration annotated with `@ExperimentalLibraryAbiReader` must be accepted either by
+ * annotating that usage with the [OptIn] annotation, e.g. `@OptIn(ExperimentalLibraryAbiReader::class)`,
+ * or by using the compiler argument `-opt-in=org.jetbrains.kotlin.library.abi.ExperimentalLibraryAbiReader`.
+ */
+@RequiresOptIn
+annotation class ExperimentalLibraryAbiReader
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbi.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbi.kt
new file mode 100644
index 00000000000..495b7f97cab
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbi.kt
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+/**
+ * The result of reading ABI from KLIB.
+ *
+ * @property manifest Information from the manifest that may be useful.
+ * @property uniqueName The library's unique name that is a part of the library ABI.
+ * Corresponds to the `unique_name` manifest property.
+ * @property signatureVersions The versions of signatures supported by the KLIB. Note that not every [AbiSignatureVersion]
+ * which is supported by the KLIB is also supported by the ABI reader. To check this please use
+ * [AbiSignatureVersion.isSupportedByAbiReader]. An attempt to obtain a signature of unsupported version will result
+ * in an exception. See also [AbiSignatures.get].
+ * @property topLevelDeclarations The list of top-level declarations.
+ */
+@ExperimentalLibraryAbiReader
+class LibraryAbi(
+ val manifest: LibraryManifest,
+ val uniqueName: String,
+ val signatureVersions: Set,
+ val topLevelDeclarations: AbiTopLevelDeclarations
+)
+
+/**
+ * The representation of the version of IR signatures supported by a KLIB.
+ *
+ * @property versionNumber The unique version number of the IR signature.
+ * @property isSupportedByAbiReader Whether this IR signature version is supported by the current implementation of
+ * the ABI reader. If it's not supported then such signatures can't be read by the ABI reader, and the version itself
+ * just serves for information purposes.
+ * @property description Brief description of the IR signature version, if available. To be used purely for discovery
+ * purposes by ABI reader clients. Warning: The description text may be freely changed in the future. So, it should
+ * NEVER be used for making ABI snapshots.
+ */
+@ExperimentalLibraryAbiReader
+interface AbiSignatureVersion {
+ val versionNumber: Int
+ val isSupportedByAbiReader: Boolean
+ val description: String?
+}
+
+@ExperimentalLibraryAbiReader
+interface AbiSignatures {
+ /**
+ * Returns the signature of the specified [AbiSignatureVersion].
+ *
+ * - If the signature version is not supported by the ABI reader (according to [AbiSignatureVersion.isSupportedByAbiReader])
+ * then throw an exception.
+ * - If the signature version is supported by the ABI reader, but the signature is unavailable for some other reason
+ * (e.g. a particular type of declaration misses a signature of a particular version), then return `null`.
+ **/
+ operator fun get(signatureVersion: AbiSignatureVersion): String?
+}
+
+/**
+ * Simple name.
+ * Examples: "TopLevelClass", "topLevelFun", "List", "EMPTY".
+ */
+@ExperimentalLibraryAbiReader
+@JvmInline
+value class AbiSimpleName(val value: String) : Comparable {
+ init {
+ require(AbiCompoundName.SEPARATOR !in value && AbiQualifiedName.SEPARATOR !in value) {
+ "Simple name contains illegal characters: $value"
+ }
+ }
+
+ override fun compareTo(other: AbiSimpleName) = value.compareTo(other.value)
+ override fun toString() = value
+}
+
+/**
+ * Compound name. An equivalent of one or more [AbiSimpleName]s which are concatenated with dots.
+ * Examples: "TopLevelClass", "topLevelFun", "List", "CharRange.Companion.EMPTY".
+ */
+@ExperimentalLibraryAbiReader
+@JvmInline
+value class AbiCompoundName(val value: String) : Comparable {
+ init {
+ require(AbiQualifiedName.SEPARATOR !in value) { "Compound name contains illegal characters: $value" }
+ }
+
+ val nameSegments: List get() = value.split(SEPARATOR).map(::AbiSimpleName)
+ val nameSegmentsCount: Int get() = value.count { it == SEPARATOR } + 1
+
+ val simpleName: AbiSimpleName get() = AbiSimpleName(value.substringAfterLast(SEPARATOR))
+
+ override fun compareTo(other: AbiCompoundName) = value.compareTo(other.value)
+ override fun toString() = value
+
+ infix fun isContainerOf(member: AbiCompoundName): Boolean {
+ val containerName = value
+ return when (val containerNameLength = containerName.length) {
+ 0 -> true
+ else -> {
+ val memberName = member.value
+ val memberNameLength = memberName.length
+ memberNameLength > containerNameLength + 1 &&
+ memberName.startsWith(containerName) &&
+ memberName[containerNameLength] == SEPARATOR
+ }
+ }
+ }
+
+ companion object {
+ const val SEPARATOR = '.'
+ }
+}
+
+/**
+ * Fully qualified name.
+ * Examples: "/TopLevelClass", "/topLevelFun", "kotlin.collections/List", "kotlin.ranges/CharRange.Companion.EMPTY".
+ */
+@ExperimentalLibraryAbiReader
+data class AbiQualifiedName(val packageName: AbiCompoundName, val relativeName: AbiCompoundName) : Comparable {
+ init {
+ require(relativeName.value.isNotEmpty()) { "Empty relative name" }
+ }
+
+ override fun compareTo(other: AbiQualifiedName): Int {
+ val diff = packageName.compareTo(other.packageName)
+ return if (diff != 0) diff else relativeName.compareTo(other.relativeName)
+ }
+
+ override fun toString() = "$packageName$SEPARATOR$relativeName"
+
+ companion object {
+ const val SEPARATOR = '/'
+ }
+}
+
+@ExperimentalLibraryAbiReader
+sealed interface AbiDeclaration {
+ val qualifiedName: AbiQualifiedName
+ val signatures: AbiSignatures
+
+ /**
+ * Annotations are not a part of ABI. But sometimes it is useful to have the ability to check if some declaration
+ * has a specific annotation. See [AbiReadingFilter.NonPublicMarkerAnnotations] as an example.
+ */
+ fun hasAnnotation(annotationClassName: AbiQualifiedName): Boolean
+}
+
+@ExperimentalLibraryAbiReader
+sealed interface AbiDeclarationWithModality : AbiDeclaration {
+ val modality: AbiModality
+}
+
+@ExperimentalLibraryAbiReader
+enum class AbiModality {
+ FINAL, OPEN, ABSTRACT, SEALED
+}
+
+@ExperimentalLibraryAbiReader
+interface AbiDeclarationContainer {
+ /** Important: The order of declarations is preserved exactly as in serialized IR. */
+ val declarations: List
+}
+
+@ExperimentalLibraryAbiReader
+interface AbiTopLevelDeclarations : AbiDeclarationContainer
+
+@ExperimentalLibraryAbiReader
+interface AbiClass : AbiDeclarationWithModality, AbiDeclarationContainer, AbiTypeParametersContainer {
+ val kind: AbiClassKind
+ val isInner: Boolean
+ val isValue: Boolean
+ val isFunction: Boolean
+
+ /**
+ * The set of non-trivial supertypes (i.e. excluding [kotlin.Any]).
+ * Important: The order of supertypes is preserved exactly as in serialized IR.
+ */
+ val superTypes: List
+}
+
+@ExperimentalLibraryAbiReader
+enum class AbiClassKind {
+ CLASS, INTERFACE, OBJECT, ENUM_CLASS, ANNOTATION_CLASS
+}
+
+@ExperimentalLibraryAbiReader
+interface AbiEnumEntry : AbiDeclaration
+
+@ExperimentalLibraryAbiReader
+interface AbiFunction : AbiDeclarationWithModality, AbiTypeParametersContainer {
+ val isConstructor: Boolean
+ val isInline: Boolean
+ val isSuspend: Boolean
+ val hasExtensionReceiverParameter: Boolean
+ val contextReceiverParametersCount: Int
+
+ /**
+ * Important: All value parameters of the function are stored in the single place, in the [valueParameters] list in
+ * a well-defined order. First, unless [hasExtensionReceiverParameter] is false, goes the extension receiver parameter.
+ * It is followed by [contextReceiverParametersCount] context receiver parameters. The remainder are the regular
+ * value parameters of the function.
+ */
+ val valueParameters: List
+ val returnType: AbiType?
+}
+
+@ExperimentalLibraryAbiReader
+interface AbiValueParameter {
+ val type: AbiType
+ val isVararg: Boolean
+ val hasDefaultArg: Boolean
+ val isNoinline: Boolean
+ val isCrossinline: Boolean
+}
+
+@ExperimentalLibraryAbiReader
+interface AbiProperty : AbiDeclarationWithModality {
+ val kind: AbiPropertyKind
+ val getter: AbiFunction?
+ val setter: AbiFunction?
+}
+
+@ExperimentalLibraryAbiReader
+enum class AbiPropertyKind { VAL, CONST_VAL, VAR }
+
+@ExperimentalLibraryAbiReader
+sealed interface AbiTypeParametersContainer : AbiDeclaration {
+ /** Important: The order of [typeParameters] is preserved exactly as in serialized IR. */
+ val typeParameters: List
+}
+
+@ExperimentalLibraryAbiReader
+interface AbiTypeParameter {
+ val tag: String
+ val variance: AbiVariance
+ val isReified: Boolean
+
+ /**
+ * The set of non-trivial upper bounds (i.e. excluding nullable [kotlin.Any]).
+ * Important: The order of upper bounds is preserved exactly as in serialized IR.
+ */
+ val upperBounds: List
+}
+
+@ExperimentalLibraryAbiReader
+sealed interface AbiType {
+ interface Dynamic : AbiType
+ interface Error : AbiType
+ interface Simple : AbiType {
+ val classifierReference: AbiClassifierReference
+ val arguments: List
+ val nullability: AbiTypeNullability
+ }
+}
+
+@ExperimentalLibraryAbiReader
+sealed interface AbiTypeArgument {
+ interface StarProjection : AbiTypeArgument
+ interface TypeProjection : AbiTypeArgument {
+ val type: AbiType
+ val variance: AbiVariance
+ }
+}
+
+@ExperimentalLibraryAbiReader
+sealed interface AbiClassifierReference {
+ interface ClassReference : AbiClassifierReference {
+ val className: AbiQualifiedName
+ }
+
+ interface TypeParameterReference : AbiClassifierReference {
+ val tag: String
+ }
+}
+
+@ExperimentalLibraryAbiReader
+enum class AbiTypeNullability {
+ MARKED_NULLABLE, NOT_SPECIFIED, DEFINITELY_NOT_NULL
+}
+
+@ExperimentalLibraryAbiReader
+enum class AbiVariance {
+ INVARIANT, IN, OUT
+}
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbiReader.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbiReader.kt
new file mode 100644
index 00000000000..a5d45738131
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbiReader.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.library.abi.impl.LibraryAbiReaderImpl
+import java.io.File
+
+/** The default implementation of [LibraryAbi] reader. */
+@ExperimentalLibraryAbiReader
+object LibraryAbiReader {
+ /** Inspect the KLIB at [library]. The KLIB can be either in a directory (unzipped) or in a file (zipped) form. */
+ fun readAbiInfo(library: File, vararg filters: AbiReadingFilter): LibraryAbi = readAbiInfo(library, filters.asList())
+ fun readAbiInfo(library: File, filters: List): LibraryAbi = LibraryAbiReaderImpl(library, filters).readAbi()
+}
+
+/**
+ * Defines a filter for skipping certain [AbiDeclaration]s during reading the KLIB ABI.
+ *
+ * There are several ready-to-use implementations: [ExcludedPackages], [ExcludedClasses] and [NonPublicMarkerAnnotations].
+ * But also it's possible to implement your own filter if necessary.
+ */
+@ExperimentalLibraryAbiReader
+interface AbiReadingFilter {
+ fun isPackageExcluded(packageName: AbiCompoundName): Boolean = false
+ fun isDeclarationExcluded(declaration: AbiDeclaration): Boolean = false
+
+ class ExcludedPackages(excludedPackageNames: Collection) : AbiReadingFilter {
+ private val excludedPackageNames = excludedPackageNames.toSet()
+
+ override fun isPackageExcluded(packageName: AbiCompoundName) = when {
+ excludedPackageNames.isEmpty() -> false
+ packageName in excludedPackageNames -> true
+ else -> excludedPackageNames.any { excludedPackageName -> excludedPackageName isContainerOf packageName }
+ }
+ }
+
+ class ExcludedClasses(excludedClassNames: Collection) : AbiReadingFilter {
+ private val excludedClassNames = excludedClassNames.toSet()
+
+ override fun isDeclarationExcluded(declaration: AbiDeclaration) =
+ declaration is AbiClass && declaration.qualifiedName in excludedClassNames
+ }
+
+ class NonPublicMarkerAnnotations(nonPublicMarkerNames: Collection) : AbiReadingFilter {
+ private val nonPublicMarkerNames = nonPublicMarkerNames.toSet().toTypedArray()
+
+ override fun isDeclarationExcluded(declaration: AbiDeclaration): Boolean {
+ for (nonPublicMarkerName in nonPublicMarkerNames) {
+ if (declaration.hasAnnotation(nonPublicMarkerName)) return true
+ }
+ return false
+ }
+ }
+
+ class Composite(filters: List) : AbiReadingFilter {
+ private val filters = filters.toTypedArray()
+
+ override fun isPackageExcluded(packageName: AbiCompoundName): Boolean {
+ for (filter in filters) {
+ if (filter.isPackageExcluded(packageName)) return true
+ }
+ return false
+ }
+
+ override fun isDeclarationExcluded(declaration: AbiDeclaration): Boolean {
+ for (filter in filters) {
+ if (filter.isDeclarationExcluded(declaration)) return true
+ }
+ return false
+ }
+ }
+}
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbiRenderer.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbiRenderer.kt
new file mode 100644
index 00000000000..fdccf0c5814
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryAbiRenderer.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.library.abi.impl.AbiRendererImpl
+
+/** The default rendering implementation. */
+@ExperimentalLibraryAbiReader
+object LibraryAbiRenderer {
+ fun render(libraryAbi: LibraryAbi, settings: AbiRenderingSettings): String =
+ buildString { render(libraryAbi, this, settings) }
+
+ fun render(libraryAbi: LibraryAbi, output: Appendable, settings: AbiRenderingSettings): Unit =
+ AbiRendererImpl(libraryAbi, settings, output).render()
+}
+
+/**
+ * @param renderedSignatureVersion The IR signature version to render. This should be a version among the versions
+ * listed in [LibraryAbi.signatureVersions].
+ * @param renderManifest Whether KLIB manifest properties should be rendered.
+ * @param renderDeclarations Whether declarations should be rendered.
+ * @param indentationString The string used for indentation of nested declarations.
+ * @param whenSignatureNotFound A handler that is executed when a signature is not found for a specific declaration.
+ */
+@ExperimentalLibraryAbiReader
+class AbiRenderingSettings(
+ val renderedSignatureVersion: AbiSignatureVersion,
+ val renderManifest: Boolean = false,
+ val renderDeclarations: Boolean = true,
+ val indentationString: String = " ",
+ val whenSignatureNotFound: (AbiDeclaration, AbiSignatureVersion) -> String = { declaration, signatureVersion ->
+ error("No signature $signatureVersion for ${declaration::class.java}, $declaration")
+ }
+)
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryManifest.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryManifest.kt
new file mode 100644
index 00000000000..bfb80f24af0
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/LibraryManifest.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.library.*
+
+/**
+ * Anything that can be retrieved from manifest and that might be helpful to know about the inspected KLIB.
+ *
+ * @property platform [KLIB_PROPERTY_BUILTINS_PLATFORM]
+ * @property nativeTargets [KLIB_PROPERTY_NATIVE_TARGETS]
+ * @property compilerVersion [KLIB_PROPERTY_COMPILER_VERSION]
+ * @property abiVersion [KLIB_PROPERTY_ABI_VERSION]
+ * @property libraryVersion [KLIB_PROPERTY_LIBRARY_VERSION]
+ * @property irProviderName [KLIB_PROPERTY_IR_PROVIDER]
+ */
+@ExperimentalLibraryAbiReader
+data class LibraryManifest(
+ val platform: String?,
+ val nativeTargets: List,
+ val compilerVersion: String?,
+ val abiVersion: String?,
+ val libraryVersion: String?,
+ val irProviderName: String?
+)
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/FlagFieldEx.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/FlagFieldEx.kt
new file mode 100644
index 00000000000..4fc46c7b058
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/FlagFieldEx.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi.impl
+
+import org.jetbrains.kotlin.metadata.deserialization.Flags.FlagField
+
+/** TODO: Think how to contribute it to [FlagField]. */
+internal abstract class FlagFieldEx(val offset: Int, val bitWidth: Int) {
+ init {
+ require(offset >= 0) { "Invalid offset: $offset" }
+ require(bitWidth > 0) { "Invalid bit width: $bitWidth" }
+
+ val overflow = offset + bitWidth - Int.SIZE_BITS
+ require(overflow <= 0) { "Not enough bit space for storage. Offset=$offset, width=$bitWidth, overflow=$overflow." }
+ }
+
+ abstract fun get(flags: Int): T
+ abstract fun toFlags(value: T): Int
+
+ private val bitMask = (1 shl bitWidth) - 1
+
+ protected fun readSignificantBitsFromFlags(flags: Int): Int = (flags ushr offset) and bitMask
+
+ protected fun storeSignificantBitsAsFlags(value: Int): Int {
+ require((value ushr bitWidth) == 0) { "Not enough space to store $value" }
+ return value shl offset
+ }
+
+ class IntFlagFieldEx(offset: Int, bitWidth: Int) : FlagFieldEx(offset, bitWidth) {
+ override fun get(flags: Int): Int = readSignificantBitsFromFlags(flags)
+ override fun toFlags(value: Int): Int = storeSignificantBitsAsFlags(value)
+ }
+
+ class EnumFlagFieldEx>(offset: Int, private val entries: Array) : FlagFieldEx(offset, computeBitWidth(entries)) {
+ override fun get(flags: Int): E = entries[readSignificantBitsFromFlags(flags)]
+ override fun toFlags(value: E): Int = storeSignificantBitsAsFlags(value.ordinal)
+
+ companion object {
+ private fun > computeBitWidth(entries: Array): Int {
+ require(entries.isNotEmpty()) { "No enum entries" }
+ return maxOf(1, Int.SIZE_BITS - entries.lastIndex.countLeadingZeroBits())
+ }
+ }
+ }
+
+ companion object {
+ fun intFirst(bitWidth: Int): FlagFieldEx = IntFlagFieldEx(0, bitWidth)
+ fun intAfter(previous: FlagField<*>, bitWidth: Int): FlagFieldEx = IntFlagFieldEx(previous.nextOffset, bitWidth)
+ fun intAfter(previous: FlagFieldEx<*>, bitWidth: Int): FlagFieldEx = IntFlagFieldEx(previous.nextOffset, bitWidth)
+
+ inline fun > first(): FlagFieldEx = EnumFlagFieldEx(0, enumValues())
+ inline fun > after(previous: FlagField<*>): FlagFieldEx = EnumFlagFieldEx(previous.nextOffset, enumValues())
+ inline fun > after(previous: FlagFieldEx<*>): FlagFieldEx = EnumFlagFieldEx(previous.nextOffset, enumValues())
+
+ private val FlagField<*>.nextOffset: Int get() = offset + bitWidth
+ private val FlagFieldEx<*>.nextOffset: Int get() = offset + bitWidth
+ }
+}
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiImpl.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiImpl.kt
new file mode 100644
index 00000000000..0810da21068
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiImpl.kt
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi.impl
+
+import org.jetbrains.kotlin.library.abi.*
+import org.jetbrains.kotlin.library.abi.impl.AbiFunctionImpl.Companion.BITS_ENOUGH_FOR_STORING_PARAMETERS_COUNT
+import org.jetbrains.kotlin.metadata.deserialization.Flags.FlagField
+
+@ExperimentalLibraryAbiReader
+internal object AbiSignatureVersions {
+ enum class Supported(override val versionNumber: Int, override val description: String) : AbiSignatureVersion {
+ V1(1, "The signatures with hashes"),
+ V2(2, "The self-descriptive signatures (with mangled names)");
+
+ override val isSupportedByAbiReader get() = true
+ }
+
+ private data class Unsupported(override val versionNumber: Int) : AbiSignatureVersion {
+ override val isSupportedByAbiReader get() = false
+ override val description: String? get() = null
+ }
+
+ fun resolveByVersionNumber(versionNumber: Int): AbiSignatureVersion =
+ Supported.entries.firstOrNull { it.versionNumber == versionNumber } ?: Unsupported(versionNumber)
+}
+
+@ExperimentalLibraryAbiReader
+internal data class AbiSignaturesImpl(private val signatureV1: String?, private val signatureV2: String?) : AbiSignatures {
+ override operator fun get(signatureVersion: AbiSignatureVersion): String? = when (signatureVersion) {
+ is AbiSignatureVersions.Supported -> when (signatureVersion) {
+ AbiSignatureVersions.Supported.V1 -> signatureV1
+ AbiSignatureVersions.Supported.V2 -> signatureV2
+ }
+ else -> error("Unsupported signature version: $signatureVersion")
+ }
+}
+
+@ExperimentalLibraryAbiReader
+internal class AbiTopLevelDeclarationsImpl(
+ override val declarations: List
+) : AbiTopLevelDeclarations
+
+@ExperimentalLibraryAbiReader
+internal class AbiClassImpl(
+ override val qualifiedName: AbiQualifiedName,
+ override val signatures: AbiSignatures,
+ private val annotations: Set,
+ modality: AbiModality,
+ kind: AbiClassKind,
+ isInner: Boolean,
+ isValue: Boolean,
+ isFunction: Boolean,
+ override val superTypes: List,
+ override val declarations: List,
+ override val typeParameters: List
+) : AbiClass {
+ private val flags = IS_INNER.toFlags(isInner) or
+ IS_VALUE.toFlags(isValue) or
+ IS_FUNCTION.toFlags(isFunction) or
+ MODALITY.toFlags(modality) or
+ CLASS_KIND.toFlags(kind)
+
+ override val modality get() = MODALITY.get(flags)
+ override val kind get() = CLASS_KIND.get(flags)
+ override val isInner get() = IS_INNER.get(flags)
+ override val isValue get() = IS_VALUE.get(flags)
+ override val isFunction get() = IS_FUNCTION.get(flags)
+ override fun hasAnnotation(annotationClassName: AbiQualifiedName) = annotationClassName in annotations
+
+ companion object {
+ private val IS_INNER = FlagField.booleanFirst()
+ private val IS_VALUE = FlagField.booleanAfter(IS_INNER)
+ private val IS_FUNCTION = FlagField.booleanAfter(IS_VALUE)
+ private val MODALITY = FlagFieldEx.after(IS_FUNCTION)
+ private val CLASS_KIND = FlagFieldEx.after(MODALITY)
+ }
+}
+
+@ExperimentalLibraryAbiReader
+internal class AbiEnumEntryImpl(
+ override val qualifiedName: AbiQualifiedName,
+ override val signatures: AbiSignatures,
+ private val annotations: Set
+) : AbiEnumEntry {
+ override fun hasAnnotation(annotationClassName: AbiQualifiedName) = annotationClassName in annotations
+}
+
+@ExperimentalLibraryAbiReader
+internal class AbiConstructorImpl(
+ override val qualifiedName: AbiQualifiedName,
+ override val signatures: AbiSignatures,
+ private val annotations: Set,
+ isInline: Boolean,
+ contextReceiverParametersCount: Int,
+ override val valueParameters: List
+) : AbiFunction {
+ private val flags = IS_INLINE.toFlags(isInline) or
+ CONTEXT_RECEIVERS_COUNT.toFlags(contextReceiverParametersCount)
+
+ override val modality get() = AbiModality.FINAL // No need to render modality for constructors.
+ override val isConstructor get() = true
+ override val isInline get() = IS_INLINE.get(flags)
+ override val isSuspend get() = false
+ override val hasExtensionReceiverParameter get() = false
+ override val contextReceiverParametersCount get() = CONTEXT_RECEIVERS_COUNT.get(flags)
+ override val returnType get() = null // No need to render return type for constructors.
+ override val typeParameters get() = emptyList()
+ override fun hasAnnotation(annotationClassName: AbiQualifiedName) = annotationClassName in annotations
+
+ companion object {
+ private val IS_INLINE = FlagField.booleanFirst()
+ private val CONTEXT_RECEIVERS_COUNT = FlagFieldEx.intAfter(IS_INLINE, BITS_ENOUGH_FOR_STORING_PARAMETERS_COUNT)
+ }
+}
+
+@ExperimentalLibraryAbiReader
+internal class AbiFunctionImpl(
+ override val qualifiedName: AbiQualifiedName,
+ override val signatures: AbiSignatures,
+ private val annotations: Set,
+ modality: AbiModality,
+ isInline: Boolean,
+ isSuspend: Boolean,
+ override val typeParameters: List,
+ hasExtensionReceiverParameter: Boolean,
+ contextReceiverParametersCount: Int,
+ override val valueParameters: List,
+ override val returnType: AbiType?
+) : AbiFunction {
+ private val flags = IS_INLINE.toFlags(isInline) or
+ IS_SUSPEND.toFlags(isSuspend) or
+ HAS_EXTENSION_RECEIVER.toFlags(hasExtensionReceiverParameter) or
+ CONTEXT_RECEIVERS_COUNT.toFlags(contextReceiverParametersCount) or
+ MODALITY.toFlags(modality)
+
+ override val modality get() = MODALITY.get(flags)
+ override val isConstructor get() = false
+ override val isInline get() = IS_INLINE.get(flags)
+ override val isSuspend get() = IS_SUSPEND.get(flags)
+ override val hasExtensionReceiverParameter get() = HAS_EXTENSION_RECEIVER.get(flags)
+ override val contextReceiverParametersCount get() = CONTEXT_RECEIVERS_COUNT.get(flags)
+ override fun hasAnnotation(annotationClassName: AbiQualifiedName) = annotationClassName in annotations
+
+ companion object {
+ /** JVM allows max 255 parameters for a function. Storing such number requires just 8 bits. */
+ const val BITS_ENOUGH_FOR_STORING_PARAMETERS_COUNT = 8
+
+ private val IS_INLINE = FlagField.booleanFirst()
+ private val IS_SUSPEND = FlagField.booleanAfter(IS_INLINE)
+ private val HAS_EXTENSION_RECEIVER = FlagField.booleanAfter(IS_SUSPEND)
+ private val CONTEXT_RECEIVERS_COUNT = FlagFieldEx.intAfter(HAS_EXTENSION_RECEIVER, BITS_ENOUGH_FOR_STORING_PARAMETERS_COUNT)
+ private val MODALITY = FlagFieldEx.after(CONTEXT_RECEIVERS_COUNT)
+ }
+}
+
+@ExperimentalLibraryAbiReader
+internal class AbiValueParameterImpl(
+ override val type: AbiType,
+ isVararg: Boolean,
+ hasDefaultArg: Boolean,
+ isNoinline: Boolean,
+ isCrossinline: Boolean
+) : AbiValueParameter {
+ private val flags = IS_VARARG.toFlags(isVararg) or
+ HAS_DEFAULT_ARG.toFlags(hasDefaultArg) or
+ IS_NOINLINE.toFlags(isNoinline) or
+ IS_CROSSINLINE.toFlags(isCrossinline)
+
+ override val isVararg get() = IS_VARARG.get(flags)
+ override val hasDefaultArg get() = HAS_DEFAULT_ARG.get(flags)
+ override val isNoinline get() = IS_NOINLINE.get(flags)
+ override val isCrossinline get() = IS_CROSSINLINE.get(flags)
+
+ companion object {
+ private val IS_VARARG = FlagField.booleanFirst()
+ private val HAS_DEFAULT_ARG = FlagField.booleanAfter(IS_VARARG)
+ private val IS_NOINLINE = FlagField.booleanAfter(HAS_DEFAULT_ARG)
+ private val IS_CROSSINLINE = FlagField.booleanAfter(IS_NOINLINE)
+ }
+}
+
+@ExperimentalLibraryAbiReader
+internal class AbiPropertyImpl(
+ override val qualifiedName: AbiQualifiedName,
+ override val signatures: AbiSignatures,
+ private val annotations: Set,
+ modality: AbiModality,
+ kind: AbiPropertyKind,
+ override val getter: AbiFunction?,
+ override val setter: AbiFunction?
+) : AbiProperty {
+ private val flags = MODALITY.toFlags(modality) or PROPERTY_KIND.toFlags(kind)
+
+ override val modality get() = MODALITY.get(flags)
+ override val kind get() = PROPERTY_KIND.get(flags)
+ override fun hasAnnotation(annotationClassName: AbiQualifiedName) = annotationClassName in annotations
+
+ companion object {
+ private val MODALITY = FlagFieldEx.first()
+ private val PROPERTY_KIND = FlagFieldEx.after(MODALITY)
+ }
+}
+
+@ExperimentalLibraryAbiReader
+internal class AbiTypeParameterImpl(
+ override val tag: String,
+ variance: AbiVariance,
+ isReified: Boolean,
+ override val upperBounds: List
+) : AbiTypeParameter {
+ private val flags = IS_REIFIED.toFlags(isReified) or VARIANCE.toFlags(variance)
+
+ override val variance = VARIANCE.get(flags)
+ override val isReified = IS_REIFIED.get(flags)
+
+ companion object {
+ private val IS_REIFIED = FlagField.booleanFirst()
+ private val VARIANCE = FlagFieldEx.after(IS_REIFIED)
+ }
+}
+
+@ExperimentalLibraryAbiReader
+internal object DynamicTypeImpl : AbiType.Dynamic
+
+@ExperimentalLibraryAbiReader
+internal object ErrorTypeImpl : AbiType.Error
+
+@ExperimentalLibraryAbiReader
+internal class SimpleTypeImpl(
+ override val classifierReference: AbiClassifierReference,
+ override val arguments: List,
+ override val nullability: AbiTypeNullability
+) : AbiType.Simple
+
+@ExperimentalLibraryAbiReader
+internal object StarProjectionImpl : AbiTypeArgument.StarProjection
+
+@ExperimentalLibraryAbiReader
+internal class TypeProjectionImpl(
+ override val type: AbiType,
+ override val variance: AbiVariance
+) : AbiTypeArgument.TypeProjection
+
+@ExperimentalLibraryAbiReader
+internal class ClassReferenceImpl(override val className: AbiQualifiedName) : AbiClassifierReference.ClassReference
+
+@ExperimentalLibraryAbiReader
+internal class TypeParameterReferenceImpl(override val tag: String) : AbiClassifierReference.TypeParameterReference
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiReaderImpl.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiReaderImpl.kt
new file mode 100644
index 00000000000..d4a6236a631
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiReaderImpl.kt
@@ -0,0 +1,728 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi.impl
+
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
+import org.jetbrains.kotlin.backend.common.serialization.*
+import org.jetbrains.kotlin.backend.common.serialization.encodings.*
+import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind.CLASS_SYMBOL
+import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind.TYPE_PARAMETER_SYMBOL
+import org.jetbrains.kotlin.descriptors.ClassKind
+import org.jetbrains.kotlin.descriptors.Modality
+import org.jetbrains.kotlin.ir.util.IdSignature
+import org.jetbrains.kotlin.ir.util.IdSignature.*
+import org.jetbrains.kotlin.ir.util.IdSignatureRenderer
+import org.jetbrains.kotlin.ir.util.render
+import org.jetbrains.kotlin.library.*
+import org.jetbrains.kotlin.library.abi.*
+import org.jetbrains.kotlin.library.abi.AbiClassifierReference.ClassReference
+import org.jetbrains.kotlin.library.abi.AbiTypeNullability.*
+import org.jetbrains.kotlin.metadata.ProtoBuf
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.name.SpecialNames
+import org.jetbrains.kotlin.types.Variance
+import org.jetbrains.kotlin.utils.*
+import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
+import java.io.File
+import org.jetbrains.kotlin.konan.file.File as KFile
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrClass as ProtoClass
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrDeclaration as ProtoDeclaration
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrDeclarationBase as ProtoDeclarationBase
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrEnumEntry as ProtoEnumEntry
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrFile as ProtoFile
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrFunctionBase as ProtoFunctionBase
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrProperty as ProtoProperty
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrValueParameter as ProtoValueParameter
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrConstructorCall as ProtoConstructorCall
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrType as ProtoType
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrDefinitelyNotNullType as ProtoIrDefinitelyNotNullType
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrSimpleType as ProtoSimpleType
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrSimpleTypeNullability as ProtoSimpleTypeNullability
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrSimpleTypeLegacy as ProtoSimpleTypeLegacy
+import org.jetbrains.kotlin.backend.common.serialization.proto.IrTypeParameter as ProtoTypeParameter
+import org.jetbrains.kotlin.backend.common.serialization.IrFlags as ProtoFlags
+
+@ExperimentalLibraryAbiReader
+internal class LibraryAbiReaderImpl(libraryFile: File, filters: List) {
+ private val library = resolveSingleFileKlib(
+ KFile(libraryFile.absolutePath),
+ strategy = ToolingSingleFileKlibResolveStrategy
+ )
+
+ private val compositeFilter: AbiReadingFilter.Composite? = if (filters.isNotEmpty()) AbiReadingFilter.Composite(filters) else null
+
+ fun readAbi(): LibraryAbi {
+ val supportedSignatureVersions = readSupportedSignatureVersions()
+
+ return LibraryAbi(
+ manifest = readManifest(),
+ uniqueName = library.uniqueName,
+ signatureVersions = supportedSignatureVersions,
+ topLevelDeclarations = LibraryDeserializer(library, supportedSignatureVersions, compositeFilter).deserialize()
+ )
+ }
+
+ private fun readManifest(): LibraryManifest {
+ val versions = library.versions
+ return LibraryManifest(
+ platform = library.builtInsPlatform,
+ nativeTargets = library.nativeTargets.sorted(),
+ compilerVersion = versions.compilerVersion,
+ abiVersion = versions.abiVersion?.toString(),
+ libraryVersion = versions.libraryVersion,
+ irProviderName = library.irProviderName
+ )
+ }
+
+ private fun readSupportedSignatureVersions(): Set {
+ return library.versions.irSignatureVersions.mapTo(hashSetOf()) { AbiSignatureVersions.resolveByVersionNumber(it.number) }
+ }
+}
+
+@ExperimentalLibraryAbiReader
+private class LibraryDeserializer(
+ private val library: KotlinLibrary,
+ supportedSignatureVersions: Set,
+ private val compositeFilter: AbiReadingFilter.Composite?
+) {
+ private val interner = IrInterningService()
+
+ private val annotationsInterner = object {
+ private val uniqueAnnotationClassNames = ObjectOpenHashSet()
+ fun intern(annotationClassName: AbiQualifiedName): AbiQualifiedName = uniqueAnnotationClassNames.addOrGet(annotationClassName)
+ }
+
+ private val needV1Signatures = AbiSignatureVersions.Supported.V1 in supportedSignatureVersions
+ private val needV2Signatures = AbiSignatureVersions.Supported.V2 in supportedSignatureVersions
+
+ private fun T?.discardIfExcluded(): T? =
+ if (this != null && compositeFilter?.isDeclarationExcluded(this) == true) null else this
+
+ private inner class FileDeserializer(fileIndex: Int) {
+ private val fileReader = IrLibraryFileFromBytes(IrKlibBytesSource(library, fileIndex))
+
+ private val packageName: AbiCompoundName
+ private val topLevelDeclarationIds: List
+ private val signatureDeserializer: IdSignatureDeserializer
+ private val typeDeserializer: TypeDeserializer
+
+ init {
+ val proto = ProtoFile.parseFrom(library.file(fileIndex).codedInputStream, IrLibraryFileFromBytes.extensionRegistryLite)
+ topLevelDeclarationIds = proto.declarationIdList
+
+ val packageFQN = fileReader.deserializeFqName(proto.fqNameList)
+ packageName = AbiCompoundName(packageFQN)
+
+ val fileSignature = FileSignature(
+ id = Any(), // Just an unique object.
+ fqName = FqName(packageFQN),
+ fileName = if (proto.hasFileEntry() && proto.fileEntry.hasName()) proto.fileEntry.name else ""
+ )
+ signatureDeserializer = IdSignatureDeserializer(fileReader, fileSignature, interner)
+ typeDeserializer = TypeDeserializer(fileReader, signatureDeserializer)
+ }
+
+ fun deserializeTo(output: MutableList) {
+ if (compositeFilter?.isPackageExcluded(packageName) == true)
+ return
+
+ topLevelDeclarationIds.mapNotNullTo(output) { topLevelDeclarationId ->
+ deserializeDeclaration(
+ proto = fileReader.declaration(topLevelDeclarationId),
+ containingEntity = ContainingEntity.Package(packageName),
+ parentTypeParameterResolver = null
+ )
+ }
+ }
+
+ private fun deserializeDeclaration(
+ proto: ProtoDeclaration,
+ containingEntity: ContainingEntity,
+ parentTypeParameterResolver: TypeParameterResolver?
+ ): AbiDeclaration? =
+ when (proto.declaratorCase) {
+ ProtoDeclaration.DeclaratorCase.IR_CLASS -> deserializeClass(proto.irClass, containingEntity, parentTypeParameterResolver)
+ ProtoDeclaration.DeclaratorCase.IR_CONSTRUCTOR -> deserializeFunction(
+ proto.irConstructor.base,
+ isConstructor = true,
+ containingEntity,
+ parentTypeParameterResolver
+ )
+ ProtoDeclaration.DeclaratorCase.IR_FUNCTION -> deserializeFunction(
+ proto.irFunction.base,
+ isConstructor = false,
+ containingEntity,
+ parentTypeParameterResolver
+ )
+ ProtoDeclaration.DeclaratorCase.IR_PROPERTY -> deserializeProperty(
+ proto.irProperty,
+ containingEntity,
+ parentTypeParameterResolver
+ )
+ ProtoDeclaration.DeclaratorCase.IR_ENUM_ENTRY -> deserializeEnumEntry(proto.irEnumEntry, containingEntity)
+ else -> null
+ }.discardIfExcluded()
+
+ private fun deserializeClass(
+ proto: ProtoClass,
+ containingEntity: ContainingEntity,
+ parentTypeParameterResolver: TypeParameterResolver?,
+ ): AbiClass? {
+ val annotations = deserializeAnnotations(proto.base)
+ val containingClassModality = (containingEntity as? ContainingEntity.Class)?.modality
+
+ if (!computeVisibilityStatus(proto.base, annotations, containingClassModality).isPubliclyVisible)
+ return null
+
+ val flags = ClassFlags.decode(proto.base.flags)
+
+ val modality = flags.modality.toAbiModality(
+ containingClassModality = null // Open nested classes in a final class remain open.
+ )
+
+ val qualifiedName = deserializeQualifiedName(proto.name, containingEntity)
+ val thisClassEntity = ContainingEntity.Class(qualifiedName, modality)
+
+ // Note: For inner classes pass the `parentTypeParameterResolver` to the constructor so that it could be
+ // possible to resolve TPs by delegating to the parent TP resolver. For non-inner classes just keep
+ // "level" to facilitate the proper TP numbering.
+ val thisClassTypeParameterResolver = TypeParameterResolver(
+ declarationName = qualifiedName,
+ parent = if (flags.isInner) parentTypeParameterResolver else null,
+ levelAdjustment = if (!flags.isInner && parentTypeParameterResolver != null) parentTypeParameterResolver.level + 1 else 0
+ )
+
+ val memberDeclarations = proto.declarationList.memoryOptimizedMapNotNull { declaration ->
+ deserializeDeclaration(declaration, containingEntity = thisClassEntity, thisClassTypeParameterResolver)
+ }
+
+ return AbiClassImpl(
+ qualifiedName = qualifiedName,
+ signatures = deserializeSignatures(proto.base),
+ annotations = annotations,
+ modality = modality,
+ kind = when (val kind = flags.kind) {
+ ClassKind.CLASS -> AbiClassKind.CLASS
+ ClassKind.INTERFACE -> AbiClassKind.INTERFACE
+ ClassKind.OBJECT -> AbiClassKind.OBJECT
+ ClassKind.ENUM_CLASS -> AbiClassKind.ENUM_CLASS
+ ClassKind.ANNOTATION_CLASS -> AbiClassKind.ANNOTATION_CLASS
+ ClassKind.ENUM_ENTRY -> error("Unexpected class kind: $kind")
+ },
+ isInner = flags.isInner,
+ isValue = flags.isValue,
+ isFunction = flags.isFun,
+ superTypes = deserializeTypes(proto.superTypeList, thisClassTypeParameterResolver) { type ->
+ !isKotlinBuiltInType(type, KOTLIN_ANY_QUALIFIED_NAME, DEFINITELY_NOT_NULL) && typeDeserializer.isPubliclyVisible(type)
+ },
+ declarations = memberDeclarations,
+ typeParameters = deserializeTypeParameters(proto.typeParameterList, thisClassTypeParameterResolver)
+ )
+ }
+
+ private inline fun deserializeTypes(
+ typeIds: List,
+ typeParameterResolver: TypeParameterResolver,
+ predicate: (AbiType) -> Boolean
+ ): List = typeIds.memoryOptimizedMapNotNull { typeId ->
+ typeDeserializer.deserializeType(typeId, typeParameterResolver).takeIf(predicate)
+ }
+
+ private fun deserializeEnumEntry(proto: ProtoEnumEntry, containingEntity: ContainingEntity): AbiEnumEntry {
+ return AbiEnumEntryImpl(
+ qualifiedName = deserializeQualifiedName(proto.name, containingEntity),
+ signatures = deserializeSignatures(proto.base),
+ annotations = deserializeAnnotations(proto.base)
+ )
+ }
+
+ private fun deserializeFunction(
+ proto: ProtoFunctionBase,
+ isConstructor: Boolean,
+ containingEntity: ContainingEntity,
+ parentTypeParameterResolver: TypeParameterResolver?
+ ): AbiFunction? {
+ val annotations = deserializeAnnotations(proto.base)
+
+ val containingClassModality = when (containingEntity) {
+ is ContainingEntity.Class -> containingEntity.modality
+ is ContainingEntity.Property -> containingEntity.containingClassModality
+ else -> null
+ }
+
+ val parentPropertyVisibilityStatus = (containingEntity as? ContainingEntity.Property)?.propertyVisibilityStatus
+ if (!computeVisibilityStatus(
+ proto.base,
+ annotations,
+ containingClassModality,
+ parentPropertyVisibilityStatus
+ ).isPubliclyVisible
+ ) {
+ return null
+ }
+
+ val flags = FunctionFlags.decode(proto.base.flags)
+ if (flags.isFakeOverride) // TODO: FO of class with supertype from interop library
+ return null
+
+ val nameAndType = BinaryNameAndType.decode(proto.nameType)
+ val functionName = deserializeQualifiedName(
+ nameId = nameAndType.nameIndex,
+ containingEntity = containingEntity
+ )
+
+ val thisFunctionTypeParameterResolver = when {
+ isConstructor -> {
+ // Reuse the TP resolved from the class, as constructors can't have own TPs.
+ parentTypeParameterResolver!!
+ }
+ containingEntity is ContainingEntity.Property -> {
+ // 1. A TP of a serialized property accessor has signature that points to the property itself.
+ // So for the need of TP resolution it's necessary to pass the name of the property to the TP resolver.
+ // 2. Properties don't have their own TPs, but their accessors can have TPs. This means that there is
+ // no need to create a TP resolver for a property, only for the accessor. To make rendering of
+ // accessor's TPs consistent with the position of the accessor inside the declaration's tree,
+ // it's necessary to adjust the "level" field inside the TP resolver by 1.
+ TypeParameterResolver(containingEntity.propertyName, parentTypeParameterResolver, levelAdjustment = 1)
+ }
+ else -> TypeParameterResolver(functionName, parentTypeParameterResolver)
+ }
+
+ val extensionReceiver = if (proto.hasExtensionReceiver())
+ deserializeValueParameter(proto.extensionReceiver, thisFunctionTypeParameterResolver)
+ else
+ null
+ val contextReceiversCount = if (proto.hasContextReceiverParametersCount()) proto.contextReceiverParametersCount else 0
+
+ val allValueParameters = ArrayList()
+ allValueParameters.addIfNotNull(extensionReceiver)
+ proto.valueParameterList.mapTo(allValueParameters) { deserializeValueParameter(it, thisFunctionTypeParameterResolver) }
+
+ return if (isConstructor) {
+ check(extensionReceiver == null) { "Unexpected extension receiver found for constructor $functionName" }
+
+ AbiConstructorImpl(
+ qualifiedName = functionName,
+ signatures = deserializeSignatures(proto.base),
+ annotations = annotations,
+ isInline = flags.isInline,
+ contextReceiverParametersCount = contextReceiversCount,
+ valueParameters = allValueParameters.compact()
+ )
+ } else {
+ // Show only a non-trivial return type for the others.
+ val nonTrivialReturnType = typeDeserializer.deserializeType(nameAndType.typeIndex, thisFunctionTypeParameterResolver)
+ .takeUnless { isKotlinBuiltInType(it, KOTLIN_UNIT_QUALIFIED_NAME, DEFINITELY_NOT_NULL) }
+
+ AbiFunctionImpl(
+ qualifiedName = functionName,
+ signatures = deserializeSignatures(proto.base),
+ annotations = annotations,
+ modality = flags.modality.toAbiModality(containingClassModality),
+ isInline = flags.isInline,
+ isSuspend = flags.isSuspend,
+ typeParameters = deserializeTypeParameters(proto.typeParameterList, thisFunctionTypeParameterResolver),
+ hasExtensionReceiverParameter = extensionReceiver != null,
+ contextReceiverParametersCount = contextReceiversCount,
+ valueParameters = allValueParameters.compact(),
+ returnType = nonTrivialReturnType
+ )
+ }
+ }
+
+ private fun deserializeProperty(
+ proto: ProtoProperty,
+ containingEntity: ContainingEntity,
+ typeParameterResolver: TypeParameterResolver?
+ ): AbiProperty? {
+ val annotations = deserializeAnnotations(proto.base)
+ val containingClassModality = (containingEntity as? ContainingEntity.Class)?.modality
+
+ val visibilityStatus = computeVisibilityStatus(proto.base, annotations, containingClassModality)
+ if (!visibilityStatus.isPubliclyVisible)
+ return null
+
+ val flags = PropertyFlags.decode(proto.base.flags)
+ if (flags.isFakeOverride) // TODO: FO of class with supertype from interop library
+ return null
+
+ val qualifiedName = deserializeQualifiedName(proto.name, containingEntity)
+ val thisPropertyEntity = ContainingEntity.Property(qualifiedName, containingClassModality, visibilityStatus)
+
+ return AbiPropertyImpl(
+ qualifiedName = qualifiedName,
+ signatures = deserializeSignatures(proto.base),
+ annotations = annotations,
+ modality = flags.modality.toAbiModality(containingClassModality),
+ kind = when {
+ flags.isConst -> AbiPropertyKind.CONST_VAL
+ flags.isVar -> AbiPropertyKind.VAR
+ else -> AbiPropertyKind.VAL
+ },
+ getter = proto.hasGetter().ifTrue {
+ deserializeFunction(
+ proto = proto.getter.base,
+ isConstructor = false,
+ containingEntity = thisPropertyEntity,
+ parentTypeParameterResolver = typeParameterResolver
+ ).discardIfExcluded()
+ },
+ setter = proto.hasSetter().ifTrue {
+ deserializeFunction(
+ proto = proto.setter.base,
+ isConstructor = false,
+ containingEntity = thisPropertyEntity,
+ parentTypeParameterResolver = typeParameterResolver
+ ).discardIfExcluded()
+ }
+ )
+ }
+
+ private fun deserializeQualifiedName(nameId: Int, containingEntity: ContainingEntity): AbiQualifiedName {
+ return containingEntity.computeNestedName(fileReader.string(nameId))
+ }
+
+ private fun deserializeSignatures(proto: ProtoDeclarationBase): AbiSignatures {
+ val signature = deserializeIdSignature(proto.symbol)
+
+ return AbiSignaturesImpl(
+ signatureV1 = if (needV1Signatures) signature.render(IdSignatureRenderer.LEGACY) else null,
+ signatureV2 = if (needV2Signatures) signature.render(IdSignatureRenderer.DEFAULT) else null
+ )
+ }
+
+ private fun deserializeTypeParameters(
+ protos: List,
+ typeParameterResolver: TypeParameterResolver,
+ ): List = protos.memoryOptimizedMapIndexed { index, proto ->
+ val flags = TypeParameterFlags.decode(proto.base.flags)
+
+ AbiTypeParameterImpl(
+ tag = typeParameterResolver.computeTypeParameterTag(index),
+ variance = flags.variance.toAbiVariance(),
+ isReified = flags.isReified,
+ upperBounds = deserializeTypes(proto.superTypeList, typeParameterResolver) { type ->
+ !isKotlinBuiltInType(type, KOTLIN_ANY_QUALIFIED_NAME, MARKED_NULLABLE)
+ }
+ )
+ }
+
+ private fun deserializeAnnotations(proto: ProtoDeclarationBase): Set {
+ fun deserialize(annotation: ProtoConstructorCall): AbiQualifiedName {
+ val signature = deserializeIdSignature(annotation.symbol)
+ val annotationClassName = when {
+ signature is CommonSignature -> signature
+ signature is CompositeSignature && signature.container is FileSignature -> signature.inner as CommonSignature
+ else -> error("Unexpected annotation signature encountered: ${signature::class.java}, ${signature.render()}")
+ }.extractQualifiedName { rawRelativeName ->
+ check(rawRelativeName.endsWith(INIT_SUFFIX)) {
+ "Annotation constructor name does not have '$INIT_SUFFIX' suffix: $rawRelativeName"
+ }
+ rawRelativeName.substring(0, rawRelativeName.length - INIT_SUFFIX.length)
+ }
+
+ // Avoid duplicated instances of popular signature names:
+ return annotationsInterner.intern(annotationClassName)
+ }
+
+ return when (proto.annotationCount) {
+ 0 -> return emptySet()
+ 1 -> return setOf(deserialize(proto.annotationList[0]))
+ else -> proto.annotationList.mapTo(SmartSet.create(), ::deserialize)
+ }
+ }
+
+ private fun computeVisibilityStatus(
+ proto: ProtoDeclarationBase,
+ annotations: Set,
+ containingClassModality: AbiModality?,
+ parentPropertyVisibilityStatus: VisibilityStatus? = null
+ ): VisibilityStatus = when (ProtoFlags.VISIBILITY.get(proto.flags.toInt())) {
+ ProtoBuf.Visibility.PUBLIC -> VisibilityStatus.PUBLIC
+
+ ProtoBuf.Visibility.PROTECTED -> {
+ if (containingClassModality == AbiModality.FINAL)
+ VisibilityStatus.NON_PUBLIC
+ else
+ VisibilityStatus.PUBLIC
+ }
+
+ ProtoBuf.Visibility.INTERNAL -> when {
+ parentPropertyVisibilityStatus == VisibilityStatus.INTERNAL_PUBLISHED_API -> VisibilityStatus.INTERNAL_PUBLISHED_API
+ PUBLISHED_API_CONSTRUCTOR_QUALIFIED_NAME in annotations -> VisibilityStatus.INTERNAL_PUBLISHED_API
+ else -> VisibilityStatus.NON_PUBLIC
+ }
+
+ else -> VisibilityStatus.NON_PUBLIC
+ }
+
+ private fun deserializeIdSignature(symbolId: Long): IdSignature {
+ val signatureId = BinarySymbolData.decode(symbolId).signatureId
+ return signatureDeserializer.deserializeIdSignature(signatureId)
+ }
+
+ private fun deserializeValueParameter(
+ proto: ProtoValueParameter,
+ typeParameterResolver: TypeParameterResolver
+ ): AbiValueParameter {
+ val flags = ValueParameterFlags.decode(proto.base.flags)
+
+ return AbiValueParameterImpl(
+ type = typeDeserializer.deserializeType(BinaryNameAndType.decode(proto.nameType).typeIndex, typeParameterResolver),
+ isVararg = proto.hasVarargElementType(),
+ hasDefaultArg = proto.hasDefaultValue(),
+ isNoinline = flags.isNoInline,
+ isCrossinline = flags.isCrossInline
+ )
+ }
+ }
+
+ private sealed interface ContainingEntity {
+ fun computeNestedName(simpleName: String): AbiQualifiedName
+
+ class Package(val packageName: AbiCompoundName) : ContainingEntity {
+ override fun computeNestedName(simpleName: String) = qualifiedName(packageName, simpleName)
+ }
+
+ class Class(val className: AbiQualifiedName, val modality: AbiModality) : ContainingEntity {
+ override fun computeNestedName(simpleName: String) = qualifiedName(className, simpleName)
+ }
+
+ class Property(
+ val propertyName: AbiQualifiedName,
+ val containingClassModality: AbiModality?,
+ val propertyVisibilityStatus: VisibilityStatus
+ ) : ContainingEntity {
+ override fun computeNestedName(simpleName: String) = qualifiedName(propertyName, simpleName)
+ }
+
+ companion object {
+ private fun qualifiedName(packageName: AbiCompoundName, topLevelSimpleName: String) =
+ AbiQualifiedName(packageName, AbiCompoundName(topLevelSimpleName))
+
+ private fun qualifiedName(parentName: AbiQualifiedName, memberSimpleName: String) =
+ AbiQualifiedName(
+ parentName.packageName,
+ AbiCompoundName("${parentName.relativeName}${AbiCompoundName.SEPARATOR}$memberSimpleName")
+ )
+ }
+ }
+
+ private class TypeParameterResolver(
+ private val declarationName: AbiQualifiedName,
+ val parent: TypeParameterResolver?,
+ levelAdjustment: Int = 0
+ ) {
+ val level: Int = (parent?.let { it.level + 1 } ?: 0) + levelAdjustment
+
+ fun computeTypeParameterTag(index: Int): String {
+ val tagPrefix = computeTagPrefix(index)
+ return if (level > 0) "$tagPrefix$level" else tagPrefix
+ }
+
+ fun resolveTypeParameterTag(declarationName: AbiQualifiedName, index: Int): String {
+ return if (declarationName == this.declarationName)
+ computeTypeParameterTag(index)
+ else
+ parent?.resolveTypeParameterTag(declarationName, index)
+ ?: error("Type parameter with local index $index can not be resolved for $declarationName")
+ }
+
+ companion object {
+ private const val ALPHABET_SIZE: Int = 'Z' - 'A' + 1
+
+ private fun computeTagPrefix(index: Int): String {
+ val result = SmartList()
+
+ var quotient = index
+ var remainder = quotient % ALPHABET_SIZE
+ do {
+ result += ('A' + remainder)
+ quotient /= ALPHABET_SIZE
+ remainder = (quotient - 1) % ALPHABET_SIZE
+ } while (quotient != 0)
+
+ return if (result.size == 1) result[0].toString() else result.asReversed().joinToString(separator = "")
+ }
+ }
+ }
+
+ private class TypeDeserializer(
+ private val libraryFile: IrLibraryFile,
+ private val signatureDeserializer: IdSignatureDeserializer
+ ) {
+ private val typeIdToTypeCache = HashMap()
+ private val nonPublicTopLevelClassNames = HashSet()
+
+ fun deserializeType(typeId: Int, typeParameterResolver: TypeParameterResolver): AbiType {
+ return typeIdToTypeCache.computeIfAbsent(typeId) {
+ val proto = libraryFile.type(typeId)
+ when (val kindCase = proto.kindCase) {
+ ProtoType.KindCase.DNN -> deserializeDefinitelyNotNullType(proto.dnn, typeParameterResolver)
+ ProtoType.KindCase.SIMPLE -> deserializeSimpleType(proto.simple, typeParameterResolver)
+ ProtoType.KindCase.LEGACYSIMPLE -> deserializeSimpleType(proto.legacySimple, typeParameterResolver)
+ ProtoType.KindCase.DYNAMIC -> DynamicTypeImpl
+ ProtoType.KindCase.ERROR -> ErrorTypeImpl
+ ProtoType.KindCase.KIND_NOT_SET -> error("Unexpected IR type: $kindCase")
+ }
+ }
+ }
+
+ fun isPubliclyVisible(type: AbiType): Boolean =
+ ((type as? AbiType.Simple)?.classifierReference as? ClassReference)?.className !in nonPublicTopLevelClassNames
+
+ private fun deserializeDefinitelyNotNullType(
+ proto: ProtoIrDefinitelyNotNullType,
+ typeParameterResolver: TypeParameterResolver,
+ ): AbiType {
+ assert(proto.typesCount == 1) { "Only DefinitelyNotNull type is now supported" }
+
+ val underlyingType = deserializeType(proto.getTypes(0), typeParameterResolver)
+ return if (underlyingType is AbiType.Simple && underlyingType.nullability != DEFINITELY_NOT_NULL)
+ SimpleTypeImpl(underlyingType.classifierReference, underlyingType.arguments, DEFINITELY_NOT_NULL)
+ else
+ underlyingType
+ }
+
+ private fun deserializeSimpleType(proto: ProtoSimpleType, typeParameterResolver: TypeParameterResolver): AbiType.Simple =
+ deserializeSimpleType(
+ symbolId = proto.classifier,
+ typeArgumentIds = proto.argumentList,
+ nullability = if (proto.hasNullability()) {
+ when (proto.nullability!!) {
+ ProtoSimpleTypeNullability.MARKED_NULLABLE -> MARKED_NULLABLE
+ ProtoSimpleTypeNullability.NOT_SPECIFIED -> NOT_SPECIFIED
+ ProtoSimpleTypeNullability.DEFINITELY_NOT_NULL -> DEFINITELY_NOT_NULL
+ }
+ } else NOT_SPECIFIED,
+ typeParameterResolver
+ )
+
+ private fun deserializeSimpleType(proto: ProtoSimpleTypeLegacy, typeParameterResolver: TypeParameterResolver): AbiType.Simple =
+ deserializeSimpleType(
+ symbolId = proto.classifier,
+ typeArgumentIds = proto.argumentList,
+ nullability = if (proto.hasHasQuestionMark() && proto.hasQuestionMark) MARKED_NULLABLE else NOT_SPECIFIED,
+ typeParameterResolver
+ )
+
+ private fun deserializeSimpleType(
+ symbolId: Long,
+ typeArgumentIds: List,
+ nullability: AbiTypeNullability,
+ typeParameterResolver: TypeParameterResolver
+ ): AbiType.Simple {
+ val symbolData = BinarySymbolData.decode(symbolId)
+ val signature = signatureDeserializer.deserializeIdSignature(symbolData.signatureId)
+ val symbolKind = symbolData.kind
+
+ return when {
+ symbolKind == CLASS_SYMBOL && signature is CommonSignature -> {
+ // Publicly visible class or interface.
+ SimpleTypeImpl(
+ classifierReference = ClassReferenceImpl(
+ className = signature.extractQualifiedName()
+ ),
+ arguments = deserializeTypeArguments(typeArgumentIds, typeParameterResolver),
+ nullability = nullability
+ )
+ }
+
+ symbolKind == CLASS_SYMBOL && signature is CompositeSignature && signature.container is FileSignature -> {
+ // Non-publicly visible classifier. Practically, this can only be a private top-level interface
+ // that some publicly visible class inherits. Need to memoize it to avoid displaying it later among
+ // supertypes of the inherited class.
+ val className = (signature.inner as CommonSignature).extractQualifiedName()
+ nonPublicTopLevelClassNames += className
+
+ SimpleTypeImpl(
+ classifierReference = ClassReferenceImpl(className),
+ arguments = deserializeTypeArguments(typeArgumentIds, typeParameterResolver),
+ nullability = nullability
+ )
+ }
+
+ symbolKind == TYPE_PARAMETER_SYMBOL && signature is CompositeSignature -> {
+ // A type-parameter.
+ SimpleTypeImpl(
+ classifierReference = TypeParameterReferenceImpl(
+ tag = typeParameterResolver.resolveTypeParameterTag(
+ declarationName = (signature.container as CommonSignature).extractQualifiedName(),
+ index = (signature.inner as LocalSignature).index()
+ )
+ ),
+ arguments = emptyList(),
+ nullability = nullability
+ )
+ }
+
+ else -> error("Unexpected combination of symbol kind ($symbolKind) and a signature: ${signature::class.java}, ${signature.render()}")
+ }
+ }
+
+ private fun deserializeTypeArguments(
+ typeArgumentIds: List,
+ typeParameterResolver: TypeParameterResolver
+ ): List {
+ return if (typeArgumentIds.isEmpty())
+ emptyList()
+ else typeArgumentIds.memoryOptimizedMap { typeArgumentId ->
+ val typeProjection = BinaryTypeProjection.decode(typeArgumentId)
+ if (typeProjection.isStarProjection)
+ StarProjectionImpl
+ else
+ TypeProjectionImpl(
+ type = deserializeType(typeProjection.typeIndex, typeParameterResolver),
+ variance = typeProjection.variance.toAbiVariance()
+ )
+ }
+ }
+ }
+
+ fun deserialize(): AbiTopLevelDeclarations {
+ val topLevels = ArrayList()
+
+ for (fileIndex in 0 until library.fileCount()) {
+ FileDeserializer(fileIndex).deserializeTo(topLevels)
+ }
+
+ return AbiTopLevelDeclarationsImpl(topLevels.compact())
+ }
+
+ private enum class VisibilityStatus(val isPubliclyVisible: Boolean) {
+ PUBLIC(true), INTERNAL_PUBLISHED_API(true), NON_PUBLIC(false)
+ }
+
+ companion object {
+ private val INIT_SUFFIX = "." + SpecialNames.INIT.asString()
+
+ private val KOTLIN_COMPOUND_NAME = AbiCompoundName("kotlin")
+ private val PUBLISHED_API_CONSTRUCTOR_QUALIFIED_NAME = AbiQualifiedName(KOTLIN_COMPOUND_NAME, AbiCompoundName("PublishedApi"))
+ private val KOTLIN_ANY_QUALIFIED_NAME = AbiQualifiedName(KOTLIN_COMPOUND_NAME, AbiCompoundName("Any"))
+ private val KOTLIN_UNIT_QUALIFIED_NAME = AbiQualifiedName(KOTLIN_COMPOUND_NAME, AbiCompoundName("Unit"))
+
+ private fun isKotlinBuiltInType(type: AbiType, className: AbiQualifiedName, nullability: AbiTypeNullability): Boolean {
+ if (type !is AbiType.Simple || type.nullability != nullability) return false
+ return (type.classifierReference as? ClassReference)?.className == className
+ }
+
+ private inline fun CommonSignature.extractQualifiedName(transformRelativeName: (String) -> String = { it }): AbiQualifiedName =
+ AbiQualifiedName(AbiCompoundName(packageFqName), AbiCompoundName(transformRelativeName(declarationFqName)))
+
+ private fun Modality.toAbiModality(containingClassModality: AbiModality?): AbiModality = when (this) {
+ Modality.FINAL -> AbiModality.FINAL
+ Modality.OPEN -> if (containingClassModality == AbiModality.FINAL) AbiModality.FINAL else AbiModality.OPEN
+ Modality.ABSTRACT -> AbiModality.ABSTRACT
+ Modality.SEALED -> AbiModality.SEALED
+ }
+
+ private fun Variance.toAbiVariance(): AbiVariance = when (this) {
+ Variance.INVARIANT -> AbiVariance.INVARIANT
+ Variance.IN_VARIANCE -> AbiVariance.IN
+ Variance.OUT_VARIANCE -> AbiVariance.OUT
+ }
+ }
+}
diff --git a/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiRendererImpl.kt b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiRendererImpl.kt
new file mode 100644
index 00000000000..f5af656c11b
--- /dev/null
+++ b/compiler/util-klib-abi/src/org/jetbrains/kotlin/library/abi/impl/LibraryAbiRendererImpl.kt
@@ -0,0 +1,434 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi.impl
+
+import org.jetbrains.kotlin.library.abi.*
+import org.jetbrains.kotlin.library.abi.impl.AbiFunctionImpl.Companion.BITS_ENOUGH_FOR_STORING_PARAMETERS_COUNT
+import org.jetbrains.kotlin.library.abi.impl.AbiRendererImpl.RenderedTopLevelDeclarations.printNestedDeclarationsInProperOrder as printTopLevelDeclarations
+import kotlin.text.Appendable
+
+@ExperimentalLibraryAbiReader
+internal class AbiRendererImpl(
+ private val libraryAbi: LibraryAbi,
+ private val settings: AbiRenderingSettings,
+ private val output: Appendable
+) {
+ fun render() {
+ printHeader()
+
+ if (settings.renderDeclarations)
+ printTopLevelDeclarations(libraryAbi.topLevelDeclarations, Printer(output, settings))
+ }
+
+ private fun printHeader() {
+ output.appendLine(
+ """
+ // Rendering settings:
+ // - Signature version: ${settings.renderedSignatureVersion.versionNumber}
+ // - Show manifest properties: ${settings.renderManifest}
+ // - Show declarations: ${settings.renderDeclarations}
+
+ // Library unique name: <${libraryAbi.uniqueName}>
+ """.trimIndent()
+ )
+
+ if (settings.renderManifest) {
+ with(libraryAbi.manifest) {
+ listOfNotNull(
+ platform?.let { "Platform" to it },
+ nativeTargets.takeIf { it.isNotEmpty() }?.let { "Native targets" to it.joinToString(separator = ", ") },
+ compilerVersion?.let { "Compiler version" to it },
+ abiVersion?.let { "ABI version" to it },
+ libraryVersion?.let { "Library version" to it },
+ irProviderName?.let { "IR provider" to it }
+ ).forEach { (name, value) ->
+ output.append("// ").append(name).append(": ").appendLine(value)
+ }
+ }
+ }
+ }
+
+ private class Printer(private val output: Appendable, private val settings: AbiRenderingSettings) {
+ private var indent = 0u
+
+ inline fun indented(block: () -> Unit) {
+ indent++
+ try {
+ block()
+ } finally {
+ indent--
+ }
+ }
+
+ fun printDeclaration(renderedDeclaration: RenderedDeclaration<*>, printOpeningBrace: Boolean = false): Unit = with(output) {
+ appendIndent()
+ append(renderedDeclaration.text)
+ if (printOpeningBrace) append(" {")
+ appendSignature(renderedDeclaration.declaration)
+ appendLine()
+ }
+
+ fun printClosingBrace(): Unit = with(output) {
+ appendIndent()
+ appendLine('}')
+ }
+
+ private fun appendIndent() {
+ for (i in 0u until indent) output.append(settings.indentationString)
+ }
+
+ private fun appendSignature(declaration: AbiDeclaration) {
+ output.append(" // ")
+ output.append(
+ declaration.signatures[settings.renderedSignatureVersion]
+ ?: settings.whenSignatureNotFound(declaration, settings.renderedSignatureVersion)
+ )
+ }
+ }
+
+ private abstract class RenderedDeclarationContainerKind {
+ /**
+ * Determines the relative order of the given [renderedDeclaration] to put it upper or lower in the renderer's output.
+ * The declarations of different kinds (ex: a class and a function) should always get a different order index.
+ */
+ protected abstract fun orderByDeclarationKind(renderedDeclaration: RenderedDeclaration<*>): Int
+
+ fun printNestedDeclarationsInProperOrder(container: T, printer: Printer) {
+ container.declarations.mapAndSort(
+ /**
+ * Always sort declarations in a strictly specific order before printing them to make the output
+ * be unaffected by the actual serialization order:
+ * 1. by declaration kind, see [orderByDeclarationKind]
+ * 2. by a fully-qualified name of the declaration
+ * 3. by an additional ordering factor #1, see implementations of [RenderedDeclaration.additionalOrderingFactor1]
+ * 4. by the text of the rendered declaration (excluding signatures!)
+ * 5. by an additional ordering factor #2, see implementations of [RenderedDeclaration.additionalOrderingFactor2]
+ */
+ compareBy(
+ ::orderByDeclarationKind,
+ { it.declaration.qualifiedName },
+ RenderedDeclaration<*>::additionalOrderingFactor1,
+ RenderedDeclaration<*>::text,
+ RenderedDeclaration<*>::additionalOrderingFactor2
+ ),
+ RenderedDeclaration.Companion::createFor
+ ).forEach { it.print(printer) }
+ }
+ }
+
+ private object RenderedTopLevelDeclarations : RenderedDeclarationContainerKind() {
+ /**
+ * When printing top-level declarations, the following order is used:
+ * 1. classes
+ * 2. properties
+ * 3. functions
+ */
+ override fun orderByDeclarationKind(renderedDeclaration: RenderedDeclaration<*>) =
+ when (renderedDeclaration.declaration) {
+ is AbiClass -> 1
+ is AbiProperty -> 2
+ is AbiFunction -> 3
+ else -> 4 // Normally, other types of declarations should not appear as top-level declarations.
+ }
+ }
+
+ private sealed class RenderedDeclaration(val declaration: T, val text: String) {
+ open val additionalOrderingFactor1: Int get() = 0
+ open val additionalOrderingFactor2: String get() = ""
+
+ abstract fun print(printer: Printer)
+
+ companion object {
+ fun createFor(declaration: AbiDeclaration): RenderedDeclaration<*> = when (declaration) {
+ is AbiFunction -> RenderedFunction(declaration)
+ is AbiProperty -> RenderedProperty(declaration)
+ is AbiClass -> RenderedClass(declaration)
+ is AbiEnumEntry -> RenderedEnumEntry(declaration)
+ }
+
+ fun StringBuilder.appendModalityOf(declaration: AbiDeclarationWithModality) {
+ append(declaration.modality.name.lowercase()).append(' ')
+ }
+
+ fun StringBuilder.appendNameOf(declaration: AbiDeclaration) {
+ // For non-top level declarations print only simple declaration's name.
+ val isTopLevel = declaration.qualifiedName.relativeName.nameSegmentsCount == 1
+ append(if (isTopLevel) declaration.qualifiedName else declaration.qualifiedName.relativeName.simpleName)
+ }
+
+ fun StringBuilder.appendTypeParametersOf(container: AbiTypeParametersContainer) {
+ if (container.typeParameters.isNotEmpty()) {
+ container.typeParameters.joinTo(this, separator = ", ", prefix = "<", postfix = ">") { typeParameter ->
+ appendTypeParameter(typeParameter)
+ }
+ append(' ')
+ }
+ }
+
+ private fun StringBuilder.appendTypeParameter(typeParameter: AbiTypeParameter): String {
+ append('#').append(typeParameter.tag).append(": ")
+ if (typeParameter.isReified) append("reified ")
+ appendVariance(typeParameter.variance)
+ when (typeParameter.upperBounds.size) {
+ 0 -> append("kotlin/Any?")
+ 1 -> appendType(typeParameter.upperBounds[0])
+ else -> appendSortedTypes(typeParameter.upperBounds, separator = " & ", prefix = "", postfix = "")
+ }
+ return ""
+ }
+
+ fun StringBuilder.appendType(type: AbiType) {
+ when (type) {
+ is AbiType.Simple -> when (val classifier = type.classifierReference) {
+ is AbiClassifierReference.ClassReference -> {
+ append(classifier.className)
+ if (type.arguments.isNotEmpty()) {
+ type.arguments.joinTo(this, separator = ", ", prefix = "<", postfix = ">") { typeArgument ->
+ appendTypeArgument(typeArgument)
+ }
+ }
+ if (type.nullability == AbiTypeNullability.MARKED_NULLABLE) append('?')
+ }
+ is AbiClassifierReference.TypeParameterReference -> {
+ append('#').append(classifier.tag)
+ when (type.nullability) {
+ AbiTypeNullability.MARKED_NULLABLE -> append('?')
+ AbiTypeNullability.NOT_SPECIFIED -> Unit // Do nothing.
+ AbiTypeNullability.DEFINITELY_NOT_NULL -> append("!!")
+ }
+ }
+ }
+ is AbiType.Dynamic -> append("dynamic")
+ is AbiType.Error -> append("error")
+ }
+ }
+
+ private fun StringBuilder.appendTypeArgument(typeArgument: AbiTypeArgument): String {
+ when (typeArgument) {
+ is AbiTypeArgument.StarProjection -> append('*')
+ is AbiTypeArgument.TypeProjection -> {
+ appendVariance(typeArgument.variance)
+ appendType(typeArgument.type)
+ }
+ }
+ return ""
+ }
+
+ private fun StringBuilder.appendVariance(variance: AbiVariance) {
+ when (variance) {
+ AbiVariance.INVARIANT -> Unit
+ AbiVariance.IN -> append("in ")
+ AbiVariance.OUT -> append("out ")
+ }
+ }
+
+ fun StringBuilder.appendSortedTypes(types: List, separator: String, prefix: String, postfix: String) {
+ types.mapAndSort(naturalOrder()) { buildString { appendType(it) } }
+ .joinTo(this, separator = separator, prefix = prefix, postfix = postfix)
+ }
+ }
+ }
+
+ private class RenderedClass(declaration: AbiClass) : RenderedDeclaration(
+ declaration = declaration,
+ text = buildString {
+ appendModalityOf(declaration)
+ if (declaration.isInner) append("inner ")
+ if (declaration.isValue) append("value ")
+ if (declaration.isFunction) append("fun ")
+ appendClassKind(declaration.kind)
+
+ // Note: Type parameters are rendered before the class name, exactly as it is done for functions.
+ // This is done intentionally for the purpose of unification of the rendering notation for different
+ // types of declarations.
+ appendTypeParametersOf(declaration)
+ appendNameOf(declaration)
+ if (declaration.superTypes.isNotEmpty()) {
+ appendSortedTypes(declaration.superTypes, separator = ", ", prefix = " : ", postfix = "")
+ }
+ }
+ ) {
+ override fun print(printer: Printer) {
+ val hasChildren = declaration.declarations.isNotEmpty()
+ printer.printDeclaration(this, printOpeningBrace = hasChildren)
+ if (hasChildren) {
+ printer.indented {
+ printNestedDeclarationsInProperOrder(container = declaration, printer)
+ }
+ printer.printClosingBrace()
+ }
+ }
+
+ companion object : RenderedDeclarationContainerKind() {
+ private fun StringBuilder.appendClassKind(classKind: AbiClassKind) {
+ append(
+ when (classKind) {
+ AbiClassKind.CLASS -> "class"
+ AbiClassKind.INTERFACE -> "interface"
+ AbiClassKind.OBJECT -> "object"
+ AbiClassKind.ENUM_CLASS -> "enum class"
+ AbiClassKind.ANNOTATION_CLASS -> "annotation class"
+ }
+ ).append(' ')
+ }
+
+ /**
+ * When printing nested declarations inside a class, the following order is used:
+ * 1. properties
+ * 2. constructors
+ * 3. functions
+ * 4. nested classes
+ * 5. enum entries
+ */
+ override fun orderByDeclarationKind(renderedDeclaration: RenderedDeclaration<*>) =
+ when (val declaration = renderedDeclaration.declaration) {
+ is AbiProperty -> 1
+ is AbiFunction -> if (declaration.isConstructor) 2 else 3
+ is AbiClass -> 4
+ is AbiEnumEntry -> 5
+ }
+ }
+ }
+
+ private class RenderedEnumEntry(declaration: AbiEnumEntry) : RenderedDeclaration(
+ declaration = declaration,
+ text = buildString {
+ append("enum entry ")
+ appendNameOf(declaration)
+ }
+ ) {
+ override fun print(printer: Printer) = printer.printDeclaration(this)
+ }
+
+ private class RenderedProperty(declaration: AbiProperty) : RenderedDeclaration(
+ declaration = declaration,
+ text = buildString {
+ appendModalityOf(declaration)
+ appendPropertyKind(declaration.kind)
+ appendNameOf(declaration)
+ }
+ ) {
+ private val getter = declaration.getter?.let(::RenderedFunction)
+ private val setter = declaration.setter?.let(::RenderedFunction)
+
+ /** Delegates to [getter] or [setter], because the property itself knows nothing about value parameters. */
+ override val additionalOrderingFactor1 get() = (getter ?: setter)?.additionalOrderingFactor1 ?: 0
+
+ /**
+ * Use rendered text of [getter] or [setter], because the rendered text of the property does not include
+ * any value parameter information useful for the proper ordering among all properties that share
+ * the same qualified name (i.e. among overloaded properties).
+ */
+ override val additionalOrderingFactor2 get() = (getter ?: setter)?.text.orEmpty()
+
+ override fun print(printer: Printer) {
+ printer.printDeclaration(this)
+ printer.indented {
+ getter?.print(printer)
+ setter?.print(printer)
+ }
+ }
+
+ companion object {
+ private fun StringBuilder.appendPropertyKind(propertyKind: AbiPropertyKind) {
+ append(
+ when (propertyKind) {
+ AbiPropertyKind.VAL -> "val"
+ AbiPropertyKind.CONST_VAL -> "const val"
+ AbiPropertyKind.VAR -> "var"
+ }
+ ).append(' ')
+ }
+ }
+ }
+
+ private class RenderedFunction(declaration: AbiFunction) : RenderedDeclaration(
+ declaration = declaration,
+ text = buildString {
+ if (!declaration.isConstructor || declaration.modality != AbiModality.FINAL) appendModalityOf(declaration)
+ if (declaration.isSuspend) append("suspend ")
+ if (declaration.isInline) append("inline ")
+ append(if (declaration.isConstructor) "constructor " else "fun ")
+ appendTypeParametersOf(declaration)
+ appendIrregularValueParametersOf(declaration)
+ appendNameOf(declaration)
+ appendRegularValueParametersOf(declaration)
+ appendReturnTypeOf(declaration)
+ }
+ ) {
+ /**
+ * Determines the relative order of a function to put it upper or lower in the renderer's output:
+ * - Functions without extension receiver go above functions with an extension receiver.
+ * - Functions without context receivers go above functions with context receivers.
+ * - The more regular value parameters function has, the lower it goes.
+ * - Same among functions with context receiver parameters.
+ */
+ override val additionalOrderingFactor1: Int
+ get() {
+ val extensionReceivers = if (declaration.hasExtensionReceiverParameter) 1 else 0
+ val contextReceivers = declaration.contextReceiverParametersCount
+ val regularParameters = declaration.valueParameters.size - extensionReceivers - contextReceivers
+ return (((contextReceivers shl 1) or extensionReceivers) shl BITS_ENOUGH_FOR_STORING_PARAMETERS_COUNT) or regularParameters
+ }
+
+ override fun print(printer: Printer) = printer.printDeclaration(this)
+
+ companion object {
+ private fun StringBuilder.appendIrregularValueParametersOf(function: AbiFunction) {
+ if (function.contextReceiverParametersCount > 0)
+ function.valueParameters
+ .asSequence()
+ .apply { if (function.hasExtensionReceiverParameter) drop(1) }
+ .take(function.contextReceiverParametersCount)
+ .joinTo(this, separator = ", ", prefix = "context(", postfix = ") ") { valueParameter ->
+ appendValueParameter(valueParameter)
+ }
+
+ if (function.hasExtensionReceiverParameter) {
+ append('(')
+ appendValueParameter(function.valueParameters[0])
+ append(").")
+ }
+ }
+
+ private fun StringBuilder.appendRegularValueParametersOf(function: AbiFunction) {
+ val skippedParametersCount = (if (function.hasExtensionReceiverParameter) 1 else 0) +
+ function.contextReceiverParametersCount
+
+ function.valueParameters
+ .asSequence()
+ .drop(skippedParametersCount)
+ .joinTo(this, separator = ", ", prefix = "(", postfix = ")") { valueParameter ->
+ appendValueParameter(valueParameter)
+ }
+ }
+
+ private fun StringBuilder.appendValueParameter(valueParameter: AbiValueParameter): String {
+ if (valueParameter.isNoinline) append("noinline ")
+ if (valueParameter.isCrossinline) append("crossinline ")
+ appendType(valueParameter.type)
+ if (valueParameter.isVararg) append("...")
+ if (valueParameter.hasDefaultArg) append(" =...")
+ return ""
+ }
+
+ private fun StringBuilder.appendReturnTypeOf(function: AbiFunction) {
+ function.returnType?.let { returnType -> append(": ").appendType(returnType) }
+ }
+ }
+ }
+
+ companion object {
+ private inline fun List.mapAndSort(comparator: Comparator, transform: (T) -> R): List {
+ if (isEmpty()) return emptyList()
+
+ val result = ArrayList(size)
+ mapTo(result, transform)
+ result.sortWith(comparator)
+ return result
+ }
+ }
+}
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AbiTypeArgumentRenderingTest.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AbiTypeArgumentRenderingTest.kt
new file mode 100644
index 00000000000..913a1f8ec1e
--- /dev/null
+++ b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AbiTypeArgumentRenderingTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.library.abi.impl.*
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.EnumSource
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+class AbiTypeArgumentRenderingTest {
+ @ParameterizedTest
+ @EnumSource(AbiSignatureVersions.Supported::class)
+ internal fun test(signatureVersion: AbiSignatureVersions.Supported) {
+ val mockLibraryAbi = mockLibraryAbi(
+ signatureVersion,
+ mockClass(
+ name = "FinalClass",
+ mockType(
+ "sample", "OpenClass",
+ mockType("sample", "InvariantClass") to AbiVariance.INVARIANT,
+ mockType("sample", "InClass") to AbiVariance.IN,
+ mockType("sample", "OutClass") to AbiVariance.OUT,
+ null
+ )
+ )
+ )
+
+ val renderedClass = LibraryAbiRenderer.render(mockLibraryAbi, AbiRenderingSettings(signatureVersion))
+ .lineSequence()
+ .filter(String::isNotBlank)
+ .last()
+
+ assertEquals(
+ "final class sample/FinalClass : sample/OpenClass // signature-v${signatureVersion.versionNumber}",
+ renderedClass
+ )
+ }
+
+ private fun mockLibraryAbi(signatureVersion: AbiSignatureVersion, vararg declarations: AbiDeclaration): LibraryAbi =
+ LibraryAbi(
+ manifest = LibraryManifest(null, emptyList(), null, null, null, null),
+ uniqueName = "type-argument-rendering-test",
+ signatureVersions = setOf(signatureVersion),
+ topLevelDeclarations = object : AbiTopLevelDeclarations {
+ override val declarations = declarations.toList()
+ }
+ )
+
+ private fun mockClass(name: String, vararg superTypes: AbiType): AbiClass =
+ AbiClassImpl(
+ qualifiedName = AbiQualifiedName(AbiCompoundName("sample"), AbiCompoundName(name)),
+ signatures = AbiSignaturesImpl("signature-v1", "signature-v2"),
+ annotations = emptySet(),
+ modality = AbiModality.FINAL,
+ kind = AbiClassKind.CLASS,
+ isInner = false,
+ isValue = false,
+ isFunction = false,
+ superTypes = superTypes.toList(),
+ declarations = emptyList(),
+ typeParameters = emptyList()
+ )
+
+ private fun mockType(packageName: String, className: String, vararg arguments: Pair?): AbiType {
+ return SimpleTypeImpl(
+ classifierReference = ClassReferenceImpl(AbiQualifiedName(AbiCompoundName(packageName), AbiCompoundName(className))),
+ arguments = arguments.map { argument ->
+ if (argument == null)
+ StarProjectionImpl
+ else
+ TypeProjectionImpl(argument.first, argument.second)
+ },
+ nullability = AbiTypeNullability.NOT_SPECIFIED
+ )
+ }
+}
\ No newline at end of file
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AbstractLibraryAbiReaderTest.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AbstractLibraryAbiReaderTest.kt
new file mode 100644
index 00000000000..a63fec27477
--- /dev/null
+++ b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AbstractLibraryAbiReaderTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.library.abi.impl.AbiSignatureVersions
+import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEqualsToFile
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.TestInfo
+import java.io.File
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+abstract class AbstractLibraryAbiReaderTest {
+ private lateinit var testName: String
+ private lateinit var buildDir: File
+
+ @BeforeEach
+ fun setUp(testInfo: TestInfo) {
+ testName = getTestName(testInfo)
+ buildDir = setUpBuildDir(testInfo)
+ }
+
+ fun runTest(relativePath: String) {
+ val (sourceFile, dumpFiles) = computeTestFiles(relativePath, AbiSignatureVersions.Supported.entries)
+
+ val filters = computeFiltersFromTestDirectives(sourceFile)
+
+ val library = buildLibrary(sourceFile, libraryName = testName, buildDir)
+ val libraryAbi = LibraryAbiReader.readAbiInfo(library, filters)
+
+ dumpFiles.entries.forEach { (signatureVersion, dumpFile) ->
+ val abiDump = LibraryAbiRenderer.render(
+ libraryAbi,
+ AbiRenderingSettings(signatureVersion)
+ )
+
+ assertEqualsToFile(dumpFile, abiDump)
+ }
+ }
+}
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AllKnownIrSignatureVersionsAreSupported.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AllKnownIrSignatureVersionsAreSupported.kt
new file mode 100644
index 00000000000..5e45f5a64b9
--- /dev/null
+++ b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/AllKnownIrSignatureVersionsAreSupported.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.library.KotlinIrSignatureVersion
+import org.jetbrains.kotlin.library.abi.impl.AbiSignatureVersions
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import java.util.*
+
+/**
+ * This test checks that all [KotlinIrSignatureVersion]s known to the current version of the compiler are supported
+ * by the same version of the ABI reader. The goal of the test is to make sure that a newly added [KotlinIrSignatureVersion]
+ * is not forgotten to be properly supported in the ABI reader.
+ *
+ * Note, that theoretically it is possible that the library itself is produced by a newer compiler, and thus it might
+ * have a newer [KotlinIrSignatureVersion] that is not yet supported by the current version of the ABI reader.
+ * This is a legal situation. And it is not covered by this test.
+ */
+@OptIn(ExperimentalLibraryAbiReader::class)
+class AllKnownIrSignatureVersionsAreSupported {
+ @Test
+ fun test() {
+ KotlinIrSignatureVersion.CURRENTLY_SUPPORTED_VERSIONS.forEach { irSignatureVersion ->
+ val abiSignatureVersion = AbiSignatureVersions.resolveByVersionNumber(irSignatureVersion.number)
+ assertTrue(abiSignatureVersion.isSupportedByAbiReader) {
+ "IR signature version $irSignatureVersion is not supported by the ABI reader"
+ }
+ }
+ }
+}
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/GenerateLibraryAbiReaderTests.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/GenerateLibraryAbiReaderTests.kt
new file mode 100644
index 00000000000..4888af3083e
--- /dev/null
+++ b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/GenerateLibraryAbiReaderTests.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
+
+fun main() {
+ System.setProperty("java.awt.headless", "true")
+
+ generateTestGroupSuiteWithJUnit5 {
+ testGroup("compiler/util-klib-abi/tests-gen", "compiler/util-klib-abi/testData") {
+ testClass(suiteTestClassName = "LibraryAbiReaderTest") {
+ model("content")
+ }
+ }
+ }
+}
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestRenderingTest.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestRenderingTest.kt
new file mode 100644
index 00000000000..9c69c5f6b84
--- /dev/null
+++ b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestRenderingTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.TestInfo
+import java.io.File
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+class ManifestRenderingTest {
+ private lateinit var buildDir: File
+ private lateinit var sourceFile: File
+
+ @BeforeEach
+ fun setUp(testInfo: TestInfo) {
+ buildDir = setUpBuildDir(testInfo)
+ sourceFile = buildDir.resolve("source.kt").apply { createNewFile() } // Just an empty file.
+ }
+
+ @Test
+ fun renderWithManifest() {
+ val customManifest = LibraryManifest(
+ platform = BuiltInsPlatform.JS.name,
+ nativeTargets = listOf("ios_arm64", "ios_simulator_arm64", "macos_arm64", "macos_x64"),
+ compilerVersion = "1.23.45",
+ abiVersion = "2.34.56",
+ libraryVersion = "3.45.67",
+ irProviderName = "test_ir_provider_123"
+ )
+
+ val libraryFile = buildLibrary(sourceFile, libraryName = "sample-library", buildDir)
+ patchManifest(libraryFile, customManifest)
+
+ val readManifest = LibraryAbiReader.readAbiInfo(libraryFile).manifest
+ assertTrue(customManifest !== readManifest)
+ assertEquals(customManifest, readManifest)
+ }
+}
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/utils.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/utils.kt
new file mode 100644
index 00000000000..1fc3b7792fe
--- /dev/null
+++ b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/utils.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.library.abi
+
+import com.intellij.openapi.vfs.StandardFileSystems
+import com.intellij.openapi.vfs.VirtualFileManager
+import com.intellij.openapi.vfs.local.CoreLocalFileSystem
+import com.intellij.psi.PsiManager
+import com.intellij.psi.SingleRootFileViewProvider
+import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
+import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
+import org.jetbrains.kotlin.cli.js.klib.compileModuleToAnalyzedFirWithPsi
+import org.jetbrains.kotlin.cli.js.klib.serializeFirKlib
+import org.jetbrains.kotlin.cli.js.klib.transformFirToIr
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.Companion.createForParallelTests
+import org.jetbrains.kotlin.config.CommonConfigurationKeys
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
+import org.jetbrains.kotlin.ir.backend.js.MainModule
+import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
+import org.jetbrains.kotlin.js.config.JSConfigurationKeys
+import org.jetbrains.kotlin.konan.file.ZipFileSystemCacheableAccessor
+import org.jetbrains.kotlin.konan.file.unzipTo
+import org.jetbrains.kotlin.konan.file.zipDirAs
+import org.jetbrains.kotlin.konan.properties.saveToFile
+import org.jetbrains.kotlin.library.*
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.serialization.js.ModuleKind
+import org.jetbrains.kotlin.test.util.KtTestUtil.getHomeDirectory
+import org.jetbrains.kotlin.test.utils.TestDisposable
+import org.jetbrains.kotlin.util.parseSpaceSeparatedArgs
+import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
+import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.TestInfo
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.PrintStream
+import java.nio.charset.Charset
+import org.jetbrains.kotlin.konan.file.File as KFile
+
+private val TestInfo.className: String get() = testClass.orElseGet { fail("Can't get test class name") }.simpleName
+private val TestInfo.methodName: String get() = testMethod.orElseGet { fail("Can't get test method name") }.name
+
+internal fun getTestName(testInfo: TestInfo): String = testInfo.methodName
+
+internal fun setUpBuildDir(testInfo: TestInfo): File {
+ val projectBuildDir = System.getenv(ENV_VAR_PROJECT_BUILD_DIR)
+ ?.let { File(it) }
+ ?: fail("$ENV_VAR_PROJECT_BUILD_DIR environment variable not specified")
+
+ assertTrue(projectBuildDir.isDirectory) { "Project build dir does not exist: $projectBuildDir" }
+
+ return projectBuildDir.resolve("t").resolve("${testInfo.className}.${testInfo.methodName}").apply {
+ deleteRecursively()
+ mkdirs()
+ }
+}
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+internal fun computeTestFiles(
+ relativePath: String,
+ signatureVersions: List
+): Pair> {
+ assertTrue(signatureVersions.isNotEmpty()) { "Signature versions not specified" }
+
+ val sourceFile = File(getHomeDirectory()).resolve(relativePath)
+ assertEquals("kt", sourceFile.extension) { "Invalid source file: $sourceFile" }
+ assertTrue(sourceFile.isFile) { "Source file does not exist: $sourceFile" }
+
+ return sourceFile to signatureVersions.associateWith { signatureVersion ->
+ val dumpFile = sourceFile.withReplacedExtensionOrNull("kt", "v${signatureVersion.versionNumber}.txt")!!
+ assertTrue(dumpFile.isFile) { "Dump file does not exist: $dumpFile" }
+ dumpFile
+ }
+}
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+internal fun computeFiltersFromTestDirectives(sourceFile: File): List {
+ fun String.parseQualifiedName() = AbiQualifiedName(
+ packageName = AbiCompoundName(substringBefore('/', missingDelimiterValue = "")),
+ relativeName = AbiCompoundName(substringAfter('/'))
+ )
+
+ val excludedPackages = mutableListOf()
+ val excludedClasses = mutableListOf()
+ val nonPublicMarkers = mutableListOf()
+
+ sourceFile.bufferedReader().lineSequence().forEach { line ->
+ if (!line.parseTestDirective(DIRECTIVE_EXCLUDED_PACKAGES, ::AbiCompoundName, excludedPackages::add)
+ && !line.parseTestDirective(DIRECTIVE_EXCLUDED_CLASSES, String::parseQualifiedName, excludedClasses::add)
+ && !line.parseTestDirective(DIRECTIVE_NON_PUBLIC_MARKERS, String::parseQualifiedName, nonPublicMarkers::add)
+ ) {
+ return listOfNotNull(
+ excludedPackages.ifNotEmpty(AbiReadingFilter::ExcludedPackages),
+ excludedClasses.ifNotEmpty(AbiReadingFilter::ExcludedClasses),
+ nonPublicMarkers.ifNotEmpty(AbiReadingFilter::NonPublicMarkerAnnotations)
+ )
+ }
+ }
+
+ return emptyList()
+}
+
+private inline fun String.parseTestDirective(
+ directivePrefix: String,
+ parser: (String) -> T,
+ consumer: (T) -> Unit,
+): Boolean {
+ if (!startsWith(directivePrefix))
+ return false
+
+ val remainder = substring(directivePrefix.length)
+ try {
+ val items = parseSpaceSeparatedArgs(remainder)
+ items.forEach { item -> consumer(parser(item)) }
+ return true
+ } catch (e: Exception) {
+ throw fail("Failure during parsing test directive: $this", e)
+ }
+}
+
+private const val DIRECTIVE_EXCLUDED_PACKAGES = "// EXCLUDED_PACKAGES:"
+private const val DIRECTIVE_EXCLUDED_CLASSES = "// EXCLUDED_CLASSES:"
+private const val DIRECTIVE_NON_PUBLIC_MARKERS = "// NON_PUBLIC_MARKERS:"
+
+internal fun buildLibrary(sourceFile: File, libraryName: String, buildDir: File): File {
+ val configuration = CompilerConfiguration()
+ val environment = createForParallelTests(TestDisposable(), configuration, EnvironmentConfigFiles.JS_CONFIG_FILES)
+
+ configuration.put(CommonConfigurationKeys.MODULE_NAME, libraryName)
+ configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
+ configuration.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
+ configuration.put(JSConfigurationKeys.ZIP_FILE_SYSTEM_ACCESSOR, ZipFileSystemCacheableAccessor(2))
+
+ val psiManager = PsiManager.getInstance(environment.project)
+ val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem
+
+ val virtualFile = fileSystem.findFileByIoFile(sourceFile) ?: error("VirtualFile for $sourceFile not found")
+ val ktFiles = SingleRootFileViewProvider(psiManager, virtualFile).allFiles.filterIsInstance()
+
+ val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter()
+
+ val jsStdlib = File("libraries/stdlib/js-ir/build/classes/kotlin/js/main").absoluteFile
+ val dependencies = listOf(jsStdlib.absolutePath)
+
+ val moduleStructure = ModulesStructure(
+ project = environment.project,
+ mainModule = MainModule.SourceFiles(ktFiles),
+ compilerConfiguration = configuration,
+ dependencies = dependencies,
+ friendDependenciesPaths = emptyList()
+ )
+
+ val outputStream = ByteArrayOutputStream()
+ val messageCollector = PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.PLAIN_FULL_PATHS, true)
+
+ val analyzedOutput = compileModuleToAnalyzedFirWithPsi(
+ moduleStructure = moduleStructure,
+ ktFiles = ktFiles,
+ libraries = dependencies,
+ friendLibraries = emptyList(),
+ diagnosticsReporter = diagnosticsReporter,
+ incrementalDataProvider = null,
+ lookupTracker = null,
+ useWasmPlatform = false
+ )
+
+ val fir2IrActualizedResult = transformFirToIr(moduleStructure, analyzedOutput.output, diagnosticsReporter)
+
+ if (analyzedOutput.reportCompilationErrors(moduleStructure, diagnosticsReporter, messageCollector)) {
+ val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8"))
+ fail("The following errors occurred compiling test:\n$messages")
+ }
+
+ val libraryDir = KFile(buildDir.resolve(libraryName).absolutePath)
+ val libraryFile = KFile(libraryDir.absolutePath + ".klib")
+
+ serializeFirKlib(
+ moduleStructure = moduleStructure,
+ firOutputs = analyzedOutput.output,
+ fir2IrActualizedResult = fir2IrActualizedResult,
+ outputKlibPath = libraryDir.absolutePath,
+ messageCollector = messageCollector,
+ diagnosticsReporter = diagnosticsReporter,
+ jsOutputName = libraryName,
+ useWasmPlatform = false
+ )
+
+ if (messageCollector.hasErrors()) {
+ val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8"))
+ fail("The following errors occurred serializing test klib:\n$messages")
+ }
+
+ libraryDir.zipDirAs(libraryFile)
+ libraryDir.deleteRecursively()
+
+ return File(libraryFile.absolutePath)
+}
+
+@OptIn(ExperimentalLibraryAbiReader::class)
+internal fun patchManifest(libraryFile: File, customManifest: LibraryManifest) {
+ val libraryKFile = KFile(libraryFile.absolutePath)
+ val manifestProperties = resolveSingleFileKlib(libraryKFile).manifestProperties
+
+ fun set(name: String, value: String?) {
+ assertTrue(!value.isNullOrBlank()) { "$name has invalid value: [$value]" }
+ manifestProperties[name] = value!!
+ }
+
+ set(KLIB_PROPERTY_BUILTINS_PLATFORM, customManifest.platform)
+ set(KLIB_PROPERTY_NATIVE_TARGETS, customManifest.nativeTargets.joinToString(" "))
+ set(KLIB_PROPERTY_COMPILER_VERSION, customManifest.compilerVersion)
+ set(KLIB_PROPERTY_ABI_VERSION, customManifest.abiVersion)
+ set(KLIB_PROPERTY_LIBRARY_VERSION, customManifest.libraryVersion)
+ set(KLIB_PROPERTY_IR_PROVIDER, customManifest.irProviderName)
+
+ assertEquals(libraryFile.extension, "klib")
+ val libraryDir = libraryFile.resolveSibling(libraryFile.nameWithoutExtension)
+ val libraryKDir = KFile(libraryDir.absolutePath)
+ libraryKFile.unzipTo(libraryKDir)
+ libraryKFile.deleteRecursively()
+
+ libraryDir.walkTopDown()
+ .filter { it.isFile && it.name == "manifest" }
+ .forEach { manifestProperties.saveToFile(KFile(it.absolutePath)) }
+
+ libraryKDir.zipDirAs(libraryKFile)
+}
+
+private const val ENV_VAR_PROJECT_BUILD_DIR = "PROJECT_BUILD_DIR"
diff --git a/compiler/util-klib-abi/testData/content/callables.kt b/compiler/util-klib-abi/testData/content/callables.kt
new file mode 100644
index 00000000000..6c9a441755f
--- /dev/null
+++ b/compiler/util-klib-abi/testData/content/callables.kt
@@ -0,0 +1,149 @@
+package callables.test
+
+fun regularFun(): String = ""
+fun regularFun(p1: Number): String = ""
+fun regularFun(p1: Int): String = ""
+fun regularFun(p1: Int, p2: Long): String = ""
+fun regularFun(p1: Number, p2: Long): String = ""
+fun regularFun(p1: Int, p2: Number): String = ""
+fun regularFun(p1: Number, p2: Number): String = ""
+fun Int.regularFun(): String = ""
+fun Long.regularFun(): String = ""
+fun Number.regularFun(): String = ""
+
+context(Int) fun regularFun(): String = ""
+context(Int) fun regularFun(p1: Number): String = ""
+context(Int) fun regularFun(p1: Int): String = ""
+context(Int) fun regularFun(p1: Int, p2: Long): String = ""
+context(Int) fun regularFun(p1: Number, p2: Long): String = ""
+context(Int) fun regularFun(p1: Int, p2: Number): String = ""
+context(Int) fun regularFun(p1: Number, p2: Number): String = ""
+context(Int) fun Int.regularFun(): String = ""
+context(Int) fun Long.regularFun(): String = ""
+context(Int) fun Number.regularFun(): String = ""
+
+context(Int, Long) fun regularFun(): String = ""
+context(Int, Long) fun regularFun(p1: Number): String = ""
+context(Int, Long) fun regularFun(p1: Int): String = ""
+context(Int, Long) fun regularFun(p1: Int, p2: Long): String = ""
+context(Int, Long) fun regularFun(p1: Number, p2: Long): String = ""
+context(Int, Long) fun regularFun(p1: Int, p2: Number): String = ""
+context(Int, Long) fun regularFun(p1: Number, p2: Number): String = ""
+context(Int, Long) fun Int.regularFun(): String = ""
+context(Int, Long) fun Long.regularFun(): String = ""
+context(Int, Long) fun Number.regularFun(): String = ""
+
+class FunctionContainer {
+ fun regularFun(): String = ""
+ fun regularFun(p1: Number): String = ""
+ fun regularFun(p1: Int): String = ""
+ fun regularFun(p1: Int, p2: Long): String = ""
+ fun regularFun(p1: Number, p2: Long): String = ""
+ fun regularFun(p1: Int, p2: Number): String = ""
+ fun regularFun(p1: Number, p2: Number): String = ""
+ fun Int.regularFun(): String = ""
+ fun Long.regularFun(): String = ""
+ fun Number.regularFun(): String = ""
+
+ context(Int) fun regularFun(): String = ""
+ context(Int) fun regularFun(p1: Number): String = ""
+ context(Int) fun regularFun(p1: Int): String = ""
+ context(Int) fun regularFun(p1: Int, p2: Long): String = ""
+ context(Int) fun regularFun(p1: Number, p2: Long): String = ""
+ context(Int) fun regularFun(p1: Int, p2: Number): String = ""
+ context(Int) fun regularFun(p1: Number, p2: Number): String = ""
+ context(Int) fun Int.regularFun(): String = ""
+ context(Int) fun Long.regularFun(): String = ""
+ context(Int) fun Number.regularFun(): String = ""
+
+ context(Int, Long) fun regularFun(): String = ""
+ context(Int, Long) fun regularFun(p1: Number): String = ""
+ context(Int, Long) fun regularFun(p1: Int): String = ""
+ context(Int, Long) fun regularFun(p1: Int, p2: Long): String = ""
+ context(Int, Long) fun regularFun(p1: Number, p2: Long): String = ""
+ context(Int, Long) fun regularFun(p1: Int, p2: Number): String = ""
+ context(Int, Long) fun regularFun(p1: Number, p2: Number): String = ""
+ context(Int, Long) fun Int.regularFun(): String = ""
+ context(Int, Long) fun Long.regularFun(): String = ""
+ context(Int, Long) fun Number.regularFun(): String = ""
+}
+
+suspend fun suspendFun(value: Int, block: (Int) -> String): String = ""
+suspend fun suspendFun(value: Int, block: suspend (Int) -> String): String = ""
+
+inline fun inlineFun(block: (Int) -> String): String = ""
+inline fun inlineFun(block: (Long) -> String): String = ""
+inline fun inlineFun(block: (Number) -> String): String = ""
+
+fun varargFun(vararg a: Int): String = ""
+fun varargFun(a: Array): String = ""
+fun varargFun(a: Array): String = ""
+fun varargFun(a: Array): String = ""
+
+fun varargFun(vararg a: String): String = ""
+fun varargFun(a: Array): String = ""
+fun varargFun(a: Array): String = ""
+fun varargFun(a: Array): String = ""
+
+fun typeParameterFun(a: String): String = ""
+fun typeParameterFun(a: String): String = ""
+fun typeParameterFun(a: T): String = ""
+fun typeParameterFun(a: T): String = ""
+fun typeParameterFun(a: T): String = ""
+fun typeParameterFun(a: T): String where T : CharSequence, T : Appendable = ""
+
+val regularVal: String get() = ""
+val Int.regularVal: String get() = ""
+val Long.regularVal: String get() = ""
+val Number.regularVal: String get() = ""
+
+var regularVar: String get() = ""
+ set(_) = Unit
+var Int.regularVar: String get() = ""
+ set(_) = Unit
+var Long.regularVar: String get() = ""
+ set(_) = Unit
+var Number.regularVar: String get() = ""
+ set(_) = Unit
+
+val typeParameterVal: String get() = ""
+val T.typeParameterVal: String where T : CharSequence, T : Appendable get() = ""
+
+var typeParameterVar: String get() = ""
+ set(_) = Unit
+var T.typeParameterVar: String where T : CharSequence, T : Appendable get() = ""
+ set(_) = Unit
+
+const val constProperty: String = ""
+
+inline val inlineVal: String get() = ""
+inline var inlineVar: String get() = ""
+ set(_) = Unit
+
+class PropertyContainer {
+ val regularVal: String get() = ""
+ val Int.regularVal: String get() = ""
+ val Long.regularVal: String get() = ""
+ val Number.regularVal: String get() = ""
+
+ var regularVar: String get() = ""
+ set(_) = Unit
+ var Int.regularVar: String get() = ""
+ set(_) = Unit
+ var Long.regularVar: String get() = ""
+ set(_) = Unit
+ var Number.regularVar: String get() = ""
+ set(_) = Unit
+
+ val typeParameterVal: String get() = ""
+ val T.typeParameterVal: String where T : CharSequence, T : Appendable get() = ""
+
+ var typeParameterVar: String get() = ""
+ set(_) = Unit
+ var T.typeParameterVar: String where T : CharSequence, T : Appendable get() = ""
+ set(_) = Unit
+
+ inline val inlineVal: String get() = ""
+ inline var inlineVar: String get() = ""
+ set(_) = Unit
+}
diff --git a/compiler/util-klib-abi/testData/content/callables.v1.txt b/compiler/util-klib-abi/testData/content/callables.v1.txt
new file mode 100644
index 00000000000..9a092415f7f
--- /dev/null
+++ b/compiler/util-klib-abi/testData/content/callables.v1.txt
@@ -0,0 +1,163 @@
+// Rendering settings:
+// - Signature version: 1
+// - Show manifest properties: false
+// - Show declarations: true
+
+// Library unique name:
+final class callables.test/FunctionContainer { // callables.test/FunctionContainer|null[0]
+ constructor () // callables.test/FunctionContainer.|-5645683436151566731[0]
+ final fun regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-1607736902443358908[0]
+ final fun regularFun(kotlin/Int): kotlin/String // callables.test/FunctionContainer.regularFun|2610511202482030950[0]
+ final fun regularFun(kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|-1444308164954439959[0]
+ final fun regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|1253865780311160876[0]
+ final fun regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|-7094704380505542965[0]
+ final fun regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|-378948707834118549[0]
+ final fun regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|-2271646920391707040[0]
+ final fun (kotlin/Int).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|2795910657932017532[0]
+ final fun (kotlin/Long).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-6283317282093795679[0]
+ final fun (kotlin/Number).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-2192609305655999008[0]
+ final fun context(kotlin/Int) regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-8267448815467806639[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Int): kotlin/String // callables.test/FunctionContainer.regularFun|-111672199320661666[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|-8697242820832893560[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|-5241394041073260245[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|-5978317558626334421[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|291819780317361658[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|8928686497007243010[0]
+ final fun context(kotlin/Int) (kotlin/Int).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-5014308192639081145[0]
+ final fun context(kotlin/Long) (kotlin/Long).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-7399704652058443494[0]
+ final fun context(kotlin/Number) (kotlin/Number).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-2107005658606574142[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|2841212003589703997[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Int): kotlin/String // callables.test/FunctionContainer.regularFun|-985389676546366868[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|1791390998424441841[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|2250042392910873061[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|1200032901543330229[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|-7756604040376043218[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|-5852923150600998663[0]
+ final fun context(kotlin/Int, kotlin/Int) (kotlin/Int).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-1047317519793480063[0]
+ final fun context(kotlin/Long, kotlin/Int) (kotlin/Long).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-4110111285900788090[0]
+ final fun context(kotlin/Number, kotlin/Int) (kotlin/Number).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|-3190887841330597637[0]
+}
+final class callables.test/PropertyContainer { // callables.test/PropertyContainer|null[0]
+ final val inlineVal // callables.test/PropertyContainer.inlineVal|-5201927927744237303[0]
+ final inline fun (): kotlin/String // callables.test/PropertyContainer.inlineVal.|-3774133809308892676[0]
+ final var inlineVar // callables.test/PropertyContainer.inlineVar|4093593025118739712[0]
+ final inline fun (): kotlin/String // callables.test/PropertyContainer.inlineVar.|5191851058645471995[0]
+ final inline fun (kotlin/String) // callables.test/PropertyContainer.inlineVar.|-1160421771960555320[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|8302928953435423801[0]
+ final fun (): kotlin/String // callables.test/PropertyContainer.regularVal.|-3202586928558914639[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|68831860860092280[0]
+ final fun (kotlin/Int).(): kotlin/String // callables.test/PropertyContainer.regularVal.|1142246892243086073[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|-5579099332440636214[0]
+ final fun (kotlin/Long).(): kotlin/String // callables.test/PropertyContainer.regularVal.|2341082381652662965[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|-5067332311376532792[0]
+ final fun (kotlin/Number).(): kotlin/String // callables.test/PropertyContainer.regularVal.|280416490180918505[0]
+ final var regularVar // callables.test/PropertyContainer.regularVar|6219482095538731779[0]
+ final fun (): kotlin/String // callables.test/PropertyContainer.regularVar.|-4669185078647138796[0]
+ final fun (kotlin/String) // callables.test/PropertyContainer.regularVar.|-4770127057965825178[0]
+ final var regularVar // callables.test/PropertyContainer.regularVar|4496565943717832687[0]
+ final fun (kotlin/Int).(): kotlin/String // callables.test/PropertyContainer.regularVar.|-6080445766330592605[0]
+ final fun (kotlin/Int).(kotlin/String) // callables.test/PropertyContainer.regularVar.|-3681323486923661283[0]
+ final var regularVar // callables.test/PropertyContainer.regularVar|1996981460395345177[0]
+ final fun (kotlin/Long).(): kotlin/String // callables.test/PropertyContainer.regularVar.|7034927826667428813[0]
+ final fun (kotlin/Long).(kotlin/String) // callables.test/PropertyContainer.regularVar.|1178143595556284736[0]
+ final var regularVar // callables.test/PropertyContainer.regularVar|-6923079879972526933[0]
+ final fun (kotlin/Number).(): kotlin/String // callables.test/PropertyContainer.regularVar.|2384277737300861982[0]
+ final fun (kotlin/Number).(kotlin/String) // callables.test/PropertyContainer.regularVar.|-6514051220363038309[0]
+ final val typeParameterVal // callables.test/PropertyContainer.typeParameterVal|4893664515247341696[0]
+ final fun (): kotlin/String // callables.test/PropertyContainer.typeParameterVal.|-166271421910940760[0]
+ final val typeParameterVal // callables.test/PropertyContainer.typeParameterVal|3916645226241752635[0]
+ final fun <#A2: kotlin.text/Appendable & kotlin/CharSequence> (#A2).(): kotlin/String // callables.test/PropertyContainer.typeParameterVal.|-3217792298032478048[0]
+ final var typeParameterVar // callables.test/PropertyContainer.typeParameterVar|6273344427230821135[0]
+ final fun (): kotlin/String // callables.test/PropertyContainer.typeParameterVar.|-3573990202711249503[0]
+ final fun (kotlin/String) // callables.test/PropertyContainer.typeParameterVar.|1653479554860734620[0]
+ final var typeParameterVar // callables.test/PropertyContainer.typeParameterVar|3838569829461573607[0]
+ final fun <#A2: kotlin.text/Appendable & kotlin/CharSequence> (#A2).(): kotlin/String // callables.test/PropertyContainer.typeParameterVar.|-2122591755051684684[0]
+ final fun <#A2: kotlin.text/Appendable & kotlin/CharSequence> (#A2).(kotlin/String) // callables.test/PropertyContainer.typeParameterVar.|-8363816135186264436[0]
+ constructor () // callables.test/PropertyContainer.|-5645683436151566731[0]
+}
+final const val callables.test/constProperty // callables.test/constProperty|-946595011727133850[0]
+ final fun (): kotlin/String // callables.test/constProperty.|4493949707887227090[0]
+final val callables.test/inlineVal // callables.test/inlineVal|-5201927927744237303[0]
+ final inline fun (): kotlin/String // callables.test/inlineVal.|-3774133809308892676[0]
+final var callables.test/inlineVar // callables.test/inlineVar|4093593025118739712[0]
+ final inline fun (): kotlin/String // callables.test/inlineVar.|5191851058645471995[0]
+ final inline fun (kotlin/String) // callables.test/inlineVar.|-1160421771960555320[0]
+final val callables.test/regularVal // callables.test/regularVal|8302928953435423801[0]
+ final fun (): kotlin/String // callables.test/regularVal.|-3202586928558914639[0]
+final val callables.test/regularVal // callables.test/regularVal|68831860860092280[0]
+ final fun (kotlin/Int).(): kotlin/String // callables.test/regularVal.|1142246892243086073[0]
+final val callables.test/regularVal // callables.test/regularVal|-5579099332440636214[0]
+ final fun (kotlin/Long).(): kotlin/String // callables.test/regularVal.|2341082381652662965[0]
+final val callables.test/regularVal // callables.test/regularVal|-5067332311376532792[0]
+ final fun (kotlin/Number).(): kotlin/String // callables.test/regularVal.|280416490180918505[0]
+final var callables.test/regularVar // callables.test/regularVar|6219482095538731779[0]
+ final fun (): kotlin/String // callables.test/regularVar.|-4669185078647138796[0]
+ final fun (kotlin/String) // callables.test/regularVar.|-4770127057965825178[0]
+final var callables.test/regularVar // callables.test/regularVar|4496565943717832687[0]
+ final fun (kotlin/Int).(): kotlin/String // callables.test/regularVar.|-6080445766330592605[0]
+ final fun (kotlin/Int).(kotlin/String) // callables.test/regularVar.|-3681323486923661283[0]
+final var callables.test/regularVar // callables.test/regularVar|1996981460395345177[0]
+ final fun (kotlin/Long).(): kotlin/String // callables.test/regularVar.|7034927826667428813[0]
+ final fun (kotlin/Long).(kotlin/String) // callables.test/regularVar.|1178143595556284736[0]
+final var callables.test/regularVar // callables.test/regularVar|-6923079879972526933[0]
+ final fun (kotlin/Number).(): kotlin/String // callables.test/regularVar.|2384277737300861982[0]
+ final fun (kotlin/Number).(kotlin/String) // callables.test/regularVar.|-6514051220363038309[0]
+final val callables.test/typeParameterVal // callables.test/typeParameterVal|4893664515247341696[0]
+ final fun (): kotlin/String // callables.test/typeParameterVal.|-166271421910940760[0]
+final val callables.test/typeParameterVal // callables.test/typeParameterVal|3916645226241752635[0]
+ final fun <#A1: kotlin.text/Appendable & kotlin/CharSequence> (#A1).(): kotlin/String // callables.test/typeParameterVal.|-3217792298032478048[0]
+final var callables.test/typeParameterVar // callables.test/typeParameterVar|6273344427230821135[0]
+ final fun (): kotlin/String // callables.test/typeParameterVar.|-3573990202711249503[0]
+ final fun (kotlin/String) // callables.test/typeParameterVar.|1653479554860734620[0]
+final var callables.test/typeParameterVar // callables.test/typeParameterVar|3838569829461573607[0]
+ final fun <#A1: kotlin.text/Appendable & kotlin/CharSequence> (#A1).(): kotlin/String // callables.test/typeParameterVar.|-2122591755051684684[0]
+ final fun <#A1: kotlin.text/Appendable & kotlin/CharSequence> (#A1).(kotlin/String) // callables.test/typeParameterVar.|-8363816135186264436[0]
+final inline fun callables.test/inlineFun(kotlin/Function1): kotlin/String // callables.test/inlineFun|714409574025065146[0]
+final inline fun callables.test/inlineFun(kotlin/Function1): kotlin/String // callables.test/inlineFun|-5246475948584640610[0]
+final inline fun callables.test/inlineFun(kotlin/Function1): kotlin/String // callables.test/inlineFun|2898953589470570438[0]
+final fun callables.test/regularFun(): kotlin/String // callables.test/regularFun|-1607736902443358908[0]
+final fun callables.test/regularFun(kotlin/Int): kotlin/String // callables.test/regularFun|2610511202482030950[0]
+final fun callables.test/regularFun(kotlin/Number): kotlin/String // callables.test/regularFun|-1444308164954439959[0]
+final fun callables.test/regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/regularFun|1253865780311160876[0]
+final fun callables.test/regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/regularFun|-7094704380505542965[0]
+final fun callables.test/regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/regularFun|-378948707834118549[0]
+final fun callables.test/regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/regularFun|-2271646920391707040[0]
+final fun (kotlin/Int).callables.test/regularFun(): kotlin/String // callables.test/regularFun|2795910657932017532[0]
+final fun (kotlin/Long).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-6283317282093795679[0]
+final fun (kotlin/Number).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-2192609305655999008[0]
+final fun context(kotlin/Int) callables.test/regularFun(): kotlin/String // callables.test/regularFun|-8267448815467806639[0]
+final fun context(kotlin/Int) callables.test/regularFun(kotlin/Int): kotlin/String // callables.test/regularFun|-111672199320661666[0]
+final fun context(kotlin/Int) callables.test/regularFun(kotlin/Number): kotlin/String // callables.test/regularFun|-8697242820832893560[0]
+final fun context(kotlin/Int) callables.test/regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/regularFun|-5241394041073260245[0]
+final fun context(kotlin/Int) callables.test/regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/regularFun|-5978317558626334421[0]
+final fun context(kotlin/Int) callables.test/regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/regularFun|291819780317361658[0]
+final fun context(kotlin/Int) callables.test/regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/regularFun|8928686497007243010[0]
+final fun context(kotlin/Int) (kotlin/Int).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-5014308192639081145[0]
+final fun context(kotlin/Long) (kotlin/Long).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-7399704652058443494[0]
+final fun context(kotlin/Number) (kotlin/Number).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-2107005658606574142[0]
+final fun context(kotlin/Int, kotlin/Long) callables.test/regularFun(): kotlin/String // callables.test/regularFun|2841212003589703997[0]
+final fun context(kotlin/Int, kotlin/Long) callables.test/regularFun(kotlin/Int): kotlin/String // callables.test/regularFun|-985389676546366868[0]
+final fun context(kotlin/Int, kotlin/Long) callables.test/regularFun(kotlin/Number): kotlin/String // callables.test/regularFun|1791390998424441841[0]
+final fun context(kotlin/Int, kotlin/Long) callables.test/regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/regularFun|2250042392910873061[0]
+final fun context(kotlin/Int, kotlin/Long) callables.test/regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/regularFun|1200032901543330229[0]
+final fun context(kotlin/Int, kotlin/Long) callables.test/regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/regularFun|-7756604040376043218[0]
+final fun context(kotlin/Int, kotlin/Long) callables.test/regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/regularFun|-5852923150600998663[0]
+final fun context(kotlin/Int, kotlin/Int) (kotlin/Int).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-1047317519793480063[0]
+final fun context(kotlin/Long, kotlin/Int) (kotlin/Long).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-4110111285900788090[0]
+final fun context(kotlin/Number, kotlin/Int) (kotlin/Number).callables.test/regularFun(): kotlin/String // callables.test/regularFun|-3190887841330597637[0]
+final suspend fun callables.test/suspendFun(kotlin/Int, kotlin.coroutines/SuspendFunction1): kotlin/String // callables.test/suspendFun|-1581459052918133591[0]
+final suspend fun callables.test/suspendFun(kotlin/Int, kotlin/Function1): kotlin/String // callables.test/suspendFun|-3317053272398283488[0]
+final fun <#A: kotlin.text/Appendable & kotlin/CharSequence> callables.test/typeParameterFun(#A): kotlin/String // callables.test/typeParameterFun|-3186493094297971013[0]
+final fun <#A: kotlin/Any> callables.test/typeParameterFun(#A): kotlin/String // callables.test/typeParameterFun|8559938077715041629[0]
+final fun <#A: kotlin/Any?> callables.test/typeParameterFun(#A): kotlin/String // callables.test/typeParameterFun|1341548673669881771[0]
+final fun <#A: kotlin/Any?> callables.test/typeParameterFun(kotlin/String): kotlin/String // callables.test/typeParameterFun|-7424528499036142451[0]
+final fun <#A: kotlin/CharSequence> callables.test/typeParameterFun(#A): kotlin/String // callables.test/typeParameterFun|5015797153746384960[0]
+final fun callables.test/typeParameterFun(kotlin/String): kotlin/String // callables.test/typeParameterFun|-2809315713852528915[0]
+final fun callables.test/varargFun(kotlin/Array): kotlin/String // callables.test/varargFun|2576907898178423483[0]
+final fun callables.test/varargFun(kotlin/Array): kotlin/String // callables.test/varargFun|-6880322114414235218[0]
+final fun callables.test/varargFun(kotlin/Array): kotlin/String // callables.test/varargFun|7971016788147623730[0]
+final fun callables.test/varargFun(kotlin/Array): kotlin/String // callables.test/varargFun|4538895325734409312[0]
+final fun callables.test/varargFun(kotlin/Array): kotlin/String // callables.test/varargFun|-2594127631335148538[0]
+final fun callables.test/varargFun(kotlin/Array): kotlin/String // callables.test/varargFun|-2225161879450919295[0]
+final fun callables.test/varargFun(kotlin/Array...): kotlin/String // callables.test/varargFun|4182110034727627686[0]
+final fun callables.test/varargFun(kotlin/IntArray...): kotlin/String // callables.test/varargFun|3145785856483302182[0]
diff --git a/compiler/util-klib-abi/testData/content/callables.v2.txt b/compiler/util-klib-abi/testData/content/callables.v2.txt
new file mode 100644
index 00000000000..b729d5a6122
--- /dev/null
+++ b/compiler/util-klib-abi/testData/content/callables.v2.txt
@@ -0,0 +1,163 @@
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: false
+// - Show declarations: true
+
+// Library unique name:
+final class callables.test/FunctionContainer { // callables.test/FunctionContainer|null[0]
+ constructor () // callables.test/FunctionContainer.|(){}[0]
+ final fun regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun(){}[0]
+ final fun regularFun(kotlin/Int): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun(kotlin.Int){}[0]
+ final fun regularFun(kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun(kotlin.Number){}[0]
+ final fun regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun(kotlin.Int;kotlin.Long){}[0]
+ final fun regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun(kotlin.Int;kotlin.Number){}[0]
+ final fun regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun(kotlin.Number;kotlin.Long){}[0]
+ final fun regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun(kotlin.Number;kotlin.Number){}[0]
+ final fun (kotlin/Int).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun@kotlin.Int(){}[0]
+ final fun (kotlin/Long).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun@kotlin.Long(){}[0]
+ final fun (kotlin/Number).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun@kotlin.Number(){}[0]
+ final fun context(kotlin/Int) regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int(){}[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Int): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int(kotlin.Int){}[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int(kotlin.Number){}[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int(kotlin.Int;kotlin.Long){}[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int(kotlin.Int;kotlin.Number){}[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int(kotlin.Number;kotlin.Long){}[0]
+ final fun context(kotlin/Int) regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int(kotlin.Number;kotlin.Number){}[0]
+ final fun context(kotlin/Int) (kotlin/Int).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int@kotlin.Int(){}[0]
+ final fun context(kotlin/Long) (kotlin/Long).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int@kotlin.Long(){}[0]
+ final fun context(kotlin/Number) (kotlin/Number).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int@kotlin.Number(){}[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long(){}[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Int): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long(kotlin.Int){}[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long(kotlin.Number){}[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Int, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long(kotlin.Int;kotlin.Long){}[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Int, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long(kotlin.Int;kotlin.Number){}[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Number, kotlin/Long): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long(kotlin.Number;kotlin.Long){}[0]
+ final fun context(kotlin/Int, kotlin/Long) regularFun(kotlin/Number, kotlin/Number): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long(kotlin.Number;kotlin.Number){}[0]
+ final fun context(kotlin/Int, kotlin/Int) (kotlin/Int).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long@kotlin.Int(){}[0]
+ final fun context(kotlin/Long, kotlin/Int) (kotlin/Long).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long@kotlin.Long(){}[0]
+ final fun context(kotlin/Number, kotlin/Int) (kotlin/Number).regularFun(): kotlin/String // callables.test/FunctionContainer.regularFun|regularFun!kotlin.Int!kotlin.Long@kotlin.Number(){}[0]
+}
+final class callables.test/PropertyContainer { // callables.test/PropertyContainer|null[0]
+ final val inlineVal // callables.test/PropertyContainer.inlineVal|{}inlineVal[0]
+ final inline fun (): kotlin/String // callables.test/PropertyContainer.inlineVal.|(){}[0]
+ final var inlineVar // callables.test/PropertyContainer.inlineVar|{}inlineVar[0]
+ final inline fun (): kotlin/String // callables.test/PropertyContainer.inlineVar.|(){}[0]
+ final inline fun (kotlin/String) // callables.test/PropertyContainer.inlineVar.|(kotlin.String){}[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|{}regularVal[0]
+ final fun (): kotlin/String // callables.test/PropertyContainer.regularVal.|(){}[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|@kotlin.Int{}regularVal[0]
+ final fun (kotlin/Int).(): kotlin/String // callables.test/PropertyContainer.regularVal.|@kotlin.Int(){}[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|@kotlin.Long{}regularVal[0]
+ final fun (kotlin/Long).(): kotlin/String // callables.test/PropertyContainer.regularVal.|@kotlin.Long(){}[0]
+ final val regularVal // callables.test/PropertyContainer.regularVal|@kotlin.Number{}regularVal[0]
+ final fun (kotlin/Number).(): kotlin/String // callables.test/PropertyContainer.regularVal.|@kotlin.Number(){}[0]
+ final var regularVar // callables.test/PropertyContainer.regularVar|{}regularVar[0]
+ final fun (): kotlin/String // callables.test/PropertyContainer.regularVar.|(){}[0]
+ final fun (kotlin/String) // callables.test/PropertyContainer.regularVar.|(kotlin.String){}[0]
+ final var regularVar // callables.test/PropertyContainer.regularVar|@kotlin.Int{}regularVar[0]
+ final fun (kotlin/Int).