[KLIB tool] Print ID signatures

This commit is contained in:
Dmitriy Dolovov
2020-10-06 14:52:53 +03:00
committed by Dmitriy Dolovov
parent 0ea834ea1f
commit 13cf470e33
9 changed files with 314 additions and 181 deletions
@@ -0,0 +1,7 @@
package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
interface DeclarationHeaderRenderer {
fun render(descriptor: DeclarationDescriptor): String
}
@@ -0,0 +1,100 @@
package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
import org.jetbrains.kotlin.utils.Printer
class DeclarationPrinter(
out: Appendable,
private val headerRenderer: DeclarationHeaderRenderer,
private val signatureRenderer: IdSignatureRenderer
) {
private val printer = Printer(out, 1, " ")
private val DeclarationDescriptorWithVisibility.isPublicOrProtected: Boolean
get() = visibility == DescriptorVisibilities.PUBLIC || visibility == DescriptorVisibilities.PROTECTED
private val CallableMemberDescriptor.isFakeOverride: Boolean
get() = kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE
private val DeclarationDescriptor.shouldBePrinted: Boolean
get() = this is ClassifierDescriptorWithTypeParameters && isPublicOrProtected
|| this is CallableMemberDescriptor && isPublicOrProtected && !isFakeOverride
fun print(module: ModuleDescriptor) {
module.accept(PrinterVisitor(), Unit)
}
private fun Printer.printWithBody(header: String, signature: String? = null, body: () -> Unit) {
println()
printPlain(header, signature, suffix = " {")
pushIndent()
body()
popIndent()
println("}")
println()
}
private fun Printer.printPlain(header: String, signature: String? = null, suffix: String? = null) {
if (signature != null) println(signature)
println(if (suffix != null ) header + suffix else header)
}
private inner class PrinterVisitor : DeclarationDescriptorVisitorEmptyBodies<Unit, Unit>() {
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Unit) {
descriptor.getPackageFragments().forEach { it.accept(this, data) }
}
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Unit) {
val children = descriptor.getMemberScope().getContributedDescriptors().filter { it.shouldBePrinted }
if (children.isNotEmpty()) {
printer.printWithBody(header = headerRenderer.render(descriptor)) {
children.forEach { it.accept(this, data) }
}
}
}
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Unit) {
val header = headerRenderer.render(descriptor)
val signature = signatureRenderer.render(descriptor)
val children = descriptor.unsubstitutedMemberScope.getContributedDescriptors().filter { it.shouldBePrinted }
val constructors = descriptor.constructors.filter { !it.isPrimary && it.shouldBePrinted }
if (children.isNotEmpty() || constructors.isNotEmpty()) {
printer.printWithBody(header, signature) {
constructors.forEach { it.accept(this, data) }
children.forEach { it.accept(this, data) }
}
} else {
printer.printPlain(header, signature)
}
}
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Unit) {
printer.printPlain(header = headerRenderer.render(descriptor), signature = signatureRenderer.render(descriptor))
}
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Unit) {
printer.printPlain(header = headerRenderer.render(descriptor), signature = signatureRenderer.render(descriptor))
descriptor.getter?.takeIf { !it.annotations.isEmpty() }?.let { getter ->
printer.pushIndent()
printer.printPlain(header = headerRenderer.render(getter), signature = signatureRenderer.render(getter))
printer.popIndent()
}
descriptor.setter?.takeIf { !it.annotations.isEmpty() || it.visibility != descriptor.visibility }?.let { setter ->
printer.pushIndent()
printer.printPlain(header = headerRenderer.render(setter), signature = signatureRenderer.render(setter))
printer.popIndent()
}
}
override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Unit) {
printer.printPlain(header = headerRenderer.render(descriptor), signature = signatureRenderer.render(descriptor))
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Unit) {
printer.printPlain(header = headerRenderer.render(descriptor), signature = signatureRenderer.render(descriptor))
}
}
}
@@ -0,0 +1,79 @@
package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.renderer.AnnotationArgumentsRenderingPolicy
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy
object DefaultDeclarationHeaderRenderer : DeclarationHeaderRenderer {
override fun render(descriptor: DeclarationDescriptor): String = when (descriptor) {
is PackageFragmentDescriptor -> render(descriptor)
is ClassifierDescriptorWithTypeParameters -> render(descriptor)
is PropertyAccessorDescriptor -> render(descriptor)
is CallableMemberDescriptor -> render(descriptor)
else -> throw AssertionError("Unknown declaration descriptor type: $descriptor")
}
private fun render(descriptor: PackageFragmentDescriptor): String {
val packageName = descriptor.fqName.let { if (it.isRoot) "<root>" else it.asString() }
return "package $packageName"
}
private fun render(descriptor: ClassifierDescriptorWithTypeParameters): String {
val renderer = when (descriptor.modality) {
// Don't render 'final' modality
Modality.FINAL -> Renderers.WITHOUT_MODALITY
else -> Renderers.DEFAULT
}
return renderer.render(descriptor)
}
private fun render(descriptor: CallableMemberDescriptor): String {
val containingDeclaration = descriptor.containingDeclaration
val renderer = when {
// Don't render modality for non-override final methods and interface methods.
containingDeclaration is ClassDescriptor && containingDeclaration.kind == ClassKind.INTERFACE ||
descriptor.modality == Modality.FINAL && descriptor.overriddenDescriptors.isEmpty() -> Renderers.WITHOUT_MODALITY
else -> Renderers.DEFAULT
}
return renderer.render(descriptor)
}
private fun render(descriptor: PropertyAccessorDescriptor) = buildString {
descriptor.annotations.forEach {
append(Renderers.DEFAULT.renderAnnotation(it)).append(" ")
}
if (descriptor.visibility != DescriptorVisibilities.DEFAULT_VISIBILITY) {
append(descriptor.visibility.internalDisplayName).append(" ")
}
when (descriptor) {
is PropertyGetterDescriptor -> append("get")
is PropertySetterDescriptor -> append("set")
else -> throw AssertionError("Unknown accessor descriptor type: $descriptor")
}
}
private object Renderers {
val DEFAULT = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions {
modifiers = DescriptorRendererModifier.ALL
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
excludedAnnotationClasses += StandardNames.FqNames.suppress
classWithPrimaryConstructor = true
renderConstructorKeyword = true
includePropertyConstant = true
unitReturnType = false
withDefinedIn = false
renderDefaultVisibility = false
secondaryConstructorsAsPrimary = false
}
val WITHOUT_MODALITY = DEFAULT.withOptions {
modifiers -= DescriptorRendererModifier.MODALITY
}
}
}
@@ -0,0 +1,24 @@
package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.backend.konan.serialization.KonanIdSignaturer
import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerDesc
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
class DefaultIdSignatureRenderer(private val prefix: String? = null) : IdSignatureRenderer {
private val idSignaturer = KonanIdSignaturer(KonanManglerDesc)
override fun render(descriptor: DeclarationDescriptor): String? {
val idSignature = if (descriptor is ClassDescriptor && descriptor.kind == ClassKind.ENUM_ENTRY) {
idSignaturer.composeEnumEntrySignature(descriptor)
} else {
idSignaturer.composeSignature(descriptor)
} ?: return null
return if (prefix != null)
prefix + idSignature.render()
else
idSignature.render()
}
}
@@ -0,0 +1,13 @@
package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
interface IdSignatureRenderer {
fun render(descriptor: DeclarationDescriptor): String?
companion object {
val NO_SIGNATURE = object : IdSignatureRenderer {
override fun render(descriptor: DeclarationDescriptor): String? = null
}
}
}
@@ -1,163 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
import org.jetbrains.kotlin.renderer.*
import org.jetbrains.kotlin.utils.Printer
class KlibPrinter(out: Appendable) {
val printer = Printer(out, 1, " ")
val DeclarationDescriptorWithVisibility.isPublicOrProtected: Boolean
get() = visibility == DescriptorVisibilities.PUBLIC || visibility == DescriptorVisibilities.PROTECTED
val CallableMemberDescriptor.isFakeOverride: Boolean
get() = kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE
val DeclarationDescriptor.shouldBePrinted: Boolean
get() = this is ClassifierDescriptorWithTypeParameters && isPublicOrProtected
|| this is CallableMemberDescriptor && isPublicOrProtected && !isFakeOverride
private fun Printer.printBody(header: CharSequence, block: () -> Unit) {
println()
println("$header {")
pushIndent()
block()
popIndent()
println("}")
println()
}
private fun ClassifierDescriptorWithTypeParameters.render(): String {
val renderer = when (modality) {
// Don't render 'final' modality
Modality.FINAL -> Renderers.WITHOUT_MODALITY
else -> Renderers.DEFAULT
}
return renderer.render(this)
}
private fun CallableMemberDescriptor.render(): String {
val containingDeclaration = containingDeclaration
val renderer = when {
// Don't render modality for non-override final methods and interface methods.
containingDeclaration is ClassDescriptor && containingDeclaration.kind == ClassKind.INTERFACE ||
modality == Modality.FINAL && overriddenDescriptors.isEmpty() ->
Renderers.WITHOUT_MODALITY
else -> Renderers.DEFAULT
}
return renderer.render(this)
}
private fun PropertyAccessorDescriptor.render() = buildString {
annotations.forEach {
append(Renderers.DEFAULT.renderAnnotation(it)).append(" ")
}
if (visibility != DescriptorVisibilities.DEFAULT_VISIBILITY) {
append(visibility.internalDisplayName).append(" ")
}
when (this@render) {
is PropertyGetterDescriptor -> append("get")
is PropertySetterDescriptor -> append("set")
else -> throw AssertionError("Unknown accessor descriptor type: ${this@render}")
}
}
fun print(module: ModuleDescriptor) {
module.accept(PrinterVisitor(), Unit)
}
private inner class PrinterVisitor : DeclarationDescriptorVisitorEmptyBodies<Unit, Unit>() {
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Unit) {
descriptor.getPackageFragments().forEach {
it.accept(this, data)
}
}
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Unit) {
val children = descriptor.getMemberScope().getContributedDescriptors().filter { it.shouldBePrinted }
if (children.isNotEmpty()) {
val packageName = descriptor.fqName.let { if (it.isRoot) "<root>" else it.asString() }
val header = "package $packageName"
printer.printBody(header) {
children.forEach { it.accept(this, data) }
}
}
}
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Unit) {
val children = descriptor.unsubstitutedMemberScope.getContributedDescriptors().filter { it.shouldBePrinted }
val constructors = descriptor.constructors.filter { !it.isPrimary && it.shouldBePrinted }
val header = descriptor.render()
if (children.isNotEmpty() || constructors.isNotEmpty()) {
printer.printBody(header) {
constructors.forEach { it.accept(this, data) }
children.forEach { it.accept(this, data) }
}
} else {
printer.println(header)
}
}
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Unit) {
printer.println(descriptor.render())
}
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Unit) {
printer.println(descriptor.render())
descriptor.getter?.let { getter ->
if (!getter.annotations.isEmpty()) {
printer.pushIndent()
printer.println(getter.render())
printer.popIndent()
}
}
descriptor.setter?.let { setter ->
if (!setter.annotations.isEmpty() || setter.visibility != descriptor.visibility) {
printer.pushIndent()
printer.println(setter.render())
printer.popIndent()
}
}
}
override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Unit) {
printer.println(descriptor.render())
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Unit) {
printer.println(descriptor.render())
}
}
object Renderers {
val DEFAULT = DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.withOptions {
modifiers = DescriptorRendererModifier.ALL
overrideRenderingPolicy = OverrideRenderingPolicy.RENDER_OVERRIDE
annotationArgumentsRenderingPolicy = AnnotationArgumentsRenderingPolicy.UNLESS_EMPTY
excludedAnnotationClasses += setOf(StandardNames.FqNames.suppress)
classWithPrimaryConstructor = true
renderConstructorKeyword = true
includePropertyConstant = true
unitReturnType = false
withDefinedIn = false
renderDefaultVisibility = false
secondaryConstructorsAsPrimary = false
}
val WITHOUT_MODALITY = DEFAULT.withOptions {
modifiers -= DescriptorRendererModifier.MODALITY
}
}
}
@@ -0,0 +1,55 @@
package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies
import org.jetbrains.kotlin.utils.Printer
class SignaturePrinter(
out: Appendable,
private val signatureRenderer: IdSignatureRenderer
) {
private val printer = Printer(out)
fun print(module: ModuleDescriptor) {
module.accept(PrinterVisitor(), Unit)
}
private fun Printer.printlnIfNotNull(line: String?) {
if (line != null) println(line)
}
private inner class PrinterVisitor : DeclarationDescriptorVisitorEmptyBodies<Unit, Unit>() {
override fun visitModuleDeclaration(descriptor: ModuleDescriptor, data: Unit) {
descriptor.getPackageFragments().forEach { it.accept(this, data) }
}
override fun visitPackageFragmentDescriptor(descriptor: PackageFragmentDescriptor, data: Unit) {
descriptor.getMemberScope().getContributedDescriptors().forEach { it.accept(this, data) }
}
override fun visitClassDescriptor(descriptor: ClassDescriptor, data: Unit) {
printer.printlnIfNotNull(signatureRenderer.render(descriptor))
descriptor.constructors.forEach { it.accept(this, data) }
descriptor.unsubstitutedMemberScope.getContributedDescriptors().forEach { it.accept(this, data) }
}
override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, data: Unit) {
printer.printlnIfNotNull(signatureRenderer.render(descriptor))
}
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Unit) {
printer.printlnIfNotNull(signatureRenderer.render(descriptor))
descriptor.getter?.let { printer.printlnIfNotNull(signatureRenderer.render(it)) }
descriptor.setter?.let { printer.printlnIfNotNull(signatureRenderer.render(it)) }
}
override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Unit) {
printer.printlnIfNotNull(signatureRenderer.render(descriptor))
}
override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Unit) {
printer.printlnIfNotNull(signatureRenderer.render(descriptor))
}
}
}
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.konan.util.KlibMetadataFactories
import org.jetbrains.kotlin.backend.common.serialization.metadata.DynamicTypeDeserializer
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.deserialization.PlatformDependentTypeTransformer
import org.jetbrains.kotlin.util.Logger
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
@@ -28,7 +29,6 @@ import org.jetbrains.kotlin.konan.library.resolverByName
import org.jetbrains.kotlin.konan.util.KonanHomeProvider
import org.jetbrains.kotlin.library.metadata.parseModuleHeader
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import java.lang.System.out
import kotlin.system.exitProcess
internal val KlibFactories = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer, PlatformDependentTypeTransformer.None)
@@ -39,15 +39,17 @@ fun printUsage() {
println("\tinfo\tgeneral information about the library")
println("\tinstall\tinstall the library to the local repository")
println("\tcontents\tlist contents of the library")
println("\tsignatures\tlist of ID signatures in the library")
println("\tremove\tremove the library from the local repository")
println("and the options are:")
println("\t-repository <path>\twork with the specified repository")
println("\t-target <name>\tinspect specifics of the given target")
println("\t-print-signatures [true|false]\tprint ID signature for every declaration (only for \"contents\" command)")
}
private fun parseArgs(args: Array<String>): Map<String, List<String>> {
val commandLine = mutableMapOf<String, MutableList<String>>()
for (index in 0..args.size - 1 step 2) {
for (index in args.indices step 2) {
val key = args[index]
if (key[0] != '-') {
throw IllegalArgumentException("Expected a flag with initial dash: $key")
@@ -62,13 +64,14 @@ private fun parseArgs(args: Array<String>): Map<String, List<String>> {
}
class Command(args: Array<String>){
class Command(args: Array<String>) {
init {
if (args.size < 2) {
printUsage()
exitProcess(0)
}
}
val verb = args[0]
val library = args[1]
val options = parseArgs(args.drop(2).toTypedArray())
@@ -160,8 +163,22 @@ class Library(val name: String, val requestedRepository: String?, val target: St
library?.libraryFile?.deleteRecursively()
}
fun contents(output: Appendable = out) {
fun contents(output: Appendable, printSignatures: Boolean) {
val module = loadModule()
val signatureRenderer = if (printSignatures) DefaultIdSignatureRenderer("// ID signature: ") else IdSignatureRenderer.NO_SIGNATURE
val printer = DeclarationPrinter(output, DefaultDeclarationHeaderRenderer, signatureRenderer)
printer.print(module)
}
fun signatures(output: Appendable) {
val module = loadModule()
val printer = SignaturePrinter(output, DefaultIdSignatureRenderer())
printer.print(module)
}
private fun loadModule(): ModuleDescriptor {
val storageManager = LockBasedStorageManager("klib")
val library = libraryInRepoOrCurrentDir(repository, name)
val versionSpec = LanguageVersionSettingsImpl(currentLanguageVersion, currentApiVersion)
@@ -173,19 +190,18 @@ class Library(val name: String, val requestedRepository: String?, val target: St
emptyList(),
distributionKlib = Distribution(KonanHomeProvider.determineKonanHome()).klib,
skipCurrentDir = true,
logger = KlibToolLogger)
resolver.defaultLinks(false, true, true)
.mapTo(defaultModules) {
KlibFactories.DefaultDeserializedDescriptorFactory.createDescriptor(
it, versionSpec, storageManager, module.builtIns, null)
}
logger = KlibToolLogger
)
resolver.defaultLinks(false, true, true).mapTo(defaultModules) {
KlibFactories.DefaultDeserializedDescriptorFactory.createDescriptor(it, versionSpec, storageManager, module.builtIns, null)
}
}
(defaultModules + module).let { allModules ->
allModules.forEach { it.setDependencies(allModules) }
}
KlibPrinter(output).print(module)
return module
}
}
@@ -208,15 +224,17 @@ fun main(args: Array<String>) {
val target = targetManager.targetName
val repository = command.options["-repository"]?.last()
val printSignatures = command.options["-print-signatures"]?.last()?.toBoolean() == true
val library = Library(command.library, repository, target)
when (command.verb) {
"contents" -> library.contents()
"info" -> library.info()
"install" -> library.install()
"remove" -> library.remove()
else -> error("Unknown command ${command.verb}.")
"contents" -> library.contents(System.out, printSignatures)
"signatures" -> library.signatures(System.out)
"info" -> library.info()
"install" -> library.install()
"remove" -> library.remove()
else -> error("Unknown command ${command.verb}.")
}
}
@@ -19,7 +19,7 @@ class ContentsTest {
private fun klibContents(library: String, printOutput: Boolean = false, expected: () -> String) {
val output = StringBuilder()
val lib = Library(library, null, "host")
lib.contents(output)
lib.contents(output, false)
if (printOutput) {
println(output.trim().toString())
}
@@ -34,7 +34,7 @@ class ContentsTest {
fun `Stdlib content should be printed without exceptions`() {
val output = StringBuilder()
val distributionPath = System.getProperty("konan.home")
Library(Distribution(distributionPath).stdlib, null, "host").contents(output)
Library(Distribution(distributionPath).stdlib, null, "host").contents(output, false)
}
@Test