[kotlinp] Refactor to separate common part from JVM-specific part
This refactoring includes the following: 1. Separate backend-neutral part of the metadata printer `Kotlinp` from the part that accesses JVM-specifics: `JvmKotlinp`. 2. Introduce `org.jetbrains.kotlin.kotlinp.Printer`: The component that renders both individual `Km*` nodes and the whole metadata tree, and does so with the proper indentation. ^KT-62340
This commit is contained in:
committed by
Space Team
parent
d769328311
commit
eec76865a7
-22
@@ -12,7 +12,6 @@ import kotlinx.metadata.jvm.localDelegatedProperties
|
||||
import kotlinx.metadata.jvm.signature
|
||||
import org.jetbrains.kotlin.abicmp.reports.MetadataPropertyReport
|
||||
import org.jetbrains.kotlin.abicmp.tasks.GenericMetadataTask
|
||||
import org.jetbrains.kotlin.kotlinp.*
|
||||
|
||||
fun loadProperties(container: KmDeclarationContainer) = container.properties.associateBy { it.getterSignature?.toString() ?: it.name }
|
||||
|
||||
@@ -28,27 +27,6 @@ fun loadLocalDelegatedProperties(kmPackage: KmPackage) =
|
||||
fun loadLocalDelegatedProperties(clazz: KotlinClassMetadata.Class) =
|
||||
clazz.kmClass.localDelegatedProperties.associateBy { it.getterSignature?.toString() ?: it.name }
|
||||
|
||||
fun List<KmVersionRequirement>.stringifyRelevantRequirements() =
|
||||
// older versions of requirements are redundant for compiler with version 1.9 or newer
|
||||
filter { it.version.major >= 2 || (it.version.major == 1 && it.version.minor >= 8) }
|
||||
.map(::printVersionRequirement).sorted().joinToString(prefix = "[", postfix = "]")
|
||||
|
||||
fun List<KmType>.stringifyTypeListSorted() = map(::printType).sorted().joinToString(prefix = "[", postfix = "]")
|
||||
|
||||
fun List<KmTypeParameter>.stringifyTypeParameters() = joinToString(prefix = "<", postfix = ">") { typeParam ->
|
||||
printTypeParameter(
|
||||
typeParam,
|
||||
KotlinpSettings(
|
||||
isVerbose = true,
|
||||
sortDeclarations = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun List<KmValueParameter>.stringifyValueParameters() = joinToString(prefix = "(", postfix = ")", transform = ::printValueParameter)
|
||||
|
||||
fun List<KmAnnotation>.stringifyAnnotations() = joinToString(prefix = "[", postfix = "]", transform = ::renderAnnotation)
|
||||
|
||||
inline fun <R, T> checkMetadataMembers(
|
||||
metadata1: R,
|
||||
metadata2: R,
|
||||
|
||||
+43
-19
@@ -5,11 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.abicmp.tasks
|
||||
|
||||
import kotlinx.metadata.ExperimentalContextReceivers
|
||||
import kotlinx.metadata.hasConstant
|
||||
import kotlinx.metadata.isVar
|
||||
import kotlinx.metadata.*
|
||||
import kotlinx.metadata.jvm.*
|
||||
import kotlinx.metadata.visibility
|
||||
import org.jetbrains.kotlin.abicmp.*
|
||||
import org.jetbrains.kotlin.abicmp.checkers.*
|
||||
import org.jetbrains.kotlin.kotlinp.*
|
||||
@@ -65,7 +62,7 @@ private val allFunctionMetadataCheckers = listOf(
|
||||
functionMetadataPropertyChecker("modifiers") { printFunctionModifiers(it) },
|
||||
functionMetadataPropertyChecker("typeParameters") { it.typeParameters.stringifyTypeParameters() },
|
||||
functionMetadataPropertyChecker("receiverParameterType") {
|
||||
it.receiverParameterType?.let { type -> printType(type) } ?: PROPERTY_VAL_STUB
|
||||
it.receiverParameterType?.let(::printType) ?: PROPERTY_VAL_STUB
|
||||
},
|
||||
functionMetadataPropertyChecker("valueParameters") { it.valueParameters.stringifyValueParameters() },
|
||||
functionMetadataPropertyChecker("contract") {
|
||||
@@ -90,13 +87,13 @@ private val allPropertyMetadataCheckers = listOf(
|
||||
propertyMetadataPropertyChecker("isVar") { it.isVar.toString() },
|
||||
propertyMetadataPropertyChecker("typeParameters") { it.typeParameters.stringifyTypeParameters() },
|
||||
propertyMetadataPropertyChecker("receiverParameterType") {
|
||||
it.receiverParameterType?.let { type -> printType(type) } ?: PROPERTY_VAL_STUB
|
||||
it.receiverParameterType?.let(::printType) ?: PROPERTY_VAL_STUB
|
||||
},
|
||||
propertyMetadataPropertyChecker("returnType") { printType(it.returnType) },
|
||||
propertyMetadataPropertyChecker("hasConstant") { it.hasConstant.toString() },
|
||||
propertyMetadataPropertyChecker("getterModifiers") { printPropertyAccessorModifiers(it.getter) },
|
||||
propertyMetadataPropertyChecker("setterModifiers") {
|
||||
it.setter?.let { setter -> printPropertyAccessorModifiers(setter) } ?: PROPERTY_VAL_STUB
|
||||
it.setter?.let(::printPropertyAccessorModifiers) ?: PROPERTY_VAL_STUB
|
||||
},
|
||||
propertyMetadataPropertyChecker("setterValueParameter") {
|
||||
it.setterParameter?.let { param -> printValueParameter(param) } ?: PROPERTY_VAL_STUB
|
||||
@@ -134,12 +131,12 @@ private val allClassMetadataCheckers = listOf(
|
||||
|
||||
classMetadataPropertyChecker("typeParameters") { it.kmClass.typeParameters.stringifyTypeParameters() },
|
||||
classMetadataPropertyChecker("superTypes") {
|
||||
it.kmClass.supertypes.map { type -> printType(type) }.sorted().joinToString(prefix = "[", postfix = "]")
|
||||
it.kmClass.supertypes.map(::printType).sorted().joinToString(prefix = "[", postfix = "]")
|
||||
},
|
||||
classMetadataPropertyChecker("companionObject") { it.kmClass.companionObject.toString() },
|
||||
classMetadataPropertyChecker("inlineClassUnderlyingPropertyName") { it.kmClass.inlineClassUnderlyingPropertyName.toString() },
|
||||
classMetadataPropertyChecker("inlineClassUnderlyingType") {
|
||||
it.kmClass.inlineClassUnderlyingType?.let { type -> printType(type) } ?: "---"
|
||||
it.kmClass.inlineClassUnderlyingType?.let(::printType) ?: "---"
|
||||
},
|
||||
classMetadataPropertyChecker("contextReceiverTypes") {
|
||||
@OptIn(ExperimentalContextReceivers::class)
|
||||
@@ -166,15 +163,7 @@ private val allMultifileClassPartMetadataCheckers = listOf(
|
||||
private val allSyntheticClassMetadataCheckers = listOf(
|
||||
syntheticClassMetadataPropertyChecker("isLambda") { it.isLambda.toString() },
|
||||
syntheticClassMetadataPropertyChecker("function") {
|
||||
it.kmLambda?.function?.let { function ->
|
||||
printFunction(
|
||||
function,
|
||||
KotlinpSettings(
|
||||
isVerbose = true,
|
||||
sortDeclarations = true
|
||||
)
|
||||
)
|
||||
} ?: PROPERTY_VAL_STUB
|
||||
it.kmLambda?.function?.let(::printFunction) ?: PROPERTY_VAL_STUB
|
||||
}
|
||||
)
|
||||
|
||||
@@ -221,4 +210,39 @@ class CheckerConfiguration(private val enabledExclusively: Set<String>, private
|
||||
if (enabledExclusively.isNotEmpty() && name !in enabledExclusively) return false
|
||||
return name !in disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val kotlinp = JvmKotlinp(Settings(isVerbose = true, sortDeclarations = true))
|
||||
|
||||
private fun renderAnnotation(annotation: KmAnnotation) = printString { kotlinp.renderAnnotation(annotation, this) }
|
||||
private fun List<KmAnnotation>.stringifyAnnotations() = joinToString(prefix = "[", postfix = "]", transform = ::renderAnnotation)
|
||||
|
||||
private fun printFunction(function: KmFunction) = printString { kotlinp.renderFunction(function, this) }
|
||||
|
||||
private fun printConstructorModifiers(constructor: KmConstructor) = printString { kotlinp.renderConstructorModifiers(constructor, this) }
|
||||
private fun printFunctionModifiers(function: KmFunction) = printString { kotlinp.renderFunctionModifiers(function, this) }
|
||||
private fun printPropertyModifiers(property: KmProperty) = printString { kotlinp.renderPropertyModifiers(property, this) }
|
||||
private fun printPropertyAccessorModifiers(accessorAttributes: KmPropertyAccessorAttributes) =
|
||||
printString { kotlinp.renderPropertyAccessorModifiers(accessorAttributes, this) }
|
||||
|
||||
private fun printType(type: KmType) = printString { kotlinp.renderType(type, this) }
|
||||
private fun List<KmType>.stringifyTypeListSorted() = map(::printType).sorted().joinToString(prefix = "[", postfix = "]")
|
||||
|
||||
private fun printTypeParameter(typeParameter: KmTypeParameter) =
|
||||
printString { kotlinp.renderTypeParameter(typeParameter, this) }
|
||||
|
||||
private fun List<KmTypeParameter>.stringifyTypeParameters() = joinToString(prefix = "<", postfix = ">", transform = ::printTypeParameter)
|
||||
|
||||
private fun printValueParameter(valueParameter: KmValueParameter) = printString { kotlinp.renderValueParameter(valueParameter, this) }
|
||||
private fun List<KmValueParameter>.stringifyValueParameters() = joinToString(prefix = "(", postfix = ")", transform = ::printValueParameter)
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private fun printContract(contract: KmContract) = printString { kotlinp.renderContract(contract, this) }
|
||||
|
||||
private fun printVersionRequirement(versionRequirement: KmVersionRequirement) =
|
||||
printString { kotlinp.renderVersionRequirement(versionRequirement, this) }
|
||||
|
||||
private fun List<KmVersionRequirement>.stringifyRelevantRequirements() =
|
||||
// older versions of requirements are redundant for compiler with version 1.9 or newer
|
||||
filter { it.version.major >= 2 || (it.version.major == 1 && it.version.minor >= 8) }
|
||||
.map(::printVersionRequirement).sorted().joinToString(prefix = "[", postfix = "]")
|
||||
|
||||
+5
-5
@@ -13,8 +13,8 @@ import org.jetbrains.kotlin.abicmp.isSynthetic
|
||||
import org.jetbrains.kotlin.abicmp.reports.ClassReport
|
||||
import org.jetbrains.kotlin.abicmp.reports.ListEntryDiff
|
||||
import org.jetbrains.kotlin.abicmp.tag
|
||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
||||
import org.jetbrains.kotlin.kotlinp.Settings
|
||||
import org.jetbrains.kotlin.kotlinp.JvmKotlinp
|
||||
import org.jetbrains.kotlin.kotlinp.readKotlinClassHeader
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassWriter
|
||||
@@ -50,7 +50,7 @@ class ClassTask(
|
||||
return classReader.readKotlinClassHeader()?.run { KotlinClassMetadata.readStrict(this) }
|
||||
}
|
||||
|
||||
val kotlinp = Kotlinp(KotlinpSettings(isVerbose = false, sortDeclarations = true))
|
||||
val kotlinp = JvmKotlinp(Settings(isVerbose = false, sortDeclarations = true))
|
||||
val metadata1 = class1.getMetadata()
|
||||
val metadata2 = class2.getMetadata()
|
||||
|
||||
@@ -59,8 +59,8 @@ class ClassTask(
|
||||
if (metadata1 == null || metadata2 == null) {
|
||||
report.addMetadataDiff(
|
||||
ListEntryDiff(
|
||||
metadata1?.run { kotlinp.renderClassFile(metadata1) },
|
||||
metadata2?.run { kotlinp.renderClassFile(metadata2) })
|
||||
metadata1?.run { kotlinp.printClassFile(metadata1) },
|
||||
metadata2?.run { kotlinp.printClassFile(metadata2) })
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.kotlinp
|
||||
|
||||
import kotlinx.metadata.*
|
||||
import kotlinx.metadata.jvm.*
|
||||
|
||||
class JvmKotlinp(settings: Settings) : Kotlinp(settings) {
|
||||
fun printClassFile(classFile: KotlinClassMetadata): String = printString {
|
||||
when (classFile) {
|
||||
is KotlinClassMetadata.Class -> renderClass(classFile.kmClass, this)
|
||||
is KotlinClassMetadata.FileFacade -> renderPackage(classFile.kmPackage, this)
|
||||
is KotlinClassMetadata.SyntheticClass -> renderSyntheticClass(classFile, this)
|
||||
is KotlinClassMetadata.MultiFileClassFacade -> renderMultiFileClassFacade(classFile, this)
|
||||
is KotlinClassMetadata.MultiFileClassPart -> renderMultiFileClassPart(classFile, this)
|
||||
is KotlinClassMetadata.Unknown -> appendLine("unknown file")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableMetadataApi::class)
|
||||
fun printModuleFile(metadata: KotlinModuleMetadata?): String = printString {
|
||||
if (metadata != null) renderModuleFile(metadata, this)
|
||||
else appendLine("unsupported file")
|
||||
}
|
||||
|
||||
private fun renderSyntheticClass(clazz: KotlinClassMetadata.SyntheticClass, printer: Printer): Unit = with(printer) {
|
||||
if (clazz.isLambda) {
|
||||
appendLine("lambda {")
|
||||
withIndent {
|
||||
val lambda = clazz.kmLambda ?: throw KotlinpException("Synthetic class $clazz is not a lambda")
|
||||
renderFunction(lambda.function, printer)
|
||||
}
|
||||
appendLine("}")
|
||||
} else {
|
||||
appendLine("synthetic class")
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderMultiFileClassFacade(clazz: KotlinClassMetadata.MultiFileClassFacade, printer: Printer): Unit = with(printer) {
|
||||
appendLine("multi-file class {")
|
||||
withIndent {
|
||||
for (part in clazz.partClassNames) {
|
||||
appendCommentedLine(part)
|
||||
}
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
private fun renderMultiFileClassPart(clazz: KotlinClassMetadata.MultiFileClassPart, printer: Printer) {
|
||||
renderPackage(clazz.kmPackage, printer) {
|
||||
printer.appendCommentedLine("facade: ", clazz.facadeClassName)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableMetadataApi::class)
|
||||
fun renderModuleFile(metadata: KotlinModuleMetadata, printer: Printer): Unit = with(printer) {
|
||||
appendLine("module {")
|
||||
|
||||
withIndent {
|
||||
val module = metadata.kmModule
|
||||
module.packageParts.forEach { (fqName, kmPackageParts) ->
|
||||
val presentableFqName = fqName.ifEmpty { "<root>" }
|
||||
appendLine("package ", presentableFqName, " {")
|
||||
withIndent {
|
||||
for (fileFacade in kmPackageParts.fileFacades) {
|
||||
appendLine(fileFacade)
|
||||
}
|
||||
for ((multiFileClassPart, facade) in kmPackageParts.multiFileClassParts) {
|
||||
appendLine(multiFileClassPart, " (", facade, ")")
|
||||
}
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
if (module.optionalAnnotationClasses.isNotEmpty()) {
|
||||
appendLine()
|
||||
appendCommentedLine("Optional annotations")
|
||||
appendLine()
|
||||
module.optionalAnnotationClasses.forEach { renderClass(it, printer) }
|
||||
}
|
||||
}
|
||||
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
override fun getAnnotations(typeParameter: KmTypeParameter) = typeParameter.annotations
|
||||
override fun getAnnotations(type: KmType) = type.annotations
|
||||
|
||||
override fun sortConstructors(constructors: List<KmConstructor>) = constructors.sortedBy { it.signature.toString() }
|
||||
override fun sortFunctions(functions: List<KmFunction>) = functions.sortedBy { it.signature.toString() }
|
||||
override fun sortProperties(properties: List<KmProperty>) = properties.sortedBy { it.getterSignature?.toString() ?: it.name }
|
||||
|
||||
override fun Printer.appendSignatures(constructor: KmConstructor) {
|
||||
constructor.signature?.let {
|
||||
appendCommentedLine("signature: ", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendSignatures(function: KmFunction) {
|
||||
function.signature?.let {
|
||||
appendCommentedLine("signature: ", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendSignatures(property: KmProperty) {
|
||||
property.fieldSignature?.let {
|
||||
appendCommentedLine("field: ", it)
|
||||
}
|
||||
property.getterSignature?.let {
|
||||
appendCommentedLine("getter: ", it)
|
||||
}
|
||||
property.setterSignature?.let {
|
||||
appendCommentedLine("setter: ", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendCustomAttributes(property: KmProperty) {
|
||||
property.syntheticMethodForAnnotations?.let {
|
||||
appendCommentedLine("synthetic method for annotations: ", it)
|
||||
}
|
||||
property.syntheticMethodForDelegate?.let {
|
||||
appendCommentedLine("synthetic method for delegate: ", it)
|
||||
}
|
||||
if (property.isMovedFromInterfaceCompanion) {
|
||||
appendCommentedLine("is moved from interface companion")
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendOrigin(clazz: KmClass) {
|
||||
clazz.anonymousObjectOriginName?.let {
|
||||
appendCommentedLine("anonymous object origin: ", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendOrigin(function: KmFunction) {
|
||||
function.lambdaClassOriginName?.let {
|
||||
appendCommentedLine("lambda class origin: ", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendCustomAttributes(clazz: KmClass) {
|
||||
appendExtensions(clazz.localDelegatedProperties, clazz.moduleName)
|
||||
|
||||
if (clazz.hasMethodBodiesInInterface) {
|
||||
appendLine()
|
||||
appendCommentedLine("has method bodies in interface")
|
||||
}
|
||||
if (clazz.isCompiledInCompatibilityMode) {
|
||||
appendLine()
|
||||
appendCommentedLine("is compiled in compatibility mode")
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendCustomAttributes(pkg: KmPackage) {
|
||||
appendExtensions(pkg.localDelegatedProperties, pkg.moduleName)
|
||||
}
|
||||
|
||||
private fun Printer.appendExtensions(localDelegatedProperties: List<KmProperty>, moduleName: String?) {
|
||||
localDelegatedProperties.sortIfNeeded(::sortProperties).forEachIndexed { index, property ->
|
||||
appendLine()
|
||||
appendCommentedLine("local delegated property #", index)
|
||||
|
||||
// Comment all uncommented lines to not make it look like these properties are declared here
|
||||
printString { renderProperty(property, this) }
|
||||
.lineSequence()
|
||||
.filter { it.isNotBlank() }
|
||||
.forEach { appendCommentedLine(it) }
|
||||
}
|
||||
|
||||
if (settings.isVerbose) {
|
||||
moduleName?.let {
|
||||
appendLine()
|
||||
appendCommentedLine("module name: ", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendEnumEntries(clazz: KmClass) {
|
||||
clazz.enumEntries.forEach { enumEntry ->
|
||||
appendLine()
|
||||
appendLine(enumEntry, ",")
|
||||
}
|
||||
}
|
||||
|
||||
override fun Printer.appendCompileTimeConstant(property: KmProperty): Printer {
|
||||
return append("...")
|
||||
}
|
||||
|
||||
override fun isRaw(type: KmType) = type.isRaw
|
||||
|
||||
override fun renderFlexibleTypeUpperBound(flexibleTypeUpperBound: KmFlexibleTypeUpperBound): String? {
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
return if (flexibleTypeUpperBound.typeFlexibilityId == JvmTypeExtensionVisitor.PLATFORM_TYPE_ID)
|
||||
printString { appendType(flexibleTypeUpperBound.type) }
|
||||
else
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,567 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2024 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.kotlinp
|
||||
|
||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||
import kotlinx.metadata.jvm.KotlinModuleMetadata
|
||||
import kotlinx.metadata.jvm.UnstableMetadataApi
|
||||
import java.io.File
|
||||
import kotlinx.metadata.*
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
|
||||
class Kotlinp(private val settings: KotlinpSettings) {
|
||||
fun renderClassFile(classFile: KotlinClassMetadata): String =
|
||||
when (classFile) {
|
||||
is KotlinClassMetadata.Class -> ClassPrinter(settings).print(classFile)
|
||||
is KotlinClassMetadata.FileFacade -> FileFacadePrinter(settings).print(classFile)
|
||||
is KotlinClassMetadata.SyntheticClass -> {
|
||||
if (classFile.isLambda) LambdaPrinter(settings).print(classFile)
|
||||
else buildString { appendLine("synthetic class") }
|
||||
abstract class Kotlinp(protected val settings: Settings) {
|
||||
fun renderAnnotation(annotation: KmAnnotation, printer: Printer): Unit = with(printer) {
|
||||
append(annotation.className)
|
||||
appendCollectionIfNotEmpty(annotation.arguments.entries, prefix = "(", postfix = ")") { (name, argument) ->
|
||||
append(name, " = ")
|
||||
renderAnnotationArgument(argument, printer)
|
||||
}
|
||||
}
|
||||
|
||||
fun renderAnnotationArgument(argument: KmAnnotationArgument, printer: Printer): Unit = with(printer) {
|
||||
fun String.sanitize(quote: Char): String = buildString(length) {
|
||||
for (c in this@sanitize) {
|
||||
when (c) {
|
||||
'\n' -> append("\\n")
|
||||
'\r' -> append("\\r")
|
||||
'\t' -> append("\\t")
|
||||
quote -> append("\\").append(quote)
|
||||
else -> append(if (c.isISOControl()) "\\u%04x".format(c.code) else c)
|
||||
}
|
||||
}
|
||||
is KotlinClassMetadata.MultiFileClassFacade -> MultiFileClassFacadePrinter().print(classFile)
|
||||
is KotlinClassMetadata.MultiFileClassPart -> MultiFileClassPartPrinter(settings).print(classFile)
|
||||
is KotlinClassMetadata.Unknown -> buildString { appendLine("unknown file") }
|
||||
}
|
||||
|
||||
|
||||
internal fun readClassFile(file: File): Metadata {
|
||||
return file.readKotlinClassHeader() ?: throw KotlinpException("file is not a Kotlin class file: $file")
|
||||
}
|
||||
|
||||
internal fun readMetadata(metadata: Metadata): KotlinClassMetadata {
|
||||
return try {
|
||||
KotlinClassMetadata.readLenient(metadata)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw KotlinpException("inconsistent Kotlin metadata: ${e.message}")
|
||||
when (argument) {
|
||||
is KmAnnotationArgument.ByteValue -> append("${argument.value}.toByte()")
|
||||
is KmAnnotationArgument.CharValue -> append("'${argument.value.toString().sanitize(quote = '\'')}'")
|
||||
is KmAnnotationArgument.ShortValue -> append("${argument.value}.toShort()")
|
||||
is KmAnnotationArgument.IntValue -> append(argument.value)
|
||||
is KmAnnotationArgument.LongValue -> append("${argument.value}L")
|
||||
is KmAnnotationArgument.FloatValue -> append("${argument.value}f")
|
||||
is KmAnnotationArgument.DoubleValue -> append(argument.value)
|
||||
is KmAnnotationArgument.UByteValue -> append("${argument.value}.toUByte()")
|
||||
is KmAnnotationArgument.UShortValue -> append("${argument.value}.toUShort()")
|
||||
is KmAnnotationArgument.UIntValue -> append("${argument.value}u")
|
||||
is KmAnnotationArgument.ULongValue -> append("${argument.value}uL")
|
||||
is KmAnnotationArgument.BooleanValue -> append(argument.value.toString())
|
||||
is KmAnnotationArgument.StringValue -> append("\"${argument.value.sanitize(quote = '"')}\"")
|
||||
is KmAnnotationArgument.KClassValue -> append("${argument.className}::class")
|
||||
is KmAnnotationArgument.ArrayKClassValue -> {
|
||||
repeat(argument.arrayDimensionCount) { append("kotlin/Array<") }
|
||||
append(argument.className, "::class")
|
||||
repeat(argument.arrayDimensionCount) { append(">") }
|
||||
}
|
||||
is KmAnnotationArgument.EnumValue -> append(argument.enumClassName, '.', argument.enumEntryName)
|
||||
is KmAnnotationArgument.AnnotationValue -> argument.annotation.let { annotation ->
|
||||
append(annotation.className)
|
||||
appendCollection(annotation.arguments.entries, prefix = "(", postfix = ")") { (name, argument) ->
|
||||
append(name, " = ")
|
||||
renderAnnotationArgument(argument, printer)
|
||||
}
|
||||
}
|
||||
is KmAnnotationArgument.ArrayValue -> appendCollection(argument.elements, prefix = "[", postfix = "]") { argument ->
|
||||
renderAnnotationArgument(argument, printer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableMetadataApi::class)
|
||||
internal fun renderModuleFile(metadata: KotlinModuleMetadata?): String =
|
||||
if (metadata != null) ModuleFilePrinter(settings).print(metadata)
|
||||
else buildString { appendLine("unsupported file") }
|
||||
protected fun Printer.appendAnnotations(hasAnnotations: Boolean?, annotations: List<KmAnnotation>, onePerLine: Boolean = true) {
|
||||
if (hasAnnotations != false) {
|
||||
annotations.forEach { annotation ->
|
||||
append("@")
|
||||
renderAnnotation(annotation, this)
|
||||
if (onePerLine) appendLine() else append(" ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableMetadataApi::class)
|
||||
internal fun readModuleFile(file: File): KotlinModuleMetadata? =
|
||||
runCatching { KotlinModuleMetadata.read(file.readBytes()) }.getOrNull()
|
||||
@OptIn(ExperimentalContextReceivers::class)
|
||||
fun renderClass(clazz: KmClass, printer: Printer): Unit = with(printer) {
|
||||
appendOrigin(clazz)
|
||||
appendVersionRequirements(clazz.versionRequirements)
|
||||
appendSignatures(clazz)
|
||||
appendAnnotations(clazz.hasAnnotations, getAnnotations(clazz))
|
||||
appendContextReceiverTypes(clazz.contextReceiverTypes)
|
||||
append(VISIBILITY_MAP[clazz.visibility])
|
||||
append(MODALITY_MAP[clazz.modality])
|
||||
appendFlags(
|
||||
clazz.isInner to "inner",
|
||||
clazz.isData to "data",
|
||||
clazz.isExternal to "external",
|
||||
clazz.isExpect to "expect",
|
||||
clazz.isValue to "value",
|
||||
clazz.isFunInterface to "fun",
|
||||
)
|
||||
append(CLASS_KIND_MAP[clazz.kind])
|
||||
append(clazz.name)
|
||||
appendTypeParameters(clazz.typeParameters)
|
||||
appendCollectionIfNotEmpty(clazz.supertypes, prefix = " : ") { appendType(it) }
|
||||
appendLine(" {")
|
||||
withIndent {
|
||||
clazz.constructors.sortIfNeeded(::sortConstructors).forEach { renderConstructor(it, printer) }
|
||||
appendDeclarationContainerMembers(clazz)
|
||||
clazz.companionObject?.let {
|
||||
appendLine()
|
||||
appendCommentedLine("companion object: $it")
|
||||
}
|
||||
clazz.nestedClasses.forEach {
|
||||
appendLine()
|
||||
appendCommentedLine("nested class: $it")
|
||||
}
|
||||
appendEnumEntries(clazz)
|
||||
clazz.sealedSubclasses.sortIfNeeded { it }.forEach {
|
||||
appendLine()
|
||||
appendCommentedLine("sealed subclass: $it")
|
||||
}
|
||||
clazz.inlineClassUnderlyingPropertyName?.let {
|
||||
appendLine()
|
||||
appendCommentedLine("underlying property: $it")
|
||||
}
|
||||
clazz.inlineClassUnderlyingType?.let {
|
||||
appendLine()
|
||||
commented { append("underlying type: ").appendType(it).appendLine() }
|
||||
}
|
||||
appendCustomAttributes(clazz)
|
||||
if (clazz.hasEnumEntries) {
|
||||
appendLine()
|
||||
appendCommentedLine("has Enum.entries")
|
||||
}
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
private fun Printer.appendDeclarationContainerMembers(container: KmDeclarationContainer) {
|
||||
container.functions.sortIfNeeded(::sortFunctions).forEach { renderFunction(it, this) }
|
||||
container.properties.sortIfNeeded(::sortProperties).forEach { renderProperty(it, this) }
|
||||
container.typeAliases.sortIfNeeded { it.sortedBy(KmTypeAlias::name) }.forEach { renderTypeAlias(it, this) }
|
||||
}
|
||||
|
||||
fun renderConstructor(constructor: KmConstructor, printer: Printer): Unit = with(printer) {
|
||||
appendLine()
|
||||
appendVersionRequirements(constructor.versionRequirements)
|
||||
appendSignatures(constructor)
|
||||
appendAnnotations(constructor.hasAnnotations, getAnnotations(constructor))
|
||||
renderConstructorModifiers(constructor, printer)
|
||||
append("constructor")
|
||||
appendValueParameters(constructor.valueParameters)
|
||||
appendLine()
|
||||
}
|
||||
|
||||
fun renderConstructorModifiers(constructor: KmConstructor, printer: Printer): Unit = with(printer) {
|
||||
append(VISIBILITY_MAP[constructor.visibility])
|
||||
appendFlags(
|
||||
constructor.isSecondary to "/* secondary */",
|
||||
constructor.hasNonStableParameterNames to "/* non-stable parameter names */"
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContextReceivers::class, ExperimentalContracts::class)
|
||||
fun renderFunction(function: KmFunction, printer: Printer): Unit = with(printer) {
|
||||
appendLine()
|
||||
appendOrigin(function)
|
||||
appendVersionRequirements(function.versionRequirements)
|
||||
appendSignatures(function)
|
||||
appendAnnotations(function.hasAnnotations, getAnnotations(function))
|
||||
appendContextReceiverTypes(function.contextReceiverTypes)
|
||||
renderFunctionModifiers(function, printer)
|
||||
append("fun ")
|
||||
appendTypeParameters(function.typeParameters, postfix = " ")
|
||||
appendReceiverParameterType(function.receiverParameterType)
|
||||
append(function.name)
|
||||
appendValueParameters(function.valueParameters)
|
||||
append(": ").appendType(function.returnType)
|
||||
appendLine()
|
||||
function.contract?.let {
|
||||
withIndent {
|
||||
renderContract(it, printer)
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderFunctionModifiers(function: KmFunction, printer: Printer): Unit = with(printer) {
|
||||
append(VISIBILITY_MAP[function.visibility])
|
||||
append(MODALITY_MAP[function.modality])
|
||||
append(MEMBER_KIND_MAP[function.kind])
|
||||
appendFlags(
|
||||
function.isOperator to "operator",
|
||||
function.isInfix to "infix",
|
||||
function.isInline to "inline",
|
||||
function.isTailrec to "tailrec",
|
||||
function.isExternal to "external",
|
||||
function.isSuspend to "suspend",
|
||||
function.isExpect to "expect",
|
||||
function.hasNonStableParameterNames to "/* non-stable parameter names */"
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun renderContract(contract: KmContract, printer: Printer): Unit = with(printer) {
|
||||
appendLine("contract {")
|
||||
withIndent {
|
||||
contract.effects.forEach { effect ->
|
||||
renderEffect(effect, printer)
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
append("}")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private fun renderEffect(effect: KmEffect, printer: Printer) {
|
||||
// If there are several arguments, only the first is taken, see ContractDeserializerImpl.deserializeSimpleEffect
|
||||
fun Printer.appendMeaningfulConstructorArgument(effect: KmEffect): Printer {
|
||||
effect.constructorArguments.firstOrNull()?.let { append(printEffectExpression(it)) }
|
||||
return this
|
||||
}
|
||||
|
||||
when (effect.type) {
|
||||
KmEffectType.RETURNS_CONSTANT -> {
|
||||
printer.append("returns(").appendMeaningfulConstructorArgument(effect).append(")")
|
||||
}
|
||||
|
||||
KmEffectType.CALLS -> {
|
||||
printer.append("callsInPlace(").appendMeaningfulConstructorArgument(effect)
|
||||
effect.invocationKind?.let {
|
||||
printer.append(", InvocationKind.${it.name}")
|
||||
}
|
||||
printer.append(")")
|
||||
}
|
||||
|
||||
KmEffectType.RETURNS_NOT_NULL -> {
|
||||
printer.append("returnsNotNull()")
|
||||
}
|
||||
}
|
||||
effect.conclusion?.let {
|
||||
printer.append(" implies (", printEffectExpression(it), ")")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private fun printEffectExpression(effectExpression: KmEffectExpression): String {
|
||||
val andArguments = effectExpression.andArguments.map(::printEffectExpression)
|
||||
val orArguments = effectExpression.orArguments.map(::printEffectExpression)
|
||||
|
||||
fun wrapIfNeeded(s: String): String =
|
||||
// A simple heuristic to avoid wrapping into unnecessary parentheses
|
||||
if ('&' in s || '|' in s) "($s)" else s
|
||||
|
||||
return printString {
|
||||
append(
|
||||
effectExpression.constantValue?.let { it.value.toString() }
|
||||
?: effectExpression.parameterIndex?.let { "p#$it" }
|
||||
?: ""
|
||||
)
|
||||
|
||||
effectExpression.isInstanceType?.let { isInstanceType ->
|
||||
append(" ")
|
||||
if (effectExpression.isNegated) append("!")
|
||||
append("is ").appendType(isInstanceType)
|
||||
}
|
||||
|
||||
if (effectExpression.isNullCheckPredicate) {
|
||||
append(if (effectExpression.isNegated) " != " else " == ")
|
||||
append("null")
|
||||
}
|
||||
|
||||
if (orArguments.isEmpty()) {
|
||||
for (andArgument in andArguments) {
|
||||
if (!isEmpty) append(" && ")
|
||||
append(wrapIfNeeded(andArgument))
|
||||
}
|
||||
}
|
||||
if (andArguments.isEmpty()) {
|
||||
for (orArgument in orArguments) {
|
||||
if (!isEmpty) append(" || ")
|
||||
append(wrapIfNeeded(orArgument))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderPackage(pkg: KmPackage, printer: Printer, appendPackageAttributes: () -> Unit = {}): Unit = with(printer) {
|
||||
appendLine("package {")
|
||||
withIndent {
|
||||
appendPackageAttributes()
|
||||
appendDeclarationContainerMembers(pkg)
|
||||
appendCustomAttributes(pkg)
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContextReceivers::class)
|
||||
fun renderProperty(property: KmProperty, printer: Printer): Unit = with(printer) {
|
||||
appendLine()
|
||||
appendVersionRequirements(property.versionRequirements)
|
||||
appendSignatures(property)
|
||||
appendCustomAttributes(property)
|
||||
appendAnnotations(property.hasAnnotations, getAnnotations(property))
|
||||
appendContextReceiverTypes(property.contextReceiverTypes)
|
||||
renderPropertyModifiers(property, printer)
|
||||
append(if (property.isVar) "var " else "val ")
|
||||
appendTypeParameters(property.typeParameters, postfix = " ")
|
||||
appendReceiverParameterType(property.receiverParameterType)
|
||||
append(property.name)
|
||||
append(": ").appendType(property.returnType)
|
||||
if (property.hasConstant) {
|
||||
append(" /* = ").appendCompileTimeConstant(property).append(" */")
|
||||
}
|
||||
appendLine()
|
||||
withIndent {
|
||||
appendGetterSignatures(property)
|
||||
appendAnnotations(property.getter.hasAnnotations, getGetterAnnotations(property))
|
||||
renderPropertyAccessorModifiers(property.getter, printer)
|
||||
appendLine("get")
|
||||
property.setter?.let { setter ->
|
||||
appendSetterSignatures(property)
|
||||
appendAnnotations(setter.hasAnnotations, getSetterAnnotations(property))
|
||||
renderPropertyAccessorModifiers(setter, printer)
|
||||
append("set")
|
||||
property.setterParameter?.let {
|
||||
appendValueParameters(listOf(it))
|
||||
}
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderPropertyModifiers(property: KmProperty, printer: Printer): Unit = with(printer) {
|
||||
append(VISIBILITY_MAP[property.visibility])
|
||||
append(MODALITY_MAP[property.modality])
|
||||
append(MEMBER_KIND_MAP[property.kind])
|
||||
appendFlags(
|
||||
property.isConst to "const",
|
||||
property.isLateinit to "lateinit",
|
||||
property.isExternal to "external",
|
||||
property.isDelegated to "/* delegated */",
|
||||
property.isExpect to "expect"
|
||||
)
|
||||
}
|
||||
|
||||
fun renderPropertyAccessorModifiers(accessorAttributes: KmPropertyAccessorAttributes, printer: Printer): Unit = with(printer) {
|
||||
append(VISIBILITY_MAP[accessorAttributes.visibility])
|
||||
append(MODALITY_MAP[accessorAttributes.modality])
|
||||
appendFlags(
|
||||
accessorAttributes.isNotDefault to "/* non-default */",
|
||||
accessorAttributes.isExternal to "external",
|
||||
accessorAttributes.isInline to "inline"
|
||||
)
|
||||
}
|
||||
|
||||
fun renderTypeAlias(typeAlias: KmTypeAlias, printer: Printer): Unit = with(printer) {
|
||||
appendLine()
|
||||
appendVersionRequirements(typeAlias.versionRequirements)
|
||||
appendSignatures(typeAlias)
|
||||
appendAnnotations(typeAlias.hasAnnotations, typeAlias.annotations)
|
||||
append(VISIBILITY_MAP[typeAlias.visibility], "typealias ", typeAlias.name)
|
||||
appendTypeParameters(typeAlias.typeParameters)
|
||||
append(" = ").appendType(typeAlias.underlyingType)
|
||||
append(" /* = ").appendType(typeAlias.expandedType).append(" */")
|
||||
appendLine()
|
||||
}
|
||||
|
||||
fun renderTypeParameter(typeParameter: KmTypeParameter, printer: Printer): Unit = with(printer) {
|
||||
appendFlags(typeParameter.isReified to "reified")
|
||||
appendAnnotations(hasAnnotations = null, getAnnotations(typeParameter), onePerLine = false)
|
||||
if (typeParameter.variance != KmVariance.INVARIANT) {
|
||||
append(typeParameter.variance.name.lowercase()).append(" ")
|
||||
}
|
||||
append("T#${typeParameter.id}")
|
||||
if (settings.isVerbose) {
|
||||
append(" /* ${typeParameter.name} */")
|
||||
}
|
||||
appendCollectionIfNotEmpty(typeParameter.upperBounds, separator = " & ", prefix = " : ") { appendType(it) }
|
||||
}
|
||||
|
||||
private fun Printer.appendTypeParameters(typeParameters: List<KmTypeParameter>, postfix: String = "") {
|
||||
appendCollectionIfNotEmpty(typeParameters, prefix = "<", postfix = ">$postfix") { renderTypeParameter(it, this) }
|
||||
}
|
||||
|
||||
fun renderType(type: KmType, printer: Printer) {
|
||||
val classifier = when (val cls = type.classifier) {
|
||||
is KmClassifier.Class -> cls.name
|
||||
is KmClassifier.TypeParameter -> "T#${cls.id}"
|
||||
is KmClassifier.TypeAlias -> "${cls.name}$TYPE_ALIAS_MARKER"
|
||||
}
|
||||
|
||||
val outerType = type.outerType
|
||||
val abbreviatedType = type.abbreviatedType
|
||||
val platformTypeUpperBound = type.flexibleTypeUpperBound?.let { renderFlexibleTypeUpperBound(it) }
|
||||
|
||||
printer += printString {
|
||||
appendAnnotations(hasAnnotations = null, getAnnotations(type), onePerLine = false)
|
||||
appendFlags(
|
||||
isRaw(type) to "/* raw */",
|
||||
type.isSuspend to "suspend"
|
||||
)
|
||||
if (outerType != null) {
|
||||
appendType(outerType).append(".").append(classifier.substringAfterLast('.'))
|
||||
} else {
|
||||
append(classifier)
|
||||
}
|
||||
|
||||
appendCollectionIfNotEmpty(type.arguments, prefix = "<", postfix = ">") { argument ->
|
||||
if (argument == KmTypeProjection.STAR) {
|
||||
append("*")
|
||||
} else {
|
||||
val (variance, argumentType) = argument
|
||||
if (variance == null || argumentType == null)
|
||||
throw IllegalArgumentException("Variance and type must be set for non-star type projection")
|
||||
|
||||
if (variance != KmVariance.INVARIANT) {
|
||||
append(variance.name.lowercase()).append(" ")
|
||||
}
|
||||
appendType(argumentType)
|
||||
}
|
||||
}
|
||||
|
||||
if (type.isNullable) append("?")
|
||||
if (type.isDefinitelyNonNull) append(" & Any")
|
||||
if (abbreviatedType != null) append(" /* = ").appendType(abbreviatedType).append(" */")
|
||||
|
||||
if (platformTypeUpperBound == "$this?") {
|
||||
append("!")
|
||||
} else if (platformTypeUpperBound != null) {
|
||||
append("..").append(platformTypeUpperBound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun Printer.appendType(type: KmType): Printer {
|
||||
renderType(type, this)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun Printer.appendContextReceiverTypes(contextReceiverTypes: List<KmType>) {
|
||||
appendCollectionIfNotEmpty(contextReceiverTypes, prefix = "context(", postfix = ")\n") { appendType(it) }
|
||||
}
|
||||
|
||||
private fun Printer.appendReceiverParameterType(receiverParameterType: KmType?) {
|
||||
receiverParameterType?.let {
|
||||
appendType(it).append(".")
|
||||
}
|
||||
}
|
||||
|
||||
fun renderValueParameter(valueParameter: KmValueParameter, printer: Printer): Unit = with(printer) {
|
||||
appendAnnotations(valueParameter.hasAnnotations, getAnnotations(valueParameter), onePerLine = false)
|
||||
appendFlags(
|
||||
valueParameter.isCrossinline to "crossinline",
|
||||
valueParameter.isNoinline to "noinline"
|
||||
)
|
||||
val varargElementType = valueParameter.varargElementType
|
||||
if (varargElementType != null) {
|
||||
append("vararg ", valueParameter.name, ": ").appendType(varargElementType)
|
||||
append(" /* ").appendType(valueParameter.type).append(" */")
|
||||
} else {
|
||||
append(valueParameter.name, ": ").appendType(valueParameter.type)
|
||||
}
|
||||
if (valueParameter.declaresDefaultValue) {
|
||||
append(" /* = ... */")
|
||||
}
|
||||
}
|
||||
|
||||
private fun Printer.appendValueParameters(valueParameters: List<KmValueParameter>) {
|
||||
appendCollection(valueParameters, prefix = "(", postfix = ")") { renderValueParameter(it, this) }
|
||||
}
|
||||
|
||||
fun renderVersionRequirement(versionRequirement: KmVersionRequirement, printer: Printer) {
|
||||
val version = with(versionRequirement.version) { "$major.$minor.$patch" }
|
||||
val kind = when (versionRequirement.kind) {
|
||||
KmVersionRequirementVersionKind.LANGUAGE_VERSION -> "language version "
|
||||
KmVersionRequirementVersionKind.COMPILER_VERSION -> "compiler version "
|
||||
KmVersionRequirementVersionKind.API_VERSION -> "API version "
|
||||
KmVersionRequirementVersionKind.UNKNOWN -> "unknown requirement "
|
||||
}
|
||||
val remainder = listOfNotNull(
|
||||
"level=${versionRequirement.level}",
|
||||
versionRequirement.errorCode?.let { "errorCode=$it" },
|
||||
versionRequirement.message?.let { "message=\"$it\"" }
|
||||
).joinToString(prefix = " (", postfix = ")")
|
||||
|
||||
printer.append("requires ", kind, version, remainder)
|
||||
}
|
||||
|
||||
private fun Printer.appendVersionRequirements(versionRequirements: List<KmVersionRequirement>) {
|
||||
versionRequirements.forEach { versionRequirement ->
|
||||
commented {
|
||||
renderVersionRequirement(versionRequirement, this)
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected inline fun <T : Any> List<T>.sortIfNeeded(sorter: (List<T>) -> List<T>): List<T> =
|
||||
if (settings.sortDeclarations) sorter(this) else this
|
||||
|
||||
companion object {
|
||||
private const val TYPE_ALIAS_MARKER = '^'
|
||||
|
||||
internal val VISIBILITY_MAP = mapOf(
|
||||
Visibility.INTERNAL to "internal ",
|
||||
Visibility.PRIVATE to "private ",
|
||||
Visibility.PRIVATE_TO_THIS to "private ",
|
||||
Visibility.PROTECTED to "protected ",
|
||||
Visibility.PUBLIC to "public ",
|
||||
Visibility.LOCAL to "local "
|
||||
)
|
||||
|
||||
internal val MODALITY_MAP = mapOf(
|
||||
Modality.FINAL to "final ",
|
||||
Modality.OPEN to "open ",
|
||||
Modality.ABSTRACT to "abstract ",
|
||||
Modality.SEALED to "sealed "
|
||||
)
|
||||
|
||||
internal val CLASS_KIND_MAP = mapOf(
|
||||
ClassKind.CLASS to "class ",
|
||||
ClassKind.INTERFACE to "interface ",
|
||||
ClassKind.ENUM_CLASS to "enum class ",
|
||||
ClassKind.ENUM_ENTRY to "enum entry ",
|
||||
ClassKind.ANNOTATION_CLASS to "annotation class ",
|
||||
ClassKind.OBJECT to "object ",
|
||||
ClassKind.COMPANION_OBJECT to "companion object "
|
||||
)
|
||||
|
||||
internal val MEMBER_KIND_MAP = mapOf(
|
||||
MemberKind.DECLARATION to "",
|
||||
MemberKind.FAKE_OVERRIDE to "/* fake override */ ",
|
||||
MemberKind.DELEGATION to "/* delegation */ ",
|
||||
MemberKind.SYNTHESIZED to "/* synthesized */ ",
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun getAnnotations(clazz: KmClass): List<KmAnnotation> = emptyList()
|
||||
protected open fun getAnnotations(constructor: KmConstructor): List<KmAnnotation> = emptyList()
|
||||
protected open fun getAnnotations(function: KmFunction): List<KmAnnotation> = emptyList()
|
||||
protected open fun getAnnotations(property: KmProperty): List<KmAnnotation> = emptyList()
|
||||
protected open fun getGetterAnnotations(property: KmProperty): List<KmAnnotation> = emptyList()
|
||||
protected open fun getSetterAnnotations(property: KmProperty): List<KmAnnotation> = emptyList()
|
||||
protected abstract fun getAnnotations(typeParameter: KmTypeParameter): List<KmAnnotation>
|
||||
protected abstract fun getAnnotations(type: KmType): List<KmAnnotation>
|
||||
protected open fun getAnnotations(valueParameter: KmValueParameter): List<KmAnnotation> = emptyList()
|
||||
|
||||
protected open fun sortConstructors(constructors: List<KmConstructor>): List<KmConstructor> = constructors
|
||||
protected open fun sortFunctions(functions: List<KmFunction>): List<KmFunction> = functions
|
||||
protected open fun sortProperties(properties: List<KmProperty>): List<KmProperty> = properties
|
||||
|
||||
protected open fun Printer.appendSignatures(clazz: KmClass) = Unit
|
||||
protected open fun Printer.appendSignatures(constructor: KmConstructor) = Unit
|
||||
protected open fun Printer.appendSignatures(function: KmFunction) = Unit
|
||||
protected open fun Printer.appendSignatures(property: KmProperty) = Unit
|
||||
protected open fun Printer.appendGetterSignatures(property: KmProperty) = Unit
|
||||
protected open fun Printer.appendSetterSignatures(property: KmProperty) = Unit
|
||||
protected open fun Printer.appendSignatures(typeAlias: KmTypeAlias) = Unit
|
||||
|
||||
protected open fun Printer.appendOrigin(clazz: KmClass) = Unit
|
||||
protected open fun Printer.appendOrigin(function: KmFunction) = Unit
|
||||
|
||||
protected abstract fun Printer.appendEnumEntries(clazz: KmClass)
|
||||
|
||||
protected open fun Printer.appendCustomAttributes(clazz: KmClass) = Unit
|
||||
protected open fun Printer.appendCustomAttributes(pkg: KmPackage) = Unit
|
||||
protected open fun Printer.appendCustomAttributes(property: KmProperty) = Unit
|
||||
|
||||
protected abstract fun Printer.appendCompileTimeConstant(property: KmProperty): Printer
|
||||
|
||||
protected open fun isRaw(type: KmType): Boolean = false
|
||||
protected open fun renderFlexibleTypeUpperBound(flexibleTypeUpperBound: KmFlexibleTypeUpperBound): String? = null
|
||||
}
|
||||
|
||||
data class KotlinpSettings(
|
||||
val isVerbose: Boolean,
|
||||
val sortDeclarations: Boolean
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2024 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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2024 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.
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,7 @@ object Main {
|
||||
}
|
||||
}
|
||||
|
||||
val kotlinp = Kotlinp(KotlinpSettings(isVerbose = verbose, sortDeclarations = sort))
|
||||
val kotlinp = JvmKotlinp(Settings(isVerbose = verbose, sortDeclarations = sort))
|
||||
|
||||
for (path in paths) {
|
||||
val file = File(path)
|
||||
@@ -43,8 +43,8 @@ object Main {
|
||||
|
||||
val text = try {
|
||||
when (file.extension) {
|
||||
"class" -> kotlinp.renderClassFile(kotlinp.readMetadata(kotlinp.readClassFile(file)))
|
||||
"kotlin_module" -> @OptIn(UnstableMetadataApi::class) kotlinp.renderModuleFile(kotlinp.readModuleFile(file))
|
||||
"class" -> kotlinp.printClassFile(readMetadata(readClassFile(file)))
|
||||
"kotlin_module" -> @OptIn(UnstableMetadataApi::class) kotlinp.printModuleFile(readModuleFile(file))
|
||||
else -> throw KotlinpException("only .class and .kotlin_module files are supported")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.kotlinp
|
||||
|
||||
inline fun printString(builder: StringBuilderPrinter.() -> Unit): String {
|
||||
return StringBuilderPrinter().apply(builder).toString()
|
||||
}
|
||||
|
||||
abstract class Printer : Appendable {
|
||||
private var indentLevel = 0
|
||||
private var commentsMode = false
|
||||
private var onEmptyLine = true
|
||||
|
||||
protected abstract val wrapped: Appendable
|
||||
|
||||
operator fun plusAssign(value: Any?) {
|
||||
append(value.toString())
|
||||
}
|
||||
|
||||
override fun append(charSequence: CharSequence, start: Int, end: Int): Printer = append(charSequence.subSequence(start, end))
|
||||
|
||||
override fun append(charSequence: CharSequence): Printer {
|
||||
for (ch in charSequence) append(ch)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun append(ch: Char): Printer {
|
||||
if (ch == '\n') {
|
||||
wrapped.append('\n')
|
||||
onEmptyLine = true
|
||||
} else {
|
||||
if (onEmptyLine) {
|
||||
onEmptyLine = false
|
||||
repeat(indentLevel) { wrapped.append(INDENT_UNIT) }
|
||||
if (commentsMode) wrapped.append("// ")
|
||||
}
|
||||
wrapped.append(ch)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun append(vararg values: Any?): Printer {
|
||||
for (value in values) {
|
||||
append(value.toString())
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun appendLine(vararg values: Any?): Printer {
|
||||
return append(*values).append('\n')
|
||||
}
|
||||
|
||||
inline fun <T> Printer.appendCollection(
|
||||
items: Collection<T>,
|
||||
separator: String = ", ",
|
||||
prefix: String = "",
|
||||
postfix: String = "",
|
||||
appendElement: Printer.(T) -> Unit,
|
||||
) {
|
||||
append(prefix)
|
||||
items.forEachIndexed { index, item ->
|
||||
if (index > 0) append(separator)
|
||||
appendElement(item)
|
||||
}
|
||||
append(postfix)
|
||||
}
|
||||
|
||||
inline fun <T> Printer.appendCollectionIfNotEmpty(
|
||||
items: Collection<T>,
|
||||
separator: String = ", ",
|
||||
prefix: String = "",
|
||||
postfix: String = "",
|
||||
appendElement: Printer.(T) -> Unit,
|
||||
) {
|
||||
if (items.isEmpty()) return
|
||||
appendCollection(items, separator, prefix, postfix, appendElement)
|
||||
}
|
||||
|
||||
fun appendFlags(vararg modifiers: Pair<Boolean, String>) {
|
||||
for ((condition, token) in modifiers) {
|
||||
if (condition) {
|
||||
append(token, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun appendCommentedLine(vararg values: Any?): Printer {
|
||||
commented { appendLine(*values) }
|
||||
return this
|
||||
}
|
||||
|
||||
fun commented(block: Printer.() -> Unit) {
|
||||
val hadCommentsMode = commentsMode
|
||||
try {
|
||||
commentsMode = true
|
||||
this.block()
|
||||
} finally {
|
||||
commentsMode = hadCommentsMode
|
||||
}
|
||||
}
|
||||
|
||||
fun withIndent(block: Printer.() -> Unit) {
|
||||
try {
|
||||
indentLevel++
|
||||
this.block()
|
||||
} finally {
|
||||
indentLevel--
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INDENT_UNIT = " "
|
||||
}
|
||||
}
|
||||
|
||||
class StringBuilderPrinter : Printer() {
|
||||
override val wrapped = StringBuilder()
|
||||
val isEmpty: Boolean get() = wrapped.isEmpty()
|
||||
override fun toString() = wrapped.toString()
|
||||
}
|
||||
|
||||
fun Printer(output: Appendable): Printer = DelegatingBuilder(output)
|
||||
|
||||
private class DelegatingBuilder(override val wrapped: Appendable) : Printer()
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.kotlinp
|
||||
|
||||
data class Settings(
|
||||
val isVerbose: Boolean,
|
||||
val sortDeclarations: Boolean
|
||||
)
|
||||
@@ -1,819 +0,0 @@
|
||||
/*
|
||||
* 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.kotlinp
|
||||
|
||||
import kotlinx.metadata.*
|
||||
import kotlinx.metadata.jvm.*
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
|
||||
private object SpecialCharacters {
|
||||
const val TYPE_ALIAS_MARKER = '^'
|
||||
}
|
||||
|
||||
fun printFunction(function: KmFunction, settings: KotlinpSettings): String {
|
||||
return StringBuilder().also { visitFunction(function, settings, it) }.toString()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContextReceivers::class, ExperimentalContracts::class)
|
||||
private fun visitFunction(
|
||||
function: KmFunction,
|
||||
settings: KotlinpSettings,
|
||||
sb: StringBuilder
|
||||
) {
|
||||
sb.appendLine()
|
||||
function.lambdaClassOriginName?.let {
|
||||
sb.appendLine(" // lambda class origin: $it")
|
||||
}
|
||||
function.versionRequirements.map(::printVersionRequirement).forEach { versionRequirement ->
|
||||
sb.appendLine(" // $versionRequirement")
|
||||
}
|
||||
function.signature?.let {
|
||||
sb.appendLine(" // signature: $it")
|
||||
}
|
||||
|
||||
if (function.contextReceiverTypes.isNotEmpty()) {
|
||||
sb.appendLine(function.contextReceiverTypes.joinToString(prefix = " context(", postfix = ")", transform = ::printType))
|
||||
}
|
||||
sb.append(" ")
|
||||
sb.appendFunctionModifiers(function)
|
||||
sb.append("fun ")
|
||||
if (function.typeParameters.isNotEmpty()) {
|
||||
function.typeParameters.joinTo(sb, prefix = "<", postfix = ">", transform = { printTypeParameter(it, settings) })
|
||||
sb.append(" ")
|
||||
}
|
||||
function.receiverParameterType?.let {
|
||||
sb.append(printType(it)).append(".")
|
||||
}
|
||||
sb.append(function.name)
|
||||
function.valueParameters.joinTo(sb, prefix = "(", postfix = ")", transform = ::printValueParameter)
|
||||
sb.append(": ").append(printType(function.returnType))
|
||||
sb.appendLine()
|
||||
function.contract?.let {
|
||||
sb.appendLine(" ${printContract(it)}")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContextReceivers::class)
|
||||
private fun visitProperty(
|
||||
property: KmProperty,
|
||||
settings: KotlinpSettings,
|
||||
sb: StringBuilder
|
||||
) {
|
||||
sb.appendLine()
|
||||
property.versionRequirements.map(::printVersionRequirement).forEach { versionRequirement ->
|
||||
sb.appendLine(" // $versionRequirement")
|
||||
}
|
||||
if (property.fieldSignature != null) {
|
||||
sb.appendLine(" // field: ${property.fieldSignature}")
|
||||
}
|
||||
if (property.getterSignature != null) {
|
||||
sb.appendLine(" // getter: ${property.getterSignature}")
|
||||
}
|
||||
if (property.setterSignature != null) {
|
||||
sb.appendLine(" // setter: ${property.setterSignature}")
|
||||
}
|
||||
if (property.syntheticMethodForAnnotations != null) {
|
||||
sb.appendLine(" // synthetic method for annotations: ${property.syntheticMethodForAnnotations}")
|
||||
}
|
||||
if (property.syntheticMethodForDelegate != null) {
|
||||
sb.appendLine(" // synthetic method for delegate: ${property.syntheticMethodForDelegate}")
|
||||
}
|
||||
if (property.isMovedFromInterfaceCompanion) {
|
||||
sb.appendLine(" // is moved from interface companion")
|
||||
}
|
||||
if (property.contextReceiverTypes.isNotEmpty()) {
|
||||
sb.appendLine(property.contextReceiverTypes.joinToString(prefix = " context(", postfix = ")", transform = ::printType))
|
||||
}
|
||||
sb.append(" ")
|
||||
sb.appendPropertyModifiers(property)
|
||||
sb.append(if (property.isVar) "var " else "val ")
|
||||
if (property.typeParameters.isNotEmpty()) {
|
||||
property.typeParameters.joinTo(sb, prefix = "<", postfix = ">", transform = { printTypeParameter(it, settings) })
|
||||
sb.append(" ")
|
||||
}
|
||||
property.receiverParameterType?.let {
|
||||
sb.append(printType(it)).append(".")
|
||||
}
|
||||
sb.append(property.name)
|
||||
sb.append(": ").append(property.returnType.let(::printType))
|
||||
if (property.hasConstant) {
|
||||
sb.append(" /* = ... */")
|
||||
}
|
||||
sb.appendLine()
|
||||
sb.append(" ")
|
||||
sb.appendPropertyAccessorModifiers(property.getter)
|
||||
sb.appendLine("get")
|
||||
val setter = property.setter
|
||||
if (setter != null) {
|
||||
sb.append(" ")
|
||||
sb.appendPropertyAccessorModifiers(setter)
|
||||
sb.append("set")
|
||||
property.setterParameter?.let {
|
||||
sb.append("(").append(printValueParameter(it)).append(")")
|
||||
}
|
||||
sb.appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun visitConstructor(constructor: KmConstructor, sb: StringBuilder) {
|
||||
sb.appendLine()
|
||||
constructor.versionRequirements.map(::printVersionRequirement).forEach { versionRequirement ->
|
||||
sb.appendLine(" // $versionRequirement")
|
||||
}
|
||||
if (constructor.signature != null) {
|
||||
sb.appendLine(" // signature: ${constructor.signature}")
|
||||
}
|
||||
sb.append(" ")
|
||||
sb.appendConstructorModifiers(constructor)
|
||||
sb.append("constructor(")
|
||||
constructor.valueParameters.joinTo(sb, transform = ::printValueParameter)
|
||||
sb.appendLine(")")
|
||||
}
|
||||
|
||||
private fun visitTypeAlias(
|
||||
typeAlias: KmTypeAlias,
|
||||
settings: KotlinpSettings,
|
||||
sb: StringBuilder
|
||||
) {
|
||||
sb.appendLine()
|
||||
typeAlias.versionRequirements.map(::printVersionRequirement).forEach { versionRequirement ->
|
||||
sb.appendLine(" // $versionRequirement")
|
||||
}
|
||||
typeAlias.annotations.forEach { annotation ->
|
||||
sb.append(" ").append("@").append(renderAnnotation(annotation)).appendLine()
|
||||
}
|
||||
sb.append(" ")
|
||||
sb.append(VISIBILITY_MAP[typeAlias.visibility])
|
||||
sb.append("typealias ").append(typeAlias.name)
|
||||
if (typeAlias.typeParameters.isNotEmpty()) {
|
||||
typeAlias.typeParameters.joinTo(sb, prefix = "<", postfix = ">") { printTypeParameter(it, settings) }
|
||||
}
|
||||
sb.append(" = ").append(typeAlias.underlyingType.let(::printType))
|
||||
sb.append(" /* = ").append(typeAlias.expandedType.let(::printType)).append(" */")
|
||||
sb.appendLine()
|
||||
}
|
||||
|
||||
fun printType(type: KmType): String {
|
||||
val classifier = when (val cls = type.classifier) {
|
||||
is KmClassifier.Class -> cls.name
|
||||
is KmClassifier.TypeParameter -> "T#${cls.id}"
|
||||
is KmClassifier.TypeAlias -> "${cls.name}${SpecialCharacters.TYPE_ALIAS_MARKER}"
|
||||
}
|
||||
|
||||
val arguments = mutableListOf<String>()
|
||||
type.arguments.forEach { argument ->
|
||||
arguments += if (argument == KmTypeProjection.STAR) {
|
||||
"*"
|
||||
} else {
|
||||
val (variance, argumentType) = argument
|
||||
if (variance == null || argumentType == null)
|
||||
throw IllegalArgumentException("Variance and type must be set for non-star type projection")
|
||||
val argumentTypeString = printType(argumentType)
|
||||
buildString {
|
||||
if (variance != KmVariance.INVARIANT) {
|
||||
append(variance.name.lowercase()).append(" ")
|
||||
}
|
||||
append(argumentTypeString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val abbreviatedType = type.abbreviatedType?.let(::printType)
|
||||
val outerType = type.outerType?.let(::printType)
|
||||
val platformTypeUpperBound = type.flexibleTypeUpperBound?.let {
|
||||
@Suppress("DEPRECATION_ERROR")
|
||||
(if (it.typeFlexibilityId == JvmTypeExtensionVisitor.PLATFORM_TYPE_ID) {
|
||||
printType(it.type)
|
||||
} else null)
|
||||
}
|
||||
|
||||
return buildString {
|
||||
for (annotation in type.annotations) {
|
||||
append("@").append(renderAnnotation(annotation)).append(" ")
|
||||
}
|
||||
if (type.isRaw) {
|
||||
append("/* raw */ ")
|
||||
}
|
||||
appendFlags(type.isSuspend to "suspend")
|
||||
if (outerType != null) {
|
||||
append(outerType).append(".").append(classifier.substringAfterLast('.'))
|
||||
} else {
|
||||
append(classifier)
|
||||
}
|
||||
if (arguments.isNotEmpty()) {
|
||||
arguments.joinTo(this, prefix = "<", postfix = ">")
|
||||
}
|
||||
if (type.isNullable) {
|
||||
append("?")
|
||||
}
|
||||
if (type.isDefinitelyNonNull) {
|
||||
append(" & Any")
|
||||
}
|
||||
if (abbreviatedType != null) {
|
||||
append(" /* = ").append(abbreviatedType).append(" */")
|
||||
}
|
||||
|
||||
if (platformTypeUpperBound == "$this?") {
|
||||
append("!")
|
||||
} else if (platformTypeUpperBound != null) {
|
||||
append("..").append(platformTypeUpperBound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun printTypeParameter(
|
||||
typeParameter: KmTypeParameter,
|
||||
settings: KotlinpSettings
|
||||
): String = buildString {
|
||||
appendFlags(typeParameter.isReified to "reified")
|
||||
for (annotation in typeParameter.annotations) {
|
||||
append("@").append(renderAnnotation(annotation)).append(" ")
|
||||
}
|
||||
if (typeParameter.variance != KmVariance.INVARIANT) {
|
||||
append(typeParameter.variance.name.lowercase()).append(" ")
|
||||
}
|
||||
append("T#${typeParameter.id}")
|
||||
if (settings.isVerbose) {
|
||||
append(" /* ${typeParameter.name} */")
|
||||
}
|
||||
if (typeParameter.upperBounds.isNotEmpty()) {
|
||||
typeParameter.upperBounds.joinTo(this, separator = " & ", prefix = " : ", transform = ::printType)
|
||||
}
|
||||
}
|
||||
|
||||
fun printValueParameter(
|
||||
valueParameter: KmValueParameter
|
||||
): String {
|
||||
val type = printType(valueParameter.type)
|
||||
val varargElementType = valueParameter.varargElementType?.let(::printType)
|
||||
return buildString {
|
||||
appendValueParameterModifiers(valueParameter)
|
||||
if (varargElementType != null) {
|
||||
append("vararg ").append(valueParameter.name).append(": ").append(varargElementType).append(" /* ").append(type).append(" */")
|
||||
} else {
|
||||
append(valueParameter.name).append(": ").append(type)
|
||||
}
|
||||
if (valueParameter.declaresDefaultValue) {
|
||||
append(" /* = ... */")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderAnnotation(annotation: KmAnnotation): String =
|
||||
annotation.className + if (annotation.arguments.isEmpty()) "" else
|
||||
annotation.arguments.entries.joinToString(prefix = "(", postfix = ")") { (name, argument) ->
|
||||
"$name = ${renderAnnotationArgument(argument)}"
|
||||
}
|
||||
|
||||
private fun renderAnnotationArgument(arg: KmAnnotationArgument): String =
|
||||
when (arg) {
|
||||
is KmAnnotationArgument.ByteValue -> arg.value.toString() + ".toByte()"
|
||||
is KmAnnotationArgument.CharValue -> "'${arg.value.toString().sanitize(quote = '\'')}'"
|
||||
is KmAnnotationArgument.ShortValue -> arg.value.toString() + ".toShort()"
|
||||
is KmAnnotationArgument.IntValue -> arg.value.toString()
|
||||
is KmAnnotationArgument.LongValue -> arg.value.toString() + "L"
|
||||
is KmAnnotationArgument.FloatValue -> arg.value.toString() + "f"
|
||||
is KmAnnotationArgument.DoubleValue -> arg.value.toString()
|
||||
is KmAnnotationArgument.UByteValue -> arg.value.toString() + ".toUByte()"
|
||||
is KmAnnotationArgument.UShortValue -> arg.value.toString() + ".toUShort()"
|
||||
is KmAnnotationArgument.UIntValue -> arg.value.toString() + "u"
|
||||
is KmAnnotationArgument.ULongValue -> arg.value.toString() + "uL"
|
||||
is KmAnnotationArgument.BooleanValue -> arg.value.toString()
|
||||
is KmAnnotationArgument.StringValue -> "\"${arg.value.sanitize(quote = '"')}\""
|
||||
is KmAnnotationArgument.KClassValue -> "${arg.className}::class"
|
||||
is KmAnnotationArgument.ArrayKClassValue -> buildString {
|
||||
repeat(arg.arrayDimensionCount) { append("kotlin/Array<") }
|
||||
append(arg.className).append("::class")
|
||||
repeat(arg.arrayDimensionCount) { append(">") }
|
||||
}
|
||||
is KmAnnotationArgument.EnumValue -> "${arg.enumClassName}.${arg.enumEntryName}"
|
||||
is KmAnnotationArgument.AnnotationValue -> arg.annotation.let { annotation ->
|
||||
val args = annotation.arguments.entries.joinToString { (name, argument) ->
|
||||
"$name = ${renderAnnotationArgument(argument)}"
|
||||
}
|
||||
"${annotation.className}($args)"
|
||||
}
|
||||
is KmAnnotationArgument.ArrayValue -> arg.elements.joinToString(
|
||||
prefix = "[",
|
||||
postfix = "]",
|
||||
transform = ::renderAnnotationArgument
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.sanitize(quote: Char): String =
|
||||
buildString(length) {
|
||||
for (c in this@sanitize) {
|
||||
when (c) {
|
||||
'\n' -> append("\\n")
|
||||
'\r' -> append("\\r")
|
||||
'\t' -> append("\\t")
|
||||
quote -> append("\\").append(quote)
|
||||
else -> append(if (c.isISOControl()) "\\u%04x".format(c.code) else c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun printVersionRequirement(versionRequirement: KmVersionRequirement): String {
|
||||
val version = with(versionRequirement.version) { "$major.$minor.$patch" }
|
||||
|
||||
return buildString {
|
||||
append("requires ").append(
|
||||
when (versionRequirement.kind) {
|
||||
KmVersionRequirementVersionKind.LANGUAGE_VERSION -> "language version"
|
||||
KmVersionRequirementVersionKind.COMPILER_VERSION -> "compiler version"
|
||||
KmVersionRequirementVersionKind.API_VERSION -> "API version"
|
||||
KmVersionRequirementVersionKind.UNKNOWN -> "unknown requirement"
|
||||
}
|
||||
).append(" ").append(version)
|
||||
|
||||
listOfNotNull(
|
||||
"level=${versionRequirement.level}",
|
||||
versionRequirement.errorCode?.let { "errorCode=$it" },
|
||||
versionRequirement.message?.let { "message=\"$it\"" }
|
||||
).joinTo(this, prefix = " (", postfix = ")")
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendDeclarationContainerExtensions(
|
||||
settings: KotlinpSettings,
|
||||
localDelegatedProperties: List<StringBuilder>,
|
||||
moduleName: String?
|
||||
) {
|
||||
for ((i, sb) in localDelegatedProperties.withIndex()) {
|
||||
appendLine()
|
||||
appendLine(" // local delegated property #$i")
|
||||
for (line in sb.lineSequence()) {
|
||||
if (line.isBlank()) continue
|
||||
// Comment all uncommented lines to not make it look like these properties are declared here
|
||||
appendLine(
|
||||
if (line.startsWith(" ") && !line.startsWith(" //")) line.replaceFirst(" ", " // ")
|
||||
else line
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.isVerbose && moduleName != null) {
|
||||
appendLine()
|
||||
appendLine(" // module name: $moduleName")
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T, R : Comparable<R>> Iterable<T>.sortIfNeededBy(settings: KotlinpSettings, selector: (T) -> R?): Iterable<T> {
|
||||
return if (settings.sortDeclarations) sortedBy(selector) else this
|
||||
}
|
||||
|
||||
@ExperimentalContracts
|
||||
fun printContract(kmContract: KmContract): String = buildString {
|
||||
appendLine("contract {")
|
||||
kmContract.effects.map(::printEffect).forEach { effect ->
|
||||
appendLine(" $effect")
|
||||
}
|
||||
append(" }")
|
||||
}
|
||||
|
||||
@ExperimentalContracts
|
||||
private fun printEffect(
|
||||
kmEffect: KmEffect
|
||||
): String {
|
||||
var argument: String? = null
|
||||
kmEffect.constructorArguments.forEach {
|
||||
// If there are several arguments, only the first is taken, see ContractDeserializerImpl.deserializeSimpleEffect
|
||||
if (argument == null) {
|
||||
argument = printEffectExpression(it)
|
||||
}
|
||||
}
|
||||
val conclusion: String? = kmEffect.conclusion?.let(::printEffectExpression)
|
||||
|
||||
return buildString {
|
||||
when (kmEffect.type) {
|
||||
KmEffectType.RETURNS_CONSTANT -> {
|
||||
append("returns(")
|
||||
if (argument != null) {
|
||||
append(argument)
|
||||
}
|
||||
append(")")
|
||||
}
|
||||
|
||||
KmEffectType.CALLS -> {
|
||||
append("callsInPlace($argument")
|
||||
kmEffect.invocationKind?.let {
|
||||
append(", InvocationKind.${it.name}")
|
||||
}
|
||||
append(")")
|
||||
}
|
||||
|
||||
KmEffectType.RETURNS_NOT_NULL -> {
|
||||
append("returnsNotNull()")
|
||||
}
|
||||
}
|
||||
if (conclusion != null) {
|
||||
append(" implies ($conclusion)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalContracts
|
||||
private fun printEffectExpression(effectExpression: KmEffectExpression): String {
|
||||
val parameterIndex: Int? = effectExpression.parameterIndex
|
||||
val constantValue: List<Any?>? = effectExpression.constantValue?.let { listOf(it.value) }
|
||||
val andArguments = effectExpression.andArguments.map(::printEffectExpression)
|
||||
val orArguments = effectExpression.orArguments.map(::printEffectExpression)
|
||||
|
||||
fun wrapIfNeeded(s: String): String =
|
||||
// A simple heuristic to avoid wrapping into unnecessary parentheses
|
||||
if ('&' in s || '|' in s) "($s)" else s
|
||||
|
||||
return buildString {
|
||||
append(
|
||||
when {
|
||||
constantValue != null -> constantValue.single().toString()
|
||||
parameterIndex != null -> "p#$parameterIndex"
|
||||
else -> ""
|
||||
}
|
||||
)
|
||||
if (effectExpression.isInstanceType != null) {
|
||||
append(" ")
|
||||
if (effectExpression.isNegated) append("!")
|
||||
append("is ${effectExpression.isInstanceType?.let(::printType)}")
|
||||
}
|
||||
if (effectExpression.isNullCheckPredicate) {
|
||||
append(if (effectExpression.isNegated) " != " else " == ")
|
||||
append("null")
|
||||
}
|
||||
|
||||
if (orArguments.isEmpty()) {
|
||||
for (andArgument in andArguments) {
|
||||
if (!isEmpty()) append(" && ")
|
||||
append(wrapIfNeeded(andArgument))
|
||||
}
|
||||
}
|
||||
if (andArguments.isEmpty()) {
|
||||
for (orArgument in orArguments) {
|
||||
if (!isEmpty()) append(" || ")
|
||||
append(wrapIfNeeded(orArgument))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AbstractPrinter<in T : KotlinClassMetadata> {
|
||||
fun print(klass: T): String
|
||||
}
|
||||
|
||||
class ClassPrinter(private val settings: KotlinpSettings) : AbstractPrinter<KotlinClassMetadata.Class> {
|
||||
private val sb = StringBuilder()
|
||||
internal val result = StringBuilder()
|
||||
|
||||
private var klass: KmClass? = null
|
||||
private var name: ClassName? = null
|
||||
private val typeParams = mutableListOf<String>()
|
||||
private val supertypes = mutableListOf<String>()
|
||||
private val contextReceiverTypes = mutableListOf<String>()
|
||||
private val versionRequirements = mutableListOf<String>()
|
||||
private var anonymousObjectOriginName: String? = null
|
||||
|
||||
private fun visitEnd() {
|
||||
if (anonymousObjectOriginName != null) {
|
||||
result.appendLine("// anonymous object origin: $anonymousObjectOriginName")
|
||||
}
|
||||
for (versionRequirement in versionRequirements) {
|
||||
result.appendLine("// $versionRequirement")
|
||||
}
|
||||
if (contextReceiverTypes.isNotEmpty()) {
|
||||
result.appendLine(contextReceiverTypes.joinToString(prefix = "context(", postfix = ")"))
|
||||
}
|
||||
result.appendClassModifiers(klass!!)
|
||||
result.append(name)
|
||||
if (typeParams.isNotEmpty()) {
|
||||
typeParams.joinTo(result, prefix = "<", postfix = ">")
|
||||
}
|
||||
if (supertypes.isNotEmpty()) {
|
||||
result.append(" : ")
|
||||
supertypes.joinTo(result)
|
||||
}
|
||||
result.appendLine(" {")
|
||||
if (klass!!.hasEnumEntries) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // has Enum.entries")
|
||||
}
|
||||
result.append(sb)
|
||||
result.appendLine("}")
|
||||
}
|
||||
|
||||
private fun visitCompanionObject(name: String) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // companion object: $name")
|
||||
}
|
||||
|
||||
private fun visitNestedClass(name: String) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // nested class: $name")
|
||||
}
|
||||
|
||||
private fun visitEnumEntry(name: String) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" $name,")
|
||||
}
|
||||
|
||||
private fun visitSealedSubclass(name: ClassName) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // sealed subclass: $name")
|
||||
}
|
||||
|
||||
private fun visitInlineClassUnderlyingPropertyName(name: String) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // underlying property: $name")
|
||||
}
|
||||
|
||||
private fun visitInlineClassUnderlyingType(type: String) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // underlying type: $type")
|
||||
}
|
||||
|
||||
private fun visitExtensions(kclass: KmClass) {
|
||||
val localDelegatedProperties = mutableListOf<StringBuilder>()
|
||||
val moduleName: String? = kclass.moduleName
|
||||
anonymousObjectOriginName = kclass.anonymousObjectOriginName
|
||||
|
||||
kclass.localDelegatedProperties.sortIfNeededBy(settings) { it.getterSignature?.toString() ?: it.name }.forEach { p ->
|
||||
visitProperty(
|
||||
p, settings, StringBuilder().also { localDelegatedProperties.add(it) }
|
||||
)
|
||||
}
|
||||
|
||||
sb.appendDeclarationContainerExtensions(settings, localDelegatedProperties, moduleName)
|
||||
if (kclass.hasMethodBodiesInInterface) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // has method bodies in interface")
|
||||
}
|
||||
if (kclass.isCompiledInCompatibilityMode) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // is compiled in compatibility mode")
|
||||
}
|
||||
}
|
||||
|
||||
override fun print(klass: KotlinClassMetadata.Class): String = print(klass.kmClass)
|
||||
|
||||
@OptIn(ExperimentalContextReceivers::class)
|
||||
fun print(kmClass: KmClass): String {
|
||||
klass = kmClass
|
||||
name = kmClass.name
|
||||
kmClass.typeParameters.forEach { typeParams.add(printTypeParameter(it, settings)) }
|
||||
supertypes.addAll(kmClass.supertypes.map { printType(it) })
|
||||
|
||||
kmClass.constructors.sortIfNeededBy(settings) { it.signature.toString() }.forEach { visitConstructor(it, sb) }
|
||||
kmClass.functions.sortIfNeededBy(settings) { it.signature.toString() }.forEach { visitFunction(it, settings, sb) }
|
||||
kmClass.properties.sortIfNeededBy(settings) {
|
||||
it.getterSignature?.toString() ?: it.name
|
||||
}.forEach { visitProperty(it, settings, sb) }
|
||||
kmClass.typeAliases.sortIfNeededBy(settings) { it.name }.forEach { visitTypeAlias(it, settings, sb) }
|
||||
kmClass.companionObject?.let { visitCompanionObject(it) }
|
||||
kmClass.nestedClasses.forEach { visitNestedClass(it) }
|
||||
kmClass.enumEntries.forEach { visitEnumEntry(it) }
|
||||
kmClass.sealedSubclasses.sortIfNeededBy(settings) { it }.forEach { visitSealedSubclass(it) }
|
||||
kmClass.inlineClassUnderlyingPropertyName?.let { visitInlineClassUnderlyingPropertyName(it) }
|
||||
kmClass.inlineClassUnderlyingType?.let { visitInlineClassUnderlyingType(printType(it)) }
|
||||
kmClass.contextReceiverTypes.forEach { contextReceiverTypes.add(printType(it)) }
|
||||
kmClass.versionRequirements.forEach { versionRequirements.add(printVersionRequirement(it)) }
|
||||
|
||||
visitExtensions(kmClass)
|
||||
visitEnd()
|
||||
return result.toString()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PackagePrinter(private val settings: KotlinpSettings) {
|
||||
internal val sb = StringBuilder().apply {
|
||||
appendLine("package {")
|
||||
}
|
||||
|
||||
private fun visitExtensions(kmPackage: KmPackage) {
|
||||
val localDelegatedProperties = mutableListOf<StringBuilder>()
|
||||
val moduleName: String? = kmPackage.moduleName
|
||||
|
||||
kmPackage.localDelegatedProperties.sortIfNeededBy(settings) { it.getterSignature?.toString() ?: it.name }.forEach { p ->
|
||||
visitProperty(p, settings, StringBuilder().also { localDelegatedProperties.add(it) })
|
||||
}
|
||||
sb.appendDeclarationContainerExtensions(settings, localDelegatedProperties, moduleName)
|
||||
}
|
||||
|
||||
fun print(kmPackage: KmPackage) {
|
||||
kmPackage.functions.sortIfNeededBy(settings) { it.signature.toString() }.forEach { visitFunction(it, settings, sb) }
|
||||
kmPackage.properties.sortIfNeededBy(settings) {
|
||||
it.getterSignature?.toString() ?: it.name
|
||||
}.forEach { visitProperty(it, settings, sb) }
|
||||
kmPackage.typeAliases.sortIfNeededBy(settings) { it.name }.forEach { visitTypeAlias(it, settings, sb) }
|
||||
visitExtensions(kmPackage)
|
||||
sb.appendLine("}")
|
||||
}
|
||||
}
|
||||
|
||||
class FileFacadePrinter(settings: KotlinpSettings) : PackagePrinter(settings), AbstractPrinter<KotlinClassMetadata.FileFacade> {
|
||||
override fun print(klass: KotlinClassMetadata.FileFacade): String {
|
||||
print(klass.kmPackage)
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class LambdaPrinter(private val settings: KotlinpSettings) : AbstractPrinter<KotlinClassMetadata.SyntheticClass> {
|
||||
override fun print(klass: KotlinClassMetadata.SyntheticClass): String {
|
||||
val sb = StringBuilder().apply {
|
||||
appendLine("lambda {")
|
||||
}
|
||||
val kLambda = klass.kmLambda ?: throw KotlinpException("Synthetic class $klass is not a lambda")
|
||||
visitFunction(kLambda.function, settings, sb)
|
||||
sb.appendLine("}")
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class MultiFileClassPartPrinter(
|
||||
settings: KotlinpSettings
|
||||
) : PackagePrinter(settings), AbstractPrinter<KotlinClassMetadata.MultiFileClassPart> {
|
||||
override fun print(klass: KotlinClassMetadata.MultiFileClassPart): String {
|
||||
sb.appendLine(" // facade: ${klass.facadeClassName}")
|
||||
print(klass.kmPackage)
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
|
||||
class MultiFileClassFacadePrinter : AbstractPrinter<KotlinClassMetadata.MultiFileClassFacade> {
|
||||
override fun print(klass: KotlinClassMetadata.MultiFileClassFacade): String =
|
||||
buildString {
|
||||
appendLine("multi-file class {")
|
||||
for (part in klass.partClassNames) {
|
||||
appendLine(" // $part")
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
}
|
||||
|
||||
class ModuleFilePrinter(private val settings: KotlinpSettings) {
|
||||
private val optionalAnnotations = mutableListOf<String>()
|
||||
|
||||
private val sb = StringBuilder().apply {
|
||||
appendLine("module {")
|
||||
}
|
||||
|
||||
private fun visitPackageParts(fqName: String, fileFacades: List<String>, multiFileClassParts: Map<String, String>) {
|
||||
val presentableFqName = fqName.ifEmpty { "<root>" }
|
||||
sb.appendLine(" package $presentableFqName {")
|
||||
for (fileFacade in fileFacades) {
|
||||
sb.appendLine(" $fileFacade")
|
||||
}
|
||||
for ((multiFileClassPart, facade) in multiFileClassParts) {
|
||||
sb.appendLine(" $multiFileClassPart ($facade)")
|
||||
}
|
||||
sb.appendLine(" }")
|
||||
}
|
||||
|
||||
private fun visitEnd() {
|
||||
if (optionalAnnotations.isNotEmpty()) {
|
||||
sb.appendLine()
|
||||
sb.appendLine(" // Optional annotations")
|
||||
sb.appendLine()
|
||||
for (element in optionalAnnotations) {
|
||||
sb.appendLine(" " + element.replace("\n", "\n ").trimEnd())
|
||||
}
|
||||
}
|
||||
sb.appendLine("}")
|
||||
}
|
||||
|
||||
@UnstableMetadataApi
|
||||
fun print(metadata: KotlinModuleMetadata): String {
|
||||
val kmModule = metadata.kmModule
|
||||
kmModule.packageParts.forEach { (fqName, kmPackageParts) ->
|
||||
visitPackageParts(fqName, kmPackageParts.fileFacades, kmPackageParts.multiFileClassParts)
|
||||
}
|
||||
// kmModule.annotations.forEach { visitAnnotation(it) } TODO
|
||||
optionalAnnotations.addAll(kmModule.optionalAnnotationClasses.map { ClassPrinter(settings).print(it) })
|
||||
visitEnd()
|
||||
return sb.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private val VISIBILITY_MAP = mapOf(
|
||||
Visibility.INTERNAL to "internal ",
|
||||
Visibility.PRIVATE to "private ",
|
||||
Visibility.PRIVATE_TO_THIS to "private ",
|
||||
Visibility.PROTECTED to "protected ",
|
||||
Visibility.PUBLIC to "public ",
|
||||
Visibility.LOCAL to "local "
|
||||
)
|
||||
|
||||
private val MODALITY_MAP = mapOf(
|
||||
Modality.FINAL to "final ",
|
||||
Modality.OPEN to "open ",
|
||||
Modality.ABSTRACT to "abstract ",
|
||||
Modality.SEALED to "sealed "
|
||||
)
|
||||
|
||||
private val CLASS_KIND_MAP = mapOf(
|
||||
ClassKind.CLASS to "class ",
|
||||
ClassKind.INTERFACE to "interface ",
|
||||
ClassKind.ENUM_CLASS to "enum class ",
|
||||
ClassKind.ENUM_ENTRY to "enum entry ",
|
||||
ClassKind.ANNOTATION_CLASS to "annotation class ",
|
||||
ClassKind.OBJECT to "object ",
|
||||
ClassKind.COMPANION_OBJECT to "companion object "
|
||||
)
|
||||
|
||||
private val MEMBER_KIND_MAP = mapOf(
|
||||
MemberKind.DECLARATION to "",
|
||||
MemberKind.FAKE_OVERRIDE to "/* fake override */ ",
|
||||
MemberKind.DELEGATION to "/* delegation */ ",
|
||||
MemberKind.SYNTHESIZED to "/* synthesized */ ",
|
||||
)
|
||||
|
||||
private fun StringBuilder.appendFlags(vararg modifiers: Pair<Boolean, String>) = modifiers.forEach { (condition, s) ->
|
||||
if (condition) {
|
||||
append(s)
|
||||
if (s.isNotEmpty()) append(" ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendClassModifiers(kmClass: KmClass) {
|
||||
append(VISIBILITY_MAP[kmClass.visibility])
|
||||
append(MODALITY_MAP[kmClass.modality])
|
||||
appendFlags(
|
||||
kmClass.isInner to "inner",
|
||||
kmClass.isData to "data",
|
||||
kmClass.isExternal to "external",
|
||||
kmClass.isExpect to "expect",
|
||||
kmClass.isValue to "value",
|
||||
kmClass.isFunInterface to "fun",
|
||||
)
|
||||
append(CLASS_KIND_MAP[kmClass.kind])
|
||||
}
|
||||
|
||||
fun printConstructorModifiers(kmConstructor: KmConstructor) = buildString {
|
||||
append(VISIBILITY_MAP[kmConstructor.visibility])
|
||||
appendFlags(
|
||||
kmConstructor.isSecondary to "/* secondary */",
|
||||
kmConstructor.hasNonStableParameterNames to "/* non-stable parameter names */"
|
||||
)
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendConstructorModifiers(kmConstructor: KmConstructor) {
|
||||
append(printConstructorModifiers(kmConstructor))
|
||||
}
|
||||
|
||||
fun printFunctionModifiers(kmFunction: KmFunction) = buildString {
|
||||
append(VISIBILITY_MAP[kmFunction.visibility])
|
||||
append(MODALITY_MAP[kmFunction.modality])
|
||||
append(MEMBER_KIND_MAP[kmFunction.kind])
|
||||
appendFlags(
|
||||
kmFunction.isOperator to "operator",
|
||||
kmFunction.isInfix to "infix",
|
||||
kmFunction.isInline to "inline",
|
||||
kmFunction.isTailrec to "tailrec",
|
||||
kmFunction.isExternal to "external",
|
||||
kmFunction.isSuspend to "suspend",
|
||||
kmFunction.isExpect to "expect",
|
||||
kmFunction.hasNonStableParameterNames to "/* non-stable parameter names */"
|
||||
)
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendFunctionModifiers(kmFunction: KmFunction) {
|
||||
append(printFunctionModifiers(kmFunction))
|
||||
}
|
||||
|
||||
fun printPropertyModifiers(kmProperty: KmProperty) = buildString {
|
||||
append(VISIBILITY_MAP[kmProperty.visibility])
|
||||
append(MODALITY_MAP[kmProperty.modality])
|
||||
append(MEMBER_KIND_MAP[kmProperty.kind])
|
||||
appendFlags(
|
||||
kmProperty.isConst to "const",
|
||||
kmProperty.isLateinit to "lateinit",
|
||||
kmProperty.isExternal to "external",
|
||||
kmProperty.isDelegated to "/* delegated */",
|
||||
kmProperty.isExpect to "expect"
|
||||
)
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendPropertyModifiers(kmProperty: KmProperty) {
|
||||
append(printPropertyModifiers(kmProperty))
|
||||
}
|
||||
|
||||
fun printPropertyAccessorModifiers(accessorAttributes: KmPropertyAccessorAttributes) = buildString {
|
||||
append(VISIBILITY_MAP[accessorAttributes.visibility])
|
||||
append(MODALITY_MAP[accessorAttributes.modality])
|
||||
appendFlags(
|
||||
accessorAttributes.isNotDefault to "/* non-default */",
|
||||
accessorAttributes.isExternal to "external",
|
||||
accessorAttributes.isInline to "inline"
|
||||
)
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendPropertyAccessorModifiers(accessorAttributes: KmPropertyAccessorAttributes) {
|
||||
append(printPropertyAccessorModifiers(accessorAttributes))
|
||||
}
|
||||
|
||||
private fun StringBuilder.appendValueParameterModifiers(valueParameter: KmValueParameter) = appendFlags(
|
||||
valueParameter.isCrossinline to "crossinline",
|
||||
valueParameter.isNoinline to "noinline"
|
||||
)
|
||||
+20
-3
@@ -1,17 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Copyright 2010-2024 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.kotlinp
|
||||
|
||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||
import kotlinx.metadata.jvm.KotlinModuleMetadata
|
||||
import kotlinx.metadata.jvm.Metadata
|
||||
import kotlinx.metadata.jvm.UnstableMetadataApi
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
internal fun File.readKotlinClassHeader(): Metadata? = ClassReader(FileInputStream(this)).readKotlinClassHeader()
|
||||
|
||||
fun ClassReader.readKotlinClassHeader(): Metadata? {
|
||||
var header: Metadata? = null
|
||||
|
||||
@@ -74,3 +75,19 @@ private fun readMetadataVisitor(output: (Metadata) -> Unit): AnnotationVisitor =
|
||||
output(Metadata(kind, metadataVersion, data1, data2, extraString, packageName, extraInt))
|
||||
}
|
||||
}
|
||||
|
||||
internal fun readClassFile(file: File): Metadata {
|
||||
return ClassReader(FileInputStream(file)).readKotlinClassHeader() ?: throw KotlinpException("file is not a Kotlin class file: $file")
|
||||
}
|
||||
|
||||
internal fun readMetadata(metadata: Metadata): KotlinClassMetadata {
|
||||
return try {
|
||||
KotlinClassMetadata.readLenient(metadata)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw KotlinpException("inconsistent Kotlin metadata: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(UnstableMetadataApi::class)
|
||||
internal fun readModuleFile(file: File): KotlinModuleMetadata? =
|
||||
runCatching { KotlinModuleMetadata.read(file.readBytes()) }.getOrNull()
|
||||
@@ -17,8 +17,10 @@ import org.jetbrains.kotlin.codegen.GenerationUtils
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.jvm.compiler.AbstractLoadJavaTest
|
||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
||||
import org.jetbrains.kotlin.kotlinp.Settings
|
||||
import org.jetbrains.kotlin.kotlinp.JvmKotlinp
|
||||
import org.jetbrains.kotlin.kotlinp.readClassFile
|
||||
import org.jetbrains.kotlin.kotlinp.readModuleFile
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
@@ -62,24 +64,24 @@ private fun compileAndPrintAllFiles(
|
||||
val main = StringBuilder()
|
||||
val afterNodes = StringBuilder()
|
||||
|
||||
val kotlinp = Kotlinp(KotlinpSettings(isVerbose = true, sortDeclarations = true))
|
||||
val kotlinp = JvmKotlinp(Settings(isVerbose = true, sortDeclarations = true))
|
||||
|
||||
@OptIn(UnstableMetadataApi::class)
|
||||
compile(file, disposable, tmpdir, useK2) { outputFile ->
|
||||
when (outputFile.extension) {
|
||||
"kotlin_module" -> {
|
||||
val moduleFile = kotlinp.readModuleFile(outputFile)!!
|
||||
val moduleFile = readModuleFile(outputFile)!!
|
||||
val transformedWithNodes = KotlinModuleMetadata.read(moduleFile.write())
|
||||
|
||||
for ((sb, moduleFileToRender) in listOf(
|
||||
main to moduleFile, afterNodes to transformedWithNodes
|
||||
)) {
|
||||
sb.appendFileName(outputFile.relativeTo(tmpdir))
|
||||
sb.append(kotlinp.renderModuleFile(moduleFileToRender))
|
||||
sb.append(kotlinp.printModuleFile(moduleFileToRender))
|
||||
}
|
||||
}
|
||||
"class" -> {
|
||||
val metadata = kotlinp.readClassFile(outputFile)
|
||||
val metadata = readClassFile(outputFile)
|
||||
val classFile = KotlinClassMetadata.readStrict(metadata)
|
||||
val classFile2 = KotlinClassMetadata.readStrict(classFile.write())
|
||||
|
||||
@@ -87,7 +89,7 @@ private fun compileAndPrintAllFiles(
|
||||
main to classFile, afterNodes to classFile2
|
||||
)) {
|
||||
sb.appendFileName(outputFile.relativeTo(tmpdir))
|
||||
sb.append(kotlinp.renderClassFile(classFileToRender))
|
||||
sb.append(kotlinp.printClassFile(classFileToRender))
|
||||
}
|
||||
}
|
||||
else -> fail("Unknown file: $outputFile")
|
||||
|
||||
@@ -8,8 +8,8 @@ package org.jetbrains.kotlin.kapt3.test.handlers
|
||||
import com.sun.tools.javac.tree.JCTree.*
|
||||
import com.sun.tools.javac.tree.Pretty
|
||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
||||
import org.jetbrains.kotlin.kotlinp.Settings
|
||||
import org.jetbrains.kotlin.kotlinp.JvmKotlinp
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.test.Assertions
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
@@ -37,7 +37,7 @@ fun renderMetadata(pretty: Pretty, tree: JCAnnotation): String {
|
||||
extraString = args[JvmAnnotationNames.METADATA_EXTRA_STRING_FIELD_NAME].stringValue() ?: "",
|
||||
packageName = args[JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME].stringValue() ?: "",
|
||||
)
|
||||
val text = Kotlinp(KotlinpSettings(isVerbose = true, sortDeclarations = true)).renderClassFile(KotlinClassMetadata.readStrict(metadata))
|
||||
val text = JvmKotlinp(Settings(isVerbose = true, sortDeclarations = true)).printClassFile(KotlinClassMetadata.readStrict(metadata))
|
||||
// "/*" and "*/" delimiters are used in kotlinp, for example to render type parameter names. Replace them with something else
|
||||
// to avoid them being interpreted as Java comments.
|
||||
val sanitized = text.split('\n').dropLast(1).map { it.replace("/*", "(*").replace("*/", "*)") }
|
||||
|
||||
@@ -22,8 +22,8 @@ import org.jetbrains.kotlin.kapt3.base.util.KaptLogger
|
||||
import org.jetbrains.kotlin.kapt3.base.util.WriterBackedKaptLogger
|
||||
import org.jetbrains.kotlin.kapt3.test.KaptMessageCollectorProvider
|
||||
import org.jetbrains.kotlin.kapt3.test.kaptOptionsProvider
|
||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
||||
import org.jetbrains.kotlin.kotlinp.Settings
|
||||
import org.jetbrains.kotlin.kotlinp.JvmKotlinp
|
||||
import org.jetbrains.kotlin.test.model.*
|
||||
import org.jetbrains.kotlin.test.services.*
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
@@ -125,7 +125,7 @@ internal data class Kapt4ContextBinaryArtifact(
|
||||
}
|
||||
|
||||
private fun Printer.renderMetadata(metadata: Metadata) {
|
||||
val text = Kotlinp(KotlinpSettings(isVerbose = true, sortDeclarations = true)).renderClassFile(KotlinClassMetadata.readLenient(metadata))
|
||||
val text = JvmKotlinp(Settings(isVerbose = true, sortDeclarations = true)).printClassFile(KotlinClassMetadata.readLenient(metadata))
|
||||
// "/*" and "*/" delimiters are used in kotlinp, for example to render type parameter names. Replace them with something else
|
||||
// to avoid them being interpreted as Java comments.
|
||||
val sanitized = text.split('\n')
|
||||
|
||||
Reference in New Issue
Block a user