[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 kotlinx.metadata.jvm.signature
|
||||||
import org.jetbrains.kotlin.abicmp.reports.MetadataPropertyReport
|
import org.jetbrains.kotlin.abicmp.reports.MetadataPropertyReport
|
||||||
import org.jetbrains.kotlin.abicmp.tasks.GenericMetadataTask
|
import org.jetbrains.kotlin.abicmp.tasks.GenericMetadataTask
|
||||||
import org.jetbrains.kotlin.kotlinp.*
|
|
||||||
|
|
||||||
fun loadProperties(container: KmDeclarationContainer) = container.properties.associateBy { it.getterSignature?.toString() ?: it.name }
|
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) =
|
fun loadLocalDelegatedProperties(clazz: KotlinClassMetadata.Class) =
|
||||||
clazz.kmClass.localDelegatedProperties.associateBy { it.getterSignature?.toString() ?: it.name }
|
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(
|
inline fun <R, T> checkMetadataMembers(
|
||||||
metadata1: R,
|
metadata1: R,
|
||||||
metadata2: R,
|
metadata2: R,
|
||||||
|
|||||||
+42
-18
@@ -5,11 +5,8 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.abicmp.tasks
|
package org.jetbrains.kotlin.abicmp.tasks
|
||||||
|
|
||||||
import kotlinx.metadata.ExperimentalContextReceivers
|
import kotlinx.metadata.*
|
||||||
import kotlinx.metadata.hasConstant
|
|
||||||
import kotlinx.metadata.isVar
|
|
||||||
import kotlinx.metadata.jvm.*
|
import kotlinx.metadata.jvm.*
|
||||||
import kotlinx.metadata.visibility
|
|
||||||
import org.jetbrains.kotlin.abicmp.*
|
import org.jetbrains.kotlin.abicmp.*
|
||||||
import org.jetbrains.kotlin.abicmp.checkers.*
|
import org.jetbrains.kotlin.abicmp.checkers.*
|
||||||
import org.jetbrains.kotlin.kotlinp.*
|
import org.jetbrains.kotlin.kotlinp.*
|
||||||
@@ -65,7 +62,7 @@ private val allFunctionMetadataCheckers = listOf(
|
|||||||
functionMetadataPropertyChecker("modifiers") { printFunctionModifiers(it) },
|
functionMetadataPropertyChecker("modifiers") { printFunctionModifiers(it) },
|
||||||
functionMetadataPropertyChecker("typeParameters") { it.typeParameters.stringifyTypeParameters() },
|
functionMetadataPropertyChecker("typeParameters") { it.typeParameters.stringifyTypeParameters() },
|
||||||
functionMetadataPropertyChecker("receiverParameterType") {
|
functionMetadataPropertyChecker("receiverParameterType") {
|
||||||
it.receiverParameterType?.let { type -> printType(type) } ?: PROPERTY_VAL_STUB
|
it.receiverParameterType?.let(::printType) ?: PROPERTY_VAL_STUB
|
||||||
},
|
},
|
||||||
functionMetadataPropertyChecker("valueParameters") { it.valueParameters.stringifyValueParameters() },
|
functionMetadataPropertyChecker("valueParameters") { it.valueParameters.stringifyValueParameters() },
|
||||||
functionMetadataPropertyChecker("contract") {
|
functionMetadataPropertyChecker("contract") {
|
||||||
@@ -90,13 +87,13 @@ private val allPropertyMetadataCheckers = listOf(
|
|||||||
propertyMetadataPropertyChecker("isVar") { it.isVar.toString() },
|
propertyMetadataPropertyChecker("isVar") { it.isVar.toString() },
|
||||||
propertyMetadataPropertyChecker("typeParameters") { it.typeParameters.stringifyTypeParameters() },
|
propertyMetadataPropertyChecker("typeParameters") { it.typeParameters.stringifyTypeParameters() },
|
||||||
propertyMetadataPropertyChecker("receiverParameterType") {
|
propertyMetadataPropertyChecker("receiverParameterType") {
|
||||||
it.receiverParameterType?.let { type -> printType(type) } ?: PROPERTY_VAL_STUB
|
it.receiverParameterType?.let(::printType) ?: PROPERTY_VAL_STUB
|
||||||
},
|
},
|
||||||
propertyMetadataPropertyChecker("returnType") { printType(it.returnType) },
|
propertyMetadataPropertyChecker("returnType") { printType(it.returnType) },
|
||||||
propertyMetadataPropertyChecker("hasConstant") { it.hasConstant.toString() },
|
propertyMetadataPropertyChecker("hasConstant") { it.hasConstant.toString() },
|
||||||
propertyMetadataPropertyChecker("getterModifiers") { printPropertyAccessorModifiers(it.getter) },
|
propertyMetadataPropertyChecker("getterModifiers") { printPropertyAccessorModifiers(it.getter) },
|
||||||
propertyMetadataPropertyChecker("setterModifiers") {
|
propertyMetadataPropertyChecker("setterModifiers") {
|
||||||
it.setter?.let { setter -> printPropertyAccessorModifiers(setter) } ?: PROPERTY_VAL_STUB
|
it.setter?.let(::printPropertyAccessorModifiers) ?: PROPERTY_VAL_STUB
|
||||||
},
|
},
|
||||||
propertyMetadataPropertyChecker("setterValueParameter") {
|
propertyMetadataPropertyChecker("setterValueParameter") {
|
||||||
it.setterParameter?.let { param -> printValueParameter(param) } ?: PROPERTY_VAL_STUB
|
it.setterParameter?.let { param -> printValueParameter(param) } ?: PROPERTY_VAL_STUB
|
||||||
@@ -134,12 +131,12 @@ private val allClassMetadataCheckers = listOf(
|
|||||||
|
|
||||||
classMetadataPropertyChecker("typeParameters") { it.kmClass.typeParameters.stringifyTypeParameters() },
|
classMetadataPropertyChecker("typeParameters") { it.kmClass.typeParameters.stringifyTypeParameters() },
|
||||||
classMetadataPropertyChecker("superTypes") {
|
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("companionObject") { it.kmClass.companionObject.toString() },
|
||||||
classMetadataPropertyChecker("inlineClassUnderlyingPropertyName") { it.kmClass.inlineClassUnderlyingPropertyName.toString() },
|
classMetadataPropertyChecker("inlineClassUnderlyingPropertyName") { it.kmClass.inlineClassUnderlyingPropertyName.toString() },
|
||||||
classMetadataPropertyChecker("inlineClassUnderlyingType") {
|
classMetadataPropertyChecker("inlineClassUnderlyingType") {
|
||||||
it.kmClass.inlineClassUnderlyingType?.let { type -> printType(type) } ?: "---"
|
it.kmClass.inlineClassUnderlyingType?.let(::printType) ?: "---"
|
||||||
},
|
},
|
||||||
classMetadataPropertyChecker("contextReceiverTypes") {
|
classMetadataPropertyChecker("contextReceiverTypes") {
|
||||||
@OptIn(ExperimentalContextReceivers::class)
|
@OptIn(ExperimentalContextReceivers::class)
|
||||||
@@ -166,15 +163,7 @@ private val allMultifileClassPartMetadataCheckers = listOf(
|
|||||||
private val allSyntheticClassMetadataCheckers = listOf(
|
private val allSyntheticClassMetadataCheckers = listOf(
|
||||||
syntheticClassMetadataPropertyChecker("isLambda") { it.isLambda.toString() },
|
syntheticClassMetadataPropertyChecker("isLambda") { it.isLambda.toString() },
|
||||||
syntheticClassMetadataPropertyChecker("function") {
|
syntheticClassMetadataPropertyChecker("function") {
|
||||||
it.kmLambda?.function?.let { function ->
|
it.kmLambda?.function?.let(::printFunction) ?: PROPERTY_VAL_STUB
|
||||||
printFunction(
|
|
||||||
function,
|
|
||||||
KotlinpSettings(
|
|
||||||
isVerbose = true,
|
|
||||||
sortDeclarations = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} ?: PROPERTY_VAL_STUB
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -222,3 +211,38 @@ class CheckerConfiguration(private val enabledExclusively: Set<String>, private
|
|||||||
return name !in disabled
|
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.ClassReport
|
||||||
import org.jetbrains.kotlin.abicmp.reports.ListEntryDiff
|
import org.jetbrains.kotlin.abicmp.reports.ListEntryDiff
|
||||||
import org.jetbrains.kotlin.abicmp.tag
|
import org.jetbrains.kotlin.abicmp.tag
|
||||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
import org.jetbrains.kotlin.kotlinp.Settings
|
||||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
import org.jetbrains.kotlin.kotlinp.JvmKotlinp
|
||||||
import org.jetbrains.kotlin.kotlinp.readKotlinClassHeader
|
import org.jetbrains.kotlin.kotlinp.readKotlinClassHeader
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||||
import org.jetbrains.org.objectweb.asm.ClassWriter
|
import org.jetbrains.org.objectweb.asm.ClassWriter
|
||||||
@@ -50,7 +50,7 @@ class ClassTask(
|
|||||||
return classReader.readKotlinClassHeader()?.run { KotlinClassMetadata.readStrict(this) }
|
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 metadata1 = class1.getMetadata()
|
||||||
val metadata2 = class2.getMetadata()
|
val metadata2 = class2.getMetadata()
|
||||||
|
|
||||||
@@ -59,8 +59,8 @@ class ClassTask(
|
|||||||
if (metadata1 == null || metadata2 == null) {
|
if (metadata1 == null || metadata2 == null) {
|
||||||
report.addMetadataDiff(
|
report.addMetadataDiff(
|
||||||
ListEntryDiff(
|
ListEntryDiff(
|
||||||
metadata1?.run { kotlinp.renderClassFile(metadata1) },
|
metadata1?.run { kotlinp.printClassFile(metadata1) },
|
||||||
metadata2?.run { kotlinp.renderClassFile(metadata2) })
|
metadata2?.run { kotlinp.printClassFile(metadata2) })
|
||||||
)
|
)
|
||||||
return
|
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.
|
* 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
|
package org.jetbrains.kotlin.kotlinp
|
||||||
|
|
||||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
import kotlinx.metadata.*
|
||||||
import kotlinx.metadata.jvm.KotlinModuleMetadata
|
import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlinx.metadata.jvm.UnstableMetadataApi
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class Kotlinp(private val settings: KotlinpSettings) {
|
abstract class Kotlinp(protected val settings: Settings) {
|
||||||
fun renderClassFile(classFile: KotlinClassMetadata): String =
|
fun renderAnnotation(annotation: KmAnnotation, printer: Printer): Unit = with(printer) {
|
||||||
when (classFile) {
|
append(annotation.className)
|
||||||
is KotlinClassMetadata.Class -> ClassPrinter(settings).print(classFile)
|
appendCollectionIfNotEmpty(annotation.arguments.entries, prefix = "(", postfix = ")") { (name, argument) ->
|
||||||
is KotlinClassMetadata.FileFacade -> FileFacadePrinter(settings).print(classFile)
|
append(name, " = ")
|
||||||
is KotlinClassMetadata.SyntheticClass -> {
|
renderAnnotationArgument(argument, printer)
|
||||||
if (classFile.isLambda) LambdaPrinter(settings).print(classFile)
|
}
|
||||||
else buildString { appendLine("synthetic class") }
|
}
|
||||||
|
|
||||||
|
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") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when (argument) {
|
||||||
internal fun readClassFile(file: File): Metadata {
|
is KmAnnotationArgument.ByteValue -> append("${argument.value}.toByte()")
|
||||||
return file.readKotlinClassHeader() ?: throw KotlinpException("file is not a Kotlin class file: $file")
|
is KmAnnotationArgument.CharValue -> append("'${argument.value.toString().sanitize(quote = '\'')}'")
|
||||||
}
|
is KmAnnotationArgument.ShortValue -> append("${argument.value}.toShort()")
|
||||||
|
is KmAnnotationArgument.IntValue -> append(argument.value)
|
||||||
internal fun readMetadata(metadata: Metadata): KotlinClassMetadata {
|
is KmAnnotationArgument.LongValue -> append("${argument.value}L")
|
||||||
return try {
|
is KmAnnotationArgument.FloatValue -> append("${argument.value}f")
|
||||||
KotlinClassMetadata.readLenient(metadata)
|
is KmAnnotationArgument.DoubleValue -> append(argument.value)
|
||||||
} catch (e: IllegalArgumentException) {
|
is KmAnnotationArgument.UByteValue -> append("${argument.value}.toUByte()")
|
||||||
throw KotlinpException("inconsistent Kotlin metadata: ${e.message}")
|
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)
|
protected fun Printer.appendAnnotations(hasAnnotations: Boolean?, annotations: List<KmAnnotation>, onePerLine: Boolean = true) {
|
||||||
internal fun renderModuleFile(metadata: KotlinModuleMetadata?): String =
|
if (hasAnnotations != false) {
|
||||||
if (metadata != null) ModuleFilePrinter(settings).print(metadata)
|
annotations.forEach { annotation ->
|
||||||
else buildString { appendLine("unsupported file") }
|
append("@")
|
||||||
|
renderAnnotation(annotation, this)
|
||||||
|
if (onePerLine) appendLine() else append(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(UnstableMetadataApi::class)
|
@OptIn(ExperimentalContextReceivers::class)
|
||||||
internal fun readModuleFile(file: File): KotlinModuleMetadata? =
|
fun renderClass(clazz: KmClass, printer: Printer): Unit = with(printer) {
|
||||||
runCatching { KotlinModuleMetadata.read(file.readBytes()) }.getOrNull()
|
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.
|
* 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.
|
* 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) {
|
for (path in paths) {
|
||||||
val file = File(path)
|
val file = File(path)
|
||||||
@@ -43,8 +43,8 @@ object Main {
|
|||||||
|
|
||||||
val text = try {
|
val text = try {
|
||||||
when (file.extension) {
|
when (file.extension) {
|
||||||
"class" -> kotlinp.renderClassFile(kotlinp.readMetadata(kotlinp.readClassFile(file)))
|
"class" -> kotlinp.printClassFile(readMetadata(readClassFile(file)))
|
||||||
"kotlin_module" -> @OptIn(UnstableMetadataApi::class) kotlinp.renderModuleFile(kotlinp.readModuleFile(file))
|
"kotlin_module" -> @OptIn(UnstableMetadataApi::class) kotlinp.printModuleFile(readModuleFile(file))
|
||||||
else -> throw KotlinpException("only .class and .kotlin_module files are supported")
|
else -> throw KotlinpException("only .class and .kotlin_module files are supported")
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} 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.
|
* 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
|
package org.jetbrains.kotlin.kotlinp
|
||||||
|
|
||||||
|
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||||
|
import kotlinx.metadata.jvm.KotlinModuleMetadata
|
||||||
import kotlinx.metadata.jvm.Metadata
|
import kotlinx.metadata.jvm.Metadata
|
||||||
|
import kotlinx.metadata.jvm.UnstableMetadataApi
|
||||||
import org.jetbrains.org.objectweb.asm.*
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
||||||
internal fun File.readKotlinClassHeader(): Metadata? = ClassReader(FileInputStream(this)).readKotlinClassHeader()
|
|
||||||
|
|
||||||
fun ClassReader.readKotlinClassHeader(): Metadata? {
|
fun ClassReader.readKotlinClassHeader(): Metadata? {
|
||||||
var header: Metadata? = null
|
var header: Metadata? = null
|
||||||
|
|
||||||
@@ -74,3 +75,19 @@ private fun readMetadataVisitor(output: (Metadata) -> Unit): AnnotationVisitor =
|
|||||||
output(Metadata(kind, metadataVersion, data1, data2, extraString, packageName, extraInt))
|
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.CommonConfigurationKeys
|
||||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||||
import org.jetbrains.kotlin.jvm.compiler.AbstractLoadJavaTest
|
import org.jetbrains.kotlin.jvm.compiler.AbstractLoadJavaTest
|
||||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
import org.jetbrains.kotlin.kotlinp.Settings
|
||||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
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.ConfigurationKind
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
@@ -62,24 +64,24 @@ private fun compileAndPrintAllFiles(
|
|||||||
val main = StringBuilder()
|
val main = StringBuilder()
|
||||||
val afterNodes = StringBuilder()
|
val afterNodes = StringBuilder()
|
||||||
|
|
||||||
val kotlinp = Kotlinp(KotlinpSettings(isVerbose = true, sortDeclarations = true))
|
val kotlinp = JvmKotlinp(Settings(isVerbose = true, sortDeclarations = true))
|
||||||
|
|
||||||
@OptIn(UnstableMetadataApi::class)
|
@OptIn(UnstableMetadataApi::class)
|
||||||
compile(file, disposable, tmpdir, useK2) { outputFile ->
|
compile(file, disposable, tmpdir, useK2) { outputFile ->
|
||||||
when (outputFile.extension) {
|
when (outputFile.extension) {
|
||||||
"kotlin_module" -> {
|
"kotlin_module" -> {
|
||||||
val moduleFile = kotlinp.readModuleFile(outputFile)!!
|
val moduleFile = readModuleFile(outputFile)!!
|
||||||
val transformedWithNodes = KotlinModuleMetadata.read(moduleFile.write())
|
val transformedWithNodes = KotlinModuleMetadata.read(moduleFile.write())
|
||||||
|
|
||||||
for ((sb, moduleFileToRender) in listOf(
|
for ((sb, moduleFileToRender) in listOf(
|
||||||
main to moduleFile, afterNodes to transformedWithNodes
|
main to moduleFile, afterNodes to transformedWithNodes
|
||||||
)) {
|
)) {
|
||||||
sb.appendFileName(outputFile.relativeTo(tmpdir))
|
sb.appendFileName(outputFile.relativeTo(tmpdir))
|
||||||
sb.append(kotlinp.renderModuleFile(moduleFileToRender))
|
sb.append(kotlinp.printModuleFile(moduleFileToRender))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"class" -> {
|
"class" -> {
|
||||||
val metadata = kotlinp.readClassFile(outputFile)
|
val metadata = readClassFile(outputFile)
|
||||||
val classFile = KotlinClassMetadata.readStrict(metadata)
|
val classFile = KotlinClassMetadata.readStrict(metadata)
|
||||||
val classFile2 = KotlinClassMetadata.readStrict(classFile.write())
|
val classFile2 = KotlinClassMetadata.readStrict(classFile.write())
|
||||||
|
|
||||||
@@ -87,7 +89,7 @@ private fun compileAndPrintAllFiles(
|
|||||||
main to classFile, afterNodes to classFile2
|
main to classFile, afterNodes to classFile2
|
||||||
)) {
|
)) {
|
||||||
sb.appendFileName(outputFile.relativeTo(tmpdir))
|
sb.appendFileName(outputFile.relativeTo(tmpdir))
|
||||||
sb.append(kotlinp.renderClassFile(classFileToRender))
|
sb.append(kotlinp.printClassFile(classFileToRender))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> fail("Unknown file: $outputFile")
|
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.JCTree.*
|
||||||
import com.sun.tools.javac.tree.Pretty
|
import com.sun.tools.javac.tree.Pretty
|
||||||
import kotlinx.metadata.jvm.KotlinClassMetadata
|
import kotlinx.metadata.jvm.KotlinClassMetadata
|
||||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
import org.jetbrains.kotlin.kotlinp.Settings
|
||||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
import org.jetbrains.kotlin.kotlinp.JvmKotlinp
|
||||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
import org.jetbrains.kotlin.test.Assertions
|
import org.jetbrains.kotlin.test.Assertions
|
||||||
import org.jetbrains.kotlin.test.model.TestModule
|
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() ?: "",
|
extraString = args[JvmAnnotationNames.METADATA_EXTRA_STRING_FIELD_NAME].stringValue() ?: "",
|
||||||
packageName = args[JvmAnnotationNames.METADATA_PACKAGE_NAME_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
|
// "/*" 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.
|
// to avoid them being interpreted as Java comments.
|
||||||
val sanitized = text.split('\n').dropLast(1).map { it.replace("/*", "(*").replace("*/", "*)") }
|
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.base.util.WriterBackedKaptLogger
|
||||||
import org.jetbrains.kotlin.kapt3.test.KaptMessageCollectorProvider
|
import org.jetbrains.kotlin.kapt3.test.KaptMessageCollectorProvider
|
||||||
import org.jetbrains.kotlin.kapt3.test.kaptOptionsProvider
|
import org.jetbrains.kotlin.kapt3.test.kaptOptionsProvider
|
||||||
import org.jetbrains.kotlin.kotlinp.Kotlinp
|
import org.jetbrains.kotlin.kotlinp.Settings
|
||||||
import org.jetbrains.kotlin.kotlinp.KotlinpSettings
|
import org.jetbrains.kotlin.kotlinp.JvmKotlinp
|
||||||
import org.jetbrains.kotlin.test.model.*
|
import org.jetbrains.kotlin.test.model.*
|
||||||
import org.jetbrains.kotlin.test.services.*
|
import org.jetbrains.kotlin.test.services.*
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
@@ -125,7 +125,7 @@ internal data class Kapt4ContextBinaryArtifact(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun Printer.renderMetadata(metadata: Metadata) {
|
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
|
// "/*" 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.
|
// to avoid them being interpreted as Java comments.
|
||||||
val sanitized = text.split('\n')
|
val sanitized = text.split('\n')
|
||||||
|
|||||||
Reference in New Issue
Block a user