Pretty printer for users
This commit is contained in:
committed by
ilmat192
parent
e0d4978760
commit
829feb7375
@@ -16,11 +16,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli.klib
|
package org.jetbrains.kotlin.cli.klib
|
||||||
|
|
||||||
import org.jetbrains.kotlin.serialization.KonanLinkData
|
import org.jetbrains.kotlin.backend.konan.serialization.KonanSerializerProtocol
|
||||||
import org.jetbrains.kotlin.backend.konan.serialization.parseModuleHeader
|
import org.jetbrains.kotlin.backend.konan.serialization.parseModuleHeader
|
||||||
import org.jetbrains.kotlin.backend.konan.serialization.parsePackageFragment
|
import org.jetbrains.kotlin.backend.konan.serialization.parsePackageFragment
|
||||||
import org.jetbrains.kotlin.backend.konan.serialization.KonanSerializerProtocol
|
|
||||||
import org.jetbrains.kotlin.serialization.Flags
|
import org.jetbrains.kotlin.serialization.Flags
|
||||||
|
import org.jetbrains.kotlin.serialization.Flags.*
|
||||||
|
import org.jetbrains.kotlin.serialization.KonanLinkData
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
import java.lang.System.out
|
import java.lang.System.out
|
||||||
@@ -37,484 +38,297 @@ open class ModuleDeserializer(val library: ByteArray) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PrettyPrinter(library: ByteArray, val packageLoader: (String) -> ByteArray)
|
//-----------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
class PrettyPrinter(library: ByteArray, val packageLoader: (String) -> ByteArray)
|
||||||
: ModuleDeserializer(library) {
|
: ModuleDeserializer(library) {
|
||||||
|
|
||||||
private fun packageFragment(fqname: String): KonanLinkData.PackageFragment
|
private fun packageFragment(fqname: String): KonanLinkData.PackageFragment
|
||||||
= parsePackageFragment(packageLoader(fqname))
|
= parsePackageFragment(packageLoader(fqname))
|
||||||
|
|
||||||
fun printPackageFragment(fqname: String) {
|
fun printPackageFragment(fqname: String) {
|
||||||
if (fqname.isNotEmpty()) println("\npackage $fqname")
|
|
||||||
val fragment = packageFragment(fqname)
|
val fragment = packageFragment(fqname)
|
||||||
PackageFragmentPrinter(fragment, out).print()
|
PackageFragmentPrinter(fragment, out).print()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------//
|
||||||
|
|
||||||
class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, out: Appendable) {
|
class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, out: Appendable) {
|
||||||
private val printer = Printer(out, " ")
|
private val printer = Printer(out, " ")
|
||||||
private val stringTable = packageFragment.stringTable!!
|
private val stringTable = packageFragment.stringTable!!
|
||||||
private val qualifiedNameTable = packageFragment.nameTable!!
|
private val qualifiedNameTable = packageFragment.nameTable!!
|
||||||
private var typeTableStack = mutableListOf<ProtoBuf.TypeTable>()
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
object Indent {
|
||||||
|
private var indent: String = ""
|
||||||
|
fun push() { indent += " " }
|
||||||
|
fun pop() { indent = indent.dropLast(4) }
|
||||||
|
override fun toString() = indent
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
object TypeTables {
|
||||||
|
val tables = mutableListOf<ProtoBuf.TypeTable>()
|
||||||
|
fun push(table: ProtoBuf.TypeTable) { tables.add(table) }
|
||||||
|
fun pop() { tables.removeAt(tables.lastIndex) }
|
||||||
|
fun type(typeId: Int) = tables.last().getType(typeId)!!
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
fun print() {
|
fun print() {
|
||||||
typeTableStack.add(packageFragment.`package`.typeTable)
|
TypeTables.push(packageFragment.`package`.typeTable)
|
||||||
|
// if (packageFragment.hasFqName()) printer.print("package ${packageFragment.fqName}\n")
|
||||||
|
packageFragment.classes.classesList.forEach { printer.println(it.asString()) }
|
||||||
|
packageFragment.`package`.functionList.forEach { printer.println(it.asString()) }
|
||||||
|
packageFragment.`package`.propertyList.forEach { printer.println(it.asString()) }
|
||||||
|
TypeTables.pop()
|
||||||
|
}
|
||||||
|
|
||||||
val protoTypeAliases = packageFragment.`package`.typeAliasList
|
//-------------------------------------------------------------------------//
|
||||||
protoTypeAliases.forEach { protoTypeAlias ->
|
|
||||||
printTypeAlias(protoTypeAlias)
|
|
||||||
}
|
|
||||||
|
|
||||||
val protoClasses = packageFragment.classes.classesList // ProtoBuf classes
|
private fun StringBuilder.buildBody(body: StringBuilder.() -> Unit) {
|
||||||
protoClasses.forEach { protoClass ->
|
Indent.push()
|
||||||
val classKind = Flags.CLASS_KIND.get(protoClass.flags)!!
|
val result = StringBuilder().apply(body).toString()
|
||||||
when (classKind) {
|
append(if (result.isEmpty()) "\n" else " {\n$result}\n")
|
||||||
ProtoBuf.Class.Kind.CLASS -> printClass(protoClass)
|
Indent.pop()
|
||||||
ProtoBuf.Class.Kind.ENUM_CLASS -> printEnum(protoClass)
|
}
|
||||||
ProtoBuf.Class.Kind.INTERFACE -> printClass(protoClass)
|
|
||||||
ProtoBuf.Class.Kind.ENUM_ENTRY -> printEnum(protoClass)
|
//-------------------------------------------------------------------------//
|
||||||
ProtoBuf.Class.Kind.ANNOTATION_CLASS -> printClass(protoClass)
|
|
||||||
ProtoBuf.Class.Kind.OBJECT -> printClass(protoClass)
|
private fun ProtoBuf.Class.asString(): String {
|
||||||
ProtoBuf.Class.Kind.COMPANION_OBJECT -> printClass(protoClass)
|
if (hasTypeTable()) TypeTables.push(typeTable)
|
||||||
|
val result = buildString {
|
||||||
|
val className = getName(fqName)
|
||||||
|
val classKind = Flags.CLASS_KIND.get(flags).asString()
|
||||||
|
val modality = Flags.MODALITY .get(flags).asString()
|
||||||
|
val visibility = Flags.VISIBILITY.get(flags).asString()
|
||||||
|
val typeParameters = typeParameterList.joinToString("<", "> ") { it.asString() }
|
||||||
|
val primaryConstructor = primaryConstructor().asString(true)
|
||||||
|
val supertypes = supertypesToString(supertypeIdList)
|
||||||
|
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.classAnnotation), "\n")
|
||||||
|
|
||||||
|
append("$annotations$Indent$modality$visibility$classKind$className$typeParameters$primaryConstructor$supertypes")
|
||||||
|
buildBody {
|
||||||
|
secondaryConstructors().forEach { append(it.asString()) }
|
||||||
|
functionList.forEach { append(it.asString()) }
|
||||||
|
propertyList.forEach { append(it.asString()) }
|
||||||
|
nestedClassNameList.forEach { append(nestedClassAsString(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hasTypeTable()) TypeTables.pop()
|
||||||
val protoFunctions = packageFragment.`package`.functionList
|
return result
|
||||||
protoFunctions.forEach { protoFunction ->
|
|
||||||
printFunction(protoFunction)
|
|
||||||
}
|
|
||||||
|
|
||||||
val protoProperties = packageFragment.`package`.propertyList
|
|
||||||
protoProperties.forEach { protoProperty ->
|
|
||||||
printProperty(protoProperty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printTypeAlias(protoTypeAlias: ProtoBuf.TypeAlias) {
|
|
||||||
val aliasName = stringTable.getString(protoTypeAlias.name)
|
|
||||||
val typeName = typeToString(protoTypeAlias.expandedTypeId)
|
|
||||||
printer.println("typealias $aliasName = $typeName")
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printClass(protoClass: ProtoBuf.Class) {
|
|
||||||
val hasTypeTable = protoClass.hasTypeTable()
|
|
||||||
if (hasTypeTable) typeTableStack.add(protoClass.typeTable)
|
|
||||||
val className = getShortName(protoClass.fqName)
|
|
||||||
val annotations = protoClass.getExtension(KonanSerializerProtocol.classAnnotation)
|
|
||||||
val keyword = classOrInterface(protoClass)
|
|
||||||
val protoConstructors = protoClass.constructorList
|
|
||||||
val protoFunctions = protoClass.functionList
|
|
||||||
val protoProperties = protoClass.propertyList
|
|
||||||
val nestedClasses = protoClass.nestedClassNameList
|
|
||||||
|
|
||||||
printAnnotations(annotations)
|
|
||||||
printer.print("$keyword $className")
|
|
||||||
printer.print(typeParametersToString(protoClass.typeParameterList))
|
|
||||||
printer.print(primaryConstructorToString(protoConstructors))
|
|
||||||
printer.print(supertypesToString(protoClass.supertypeIdList))
|
|
||||||
|
|
||||||
printer.println(" {")
|
|
||||||
printer.pushIndent()
|
|
||||||
printSecondaryConstructors(protoConstructors)
|
|
||||||
printCompanionObject(protoClass)
|
|
||||||
protoFunctions.forEach { printFunction(it) }
|
|
||||||
protoProperties.forEach { printProperty(it) }
|
|
||||||
nestedClasses.forEach { printNestedClass(it) }
|
|
||||||
printer.popIndent()
|
|
||||||
printer.println("}\n")
|
|
||||||
if (hasTypeTable) typeTableStack.removeAt(typeTableStack.lastIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printEnum(protoEnum: ProtoBuf.Class) {
|
|
||||||
val hasTypeTable = protoEnum.hasTypeTable()
|
|
||||||
if (hasTypeTable) typeTableStack.add(protoEnum.typeTable)
|
|
||||||
val flags = protoEnum.flags
|
|
||||||
val enumName = getShortName(protoEnum.fqName)
|
|
||||||
val modality = modalityToString(Flags.MODALITY.get(flags))
|
|
||||||
val annotations = protoEnum.getExtension(KonanSerializerProtocol.classAnnotation)
|
|
||||||
val enumEntries = protoEnum.enumEntryList
|
|
||||||
if (Flags.CLASS_KIND.get(flags) == ProtoBuf.Class.Kind.ENUM_ENTRY) return
|
|
||||||
|
|
||||||
printAnnotations(annotations)
|
|
||||||
printer.print("enum class $modality")
|
|
||||||
printer.print(enumName)
|
|
||||||
|
|
||||||
printer.println(" {")
|
|
||||||
enumEntries.dropLast(1).forEach { printer.print(" ${enumEntryToString(it)},\n") }
|
|
||||||
enumEntries.lastOrNull()?.let { printer.print(" ${enumEntryToString(it)} \n") }
|
|
||||||
printer.println("}\n")
|
|
||||||
if (hasTypeTable) typeTableStack.removeAt(typeTableStack.lastIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printFunction(protoFunction: ProtoBuf.Function) {
|
|
||||||
val hasTypeTable = protoFunction.hasTypeTable()
|
|
||||||
if (hasTypeTable) typeTableStack.add(protoFunction.typeTable)
|
|
||||||
val flags = protoFunction.flags
|
|
||||||
val name = stringTable.getString(protoFunction.name)
|
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
|
||||||
val isInline = inlineToString(Flags.IS_INLINE.get(flags))
|
|
||||||
val receiverType = receiverTypeToString(protoFunction)
|
|
||||||
val annotations = protoFunction.getExtension(KonanSerializerProtocol.functionAnnotation)
|
|
||||||
val typeParameters = typeParametersToString(protoFunction.typeParameterList)
|
|
||||||
val valueParameters = valueParametersToString(protoFunction.valueParameterList)
|
|
||||||
val returnType = returnTypeToString(protoFunction)
|
|
||||||
|
|
||||||
printAnnotations(annotations)
|
|
||||||
printer.println("$visibility${isInline}fun $typeParameters$receiverType$name$valueParameters$returnType")
|
|
||||||
if (hasTypeTable) typeTableStack.removeAt(typeTableStack.lastIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printProperty(protoProperty: ProtoBuf.Property) {
|
|
||||||
val name = stringTable.getString(protoProperty.name)
|
|
||||||
val flags = protoProperty.flags
|
|
||||||
val isVar = if (Flags.IS_VAR.get(flags)) "var" else "val"
|
|
||||||
val modality = modalityToString(Flags.MODALITY.get(flags))
|
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
|
||||||
val returnType = typeToString(protoProperty.returnTypeId)
|
|
||||||
val annotations = protoProperty.getExtension(KonanSerializerProtocol.propertyAnnotation)
|
|
||||||
|
|
||||||
printAnnotations(annotations)
|
|
||||||
printer.println("$modality$visibility$isVar $name: $returnType")
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
fun printObject(protoObject: ProtoBuf.Class) {
|
|
||||||
val flags = protoObject.flags
|
|
||||||
val name = getShortName(protoObject.fqName)
|
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
|
||||||
val annotations = protoObject.getExtension(KonanSerializerProtocol.classAnnotation)
|
|
||||||
|
|
||||||
printAnnotations(annotations)
|
|
||||||
printer.println("${visibility}object $name")
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printAnnotations(protoAnnotations: List<ProtoBuf.Annotation>) {
|
|
||||||
val annotations = annotationsToString(protoAnnotations)
|
|
||||||
if (annotations.isEmpty()) return
|
|
||||||
printer.println(annotations)
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun annotationsToString(protoAnnotations: List<ProtoBuf.Annotation>): String {
|
|
||||||
val buff = StringBuilder()
|
|
||||||
protoAnnotations.forEach { buff.append(annotationToString(it)) }
|
|
||||||
return buff.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun annotationToString(protoAnnotation: ProtoBuf.Annotation): String {
|
|
||||||
val buff = StringBuilder()
|
|
||||||
val annotationName = getShortName(protoAnnotation.id)
|
|
||||||
buff.append("@$annotationName")
|
|
||||||
val arguments = annotationArgumentsToString(protoAnnotation.argumentList)
|
|
||||||
buff.append(arguments)
|
|
||||||
return buff.append(" ").toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun annotationArgumentsToString(annotationArguments: List<ProtoBuf.Annotation.Argument>): String {
|
|
||||||
if (annotationArguments.isEmpty()) return ""
|
|
||||||
val buff = StringBuilder("(")
|
|
||||||
annotationArguments.dropLast(1).forEach { buff.append(annotationArgumentValueToString(it.value) + ", ") }
|
|
||||||
annotationArguments.lastOrNull()?.let { buff.append(annotationArgumentValueToString(it.value)) }
|
|
||||||
return buff.append(")").toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun annotationArgumentValueToString(value: ProtoBuf.Annotation.Argument.Value): String {
|
|
||||||
val buff = StringBuilder()
|
|
||||||
if (value.hasAnnotation()) buff.append(annotationToString(value.annotation))
|
|
||||||
if (value.hasStringValue()) buff.append("\"${stringTable.getString(value.stringValue)}\"")
|
|
||||||
if (value.hasDoubleValue()) buff.append(value.doubleValue)
|
|
||||||
if (value.hasFloatValue()) buff.append(value.floatValue)
|
|
||||||
if (value.hasIntValue()) buff.append(value.intValue)
|
|
||||||
if (value.hasClassId()) buff.append(getShortName(value.classId) + ".")
|
|
||||||
if (value.hasEnumValueId()) buff.append(stringTable.getString(value.enumValueId))
|
|
||||||
if (value.arrayElementCount > 0) {
|
|
||||||
val arrayElements = value.arrayElementList
|
|
||||||
arrayElements.dropLast(1).forEach { buff.append(annotationArgumentValueToString(it) + ", ") }
|
|
||||||
arrayElements.lastOrNull()?.let { buff.append(annotationArgumentValueToString(it)) }
|
|
||||||
}
|
|
||||||
return buff.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun supertypesToString(supertypesId: List<Int>): String {
|
private fun supertypesToString(supertypesId: List<Int>): String {
|
||||||
val buff = StringBuilder()
|
val result = supertypesId.joinToString {
|
||||||
supertypesId.dropLast(1).forEach { supertypeId ->
|
val supertype = TypeTables.type(it).asString()
|
||||||
val supertype = typeToString(supertypeId)
|
if (supertype == "Any") "" else supertype
|
||||||
if (supertype != "Any") buff.append("$supertype, ")
|
|
||||||
}
|
}
|
||||||
supertypesId.lastOrNull()?.let { supertypeId ->
|
return if (result.isEmpty()) "" else " : $result"
|
||||||
val supertype = typeToString(supertypeId)
|
|
||||||
if (supertype != "Any") buff.append(supertype)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buff.isEmpty()) return ""
|
|
||||||
return " : " + buff.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun primaryConstructorToString(protoConstructors: List<ProtoBuf.Constructor>): String {
|
private fun ProtoBuf.Constructor.asString(isPrimary: Boolean = false): String {
|
||||||
val primaryConstructor = protoConstructors.firstOrNull { protoConstructor ->
|
val visibility = Flags.VISIBILITY.get(flags).asString()
|
||||||
!Flags.IS_SECONDARY.get(protoConstructor.flags)
|
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.constructorAnnotation), "\n")
|
||||||
} ?: return ""
|
val parameters = valueParameterList.joinToString(", ", prefix = "(", postfix = ")") { it.asString(true) }
|
||||||
|
|
||||||
val flags = primaryConstructor.flags
|
val header = "$visibility$annotations"
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
if (isPrimary) {
|
||||||
val annotations = annotationsToString(primaryConstructor.getExtension(KonanSerializerProtocol.constructorAnnotation))
|
if (header.isNotEmpty()) return "$header constructor$parameters"
|
||||||
val valueParameters = constructorValueParametersToString(primaryConstructor.valueParameterList)
|
if (parameters == "()") return ""
|
||||||
|
return parameters
|
||||||
val buff = "$visibility$annotations"
|
} else {
|
||||||
if (buff.isNotEmpty()) return " ${buff}constructor$valueParameters"
|
if (header.isNotEmpty()) return "$Indent$header constructor$parameters\n"
|
||||||
else return valueParameters
|
return "${Indent}constructor$parameters\n"
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printSecondaryConstructors(protoConstructors: List<ProtoBuf.Constructor>) {
|
|
||||||
val secondaryConstructors = protoConstructors.filter { protoConstructor ->
|
|
||||||
Flags.IS_SECONDARY.get(protoConstructor.flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
secondaryConstructors.forEach { protoConstructor ->
|
|
||||||
val flags = protoConstructor.flags
|
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
|
||||||
val valueParameters = valueParametersToString(protoConstructor.valueParameterList)
|
|
||||||
printer.println(" ${visibility}constructor$valueParameters")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun typeParametersToString(typeParameters: List<ProtoBuf.TypeParameter>): String {
|
private fun ProtoBuf.Function.asString(): String {
|
||||||
if (typeParameters.isEmpty()) return ""
|
if (hasTypeTable()) TypeTables.push(typeTable)
|
||||||
|
val result = buildString {
|
||||||
val buff = StringBuilder("<")
|
val name = stringTable.getString(name)
|
||||||
typeParameters.dropLast(1).forEach { buff.append(typeParameterToString(it) + ", ") }
|
val visibility = Flags.VISIBILITY.get(flags).asString()
|
||||||
typeParameters.lastOrNull()?.let { buff.append(typeParameterToString(it)) }
|
val isInline = Flags.IS_INLINE.asString(flags)
|
||||||
return buff.append("> ").toString()
|
val receiverType = receiverType()
|
||||||
|
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.functionAnnotation), "\n")
|
||||||
|
val typeParameters = typeParameterList.joinToString("<", "> ") { it.asString() }
|
||||||
|
val valueParameters = valueParameterList.joinToString(", ", "(", ")") { it.asString() }
|
||||||
|
val returnType = returnType()
|
||||||
|
append("$annotations$Indent$visibility${isInline}fun $typeParameters$receiverType$name$valueParameters$returnType\n")
|
||||||
|
}
|
||||||
|
if (hasTypeTable()) TypeTables.pop()
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun typeParameterToString(protoTypeParameter: ProtoBuf.TypeParameter): String {
|
private fun ProtoBuf.Property.asString() = buildString {
|
||||||
val parameterName = stringTable.getString(protoTypeParameter.name)
|
val name = stringTable.getString(name)
|
||||||
val upperBounds = upperBoundsToString(protoTypeParameter.upperBoundIdList)
|
val isVar = Flags.IS_VAR.asString(flags)
|
||||||
val isReified = if (protoTypeParameter.reified) "reified " else ""
|
val modality = Flags.MODALITY.get(flags).asString()
|
||||||
val variance = varianceToString(protoTypeParameter.variance)
|
val visibility = Flags.VISIBILITY.get(flags).asString()
|
||||||
val protoAnnotations = protoTypeParameter.getExtension(KonanSerializerProtocol.typeParameterAnnotation)
|
val returnType = TypeTables.type(returnTypeId).asString()
|
||||||
val annotations = annotationsToString(protoAnnotations)
|
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.propertyAnnotation), "\n")
|
||||||
|
append("$annotations$Indent$modality$visibility$isVar$name: $returnType\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun nestedClassAsString(nestedClass: Int): String {
|
||||||
|
val nestedClassName = stringTable.getString(nestedClass)
|
||||||
|
return "${Indent}class $nestedClassName\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Function.receiverType(): String {
|
||||||
|
if (!hasReceiverTypeId()) return ""
|
||||||
|
return TypeTables.type(receiverTypeId).asString() + "."
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Function.returnType(): String {
|
||||||
|
val returnType = TypeTables.type(returnTypeId).asString()
|
||||||
|
return if (returnType == "Unit") "" else ": " + returnType
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.TypeParameter.asString(): String {
|
||||||
|
val parameterName = stringTable.getString(name)
|
||||||
|
val upperBounds = upperBoundsToString(upperBoundIdList)
|
||||||
|
val isReified = if (reified) "reified " else ""
|
||||||
|
val variance = variance.asString()
|
||||||
|
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.typeParameterAnnotation))
|
||||||
return "$annotations$isReified$variance$parameterName$upperBounds"
|
return "$annotations$isReified$variance$parameterName$upperBounds"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun upperBoundsToString(upperBounds: List<Int>): String {
|
||||||
|
if (upperBounds.isEmpty()) return ""
|
||||||
|
return ": " + TypeTables.type(upperBounds.first()).asString()
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun constructorValueParametersToString(valueParameters: List<ProtoBuf.ValueParameter>): String {
|
private fun ProtoBuf.ValueParameter.asString(isConstructor: Boolean = false): String {
|
||||||
if (valueParameters.isEmpty()) return ""
|
val parameterName = stringTable.getString(name)
|
||||||
|
val type = TypeTables.type(typeId).asString()
|
||||||
|
|
||||||
val buff = StringBuilder("(")
|
val isVar = if (isConstructor) Flags.IS_VAR.asString(flags) else ""
|
||||||
valueParameters.dropLast(1).forEach { valueParameter ->
|
val isCrossInline = Flags.IS_CROSSINLINE.asString(flags)
|
||||||
val parameter = constructorValueParameterToString(valueParameter)
|
val visibility0 = Flags.VISIBILITY.get(flags).asString()
|
||||||
buff.append("$parameter, ")
|
val visibility = if (visibility0 == "internal ") "" else visibility0
|
||||||
|
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.parameterAnnotation))
|
||||||
|
|
||||||
|
return "$annotations$isCrossInline$visibility$isVar$parameterName: $type"
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun annotationsToString(annotations: List<ProtoBuf.Annotation>, separator: String = " "): String {
|
||||||
|
return buildString { annotations.forEach { append(it.asString() + separator) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Annotation.asString(): String {
|
||||||
|
val builder = StringBuilder("$Indent@${getName(id)}")
|
||||||
|
argumentList.joinTo(builder, prefix = "(", postfix = ")") { it.value.asString() }
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Annotation.Argument.Value.asString(): String {
|
||||||
|
val builder = StringBuilder(
|
||||||
|
when {
|
||||||
|
hasAnnotation() -> annotation.asString()
|
||||||
|
hasStringValue() -> "\"${stringTable.getString(stringValue)}\""
|
||||||
|
hasDoubleValue() -> doubleValue.toString()
|
||||||
|
hasFloatValue() -> floatValue.toString()
|
||||||
|
hasIntValue() -> intValue.toString()
|
||||||
|
hasClassId() -> getName(classId) + "."
|
||||||
|
hasEnumValueId() -> stringTable.getString(enumValueId)
|
||||||
|
else -> ""
|
||||||
|
})
|
||||||
|
|
||||||
|
arrayElementList.joinTo(builder) { it.asString() }
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--- Types ---------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Type.asValueString(): String {
|
||||||
|
val builder = StringBuilder(name())
|
||||||
|
argumentList.joinTo(builder, "<", ">") { it.type.asString() }
|
||||||
|
if (nullable) builder.append("?")
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Type.asCallableString(): String {
|
||||||
|
val builder = StringBuilder()
|
||||||
|
argumentList.dropLast(1).joinTo(builder, ", ", "(", ") -> ") { TypeTables.type(it.typeId).asString() }
|
||||||
|
val returnType = argumentList.lastOrNull()
|
||||||
|
if (returnType != null) builder.append(TypeTables.type(returnType.typeId).asString())
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Type.name() = when {
|
||||||
|
hasClassName() -> getName(className)
|
||||||
|
hasTypeParameterName() -> stringTable.getString(typeParameterName)
|
||||||
|
hasTypeAliasName() -> stringTable.getString(typeAliasName)
|
||||||
|
else -> "undefined"
|
||||||
}
|
}
|
||||||
valueParameters.lastOrNull()?.let { valueParameter ->
|
|
||||||
val parameter = constructorValueParameterToString(valueParameter)
|
|
||||||
buff.append(parameter)
|
|
||||||
}
|
|
||||||
return buff.append(")").toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun valueParametersToString(valueParameters: List<ProtoBuf.ValueParameter>): String {
|
private fun ProtoBuf.Type.isCallable() = name().contains("Function")
|
||||||
val buff = StringBuilder("(")
|
|
||||||
valueParameters.dropLast(1).forEach { valueParameter ->
|
|
||||||
val parameter = valueParameterToString(valueParameter)
|
|
||||||
buff.append("$parameter, ")
|
|
||||||
}
|
|
||||||
valueParameters.lastOrNull()?.let { valueParameter ->
|
|
||||||
val parameter = valueParameterToString(valueParameter)
|
|
||||||
buff.append(parameter)
|
|
||||||
}
|
|
||||||
return buff.append(")").toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun valueParameterToString(protoValueParameter: ProtoBuf.ValueParameter): String {
|
private fun ProtoBuf.Type.asString() = if (isCallable()) asCallableString() else asValueString()
|
||||||
val parameterName = stringTable.getString(protoValueParameter.name)
|
|
||||||
val typeId = protoValueParameter.typeId
|
|
||||||
val type = if (isCallableType(typeId)) callableTypeToString(typeId)
|
|
||||||
else typeToString(typeId)
|
|
||||||
|
|
||||||
val flags = protoValueParameter.flags
|
|
||||||
val isCrossInline = crossInlineToString(Flags.IS_CROSSINLINE.get(flags))
|
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
|
||||||
val protoAnnotations = protoValueParameter.getExtension(KonanSerializerProtocol.parameterAnnotation)
|
|
||||||
val annotations = annotationsToString(protoAnnotations)
|
|
||||||
|
|
||||||
return "$annotations$isCrossInline$visibility$parameterName: $type"
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun constructorValueParameterToString(protoValueParameter: ProtoBuf.ValueParameter): String {
|
|
||||||
val flags = protoValueParameter.flags
|
|
||||||
val isVar = if (Flags.IS_VAR.get(flags)) "var " else "val "
|
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
|
||||||
val parameterName = stringTable.getString(protoValueParameter.name)
|
|
||||||
val type = typeToString(protoValueParameter.typeId)
|
|
||||||
val protoAnnotations = protoValueParameter.getExtension(KonanSerializerProtocol.parameterAnnotation)
|
|
||||||
val annotations = annotationsToString(protoAnnotations)
|
|
||||||
|
|
||||||
return "$annotations$visibility$isVar$parameterName: $type"
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun enumEntryToString(protoEnumEntry: ProtoBuf.EnumEntry): String {
|
|
||||||
val buff = stringTable.getString(protoEnumEntry.name)
|
|
||||||
return buff
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printCompanionObject(protoClass: ProtoBuf.Class) {
|
|
||||||
if (!protoClass.hasCompanionObjectName()) return
|
|
||||||
val companionObjectName = stringTable.getString(protoClass.companionObjectName)
|
|
||||||
printer.println("companion object $companionObjectName")
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun printNestedClass(nestedClass: Int) {
|
|
||||||
val nestedClassName = stringTable.getString(nestedClass)
|
|
||||||
printer.println("class $nestedClassName")
|
|
||||||
}
|
|
||||||
|
|
||||||
//--- Helpers -------------------------------------------------------------//
|
//--- Helpers -------------------------------------------------------------//
|
||||||
|
|
||||||
private fun getShortName(id: Int): String {
|
private fun getName(id: Int): String {
|
||||||
val shortQualifiedName = qualifiedNameTable.getQualifiedName(id)
|
val shortQualifiedName = qualifiedNameTable.getQualifiedName(id)
|
||||||
val shortStringId = shortQualifiedName.shortName
|
val shortStringId = shortQualifiedName.shortName
|
||||||
val shortName = stringTable.getString(shortStringId)
|
return stringTable.getString(shortStringId)
|
||||||
return shortName
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
fun getParentName(id: Int): String {
|
private fun getParentName(id: Int) = qualifiedNameTable.getQualifiedName(id).parentQualifiedName
|
||||||
val childQualifiedName = qualifiedNameTable.getQualifiedName(id)
|
private fun ProtoBuf.Class.primaryConstructor() = constructorList.first { !Flags.IS_SECONDARY.get(it.flags) }
|
||||||
val parentQualifiedId = childQualifiedName.parentQualifiedName
|
private fun ProtoBuf.Class.secondaryConstructors() = constructorList.filter { Flags.IS_SECONDARY.get(it.flags) }
|
||||||
val parentQualifiedName = qualifiedNameTable.getQualifiedName(parentQualifiedId)
|
|
||||||
val parentStringId = parentQualifiedName.shortName
|
//-------------------------------------------------------------------------//
|
||||||
val parentName = stringTable.getString(parentStringId)
|
|
||||||
return parentName
|
private fun ProtoBuf.TypeParameter.Variance.asString(): String {
|
||||||
|
if (this == ProtoBuf.TypeParameter.Variance.INV) return ""
|
||||||
|
return toString().toLowerCase() + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun varianceToString(variance: ProtoBuf.TypeParameter.Variance): String {
|
private fun ProtoBuf.Modality.asString() =
|
||||||
if (variance == ProtoBuf.TypeParameter.Variance.INV) return ""
|
when (this) {
|
||||||
return variance.toString().toLowerCase() + " "
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun upperBoundsToString(upperBounds: List<Int>): String {
|
|
||||||
val buff = StringBuilder()
|
|
||||||
upperBounds.forEach { buff.append(": " + typeToString(it)) }
|
|
||||||
return buff.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun typeToString(typeId: Int): String {
|
|
||||||
val type = typeTableStack.last().getType(typeId)
|
|
||||||
val buff = StringBuilder(typeName(type))
|
|
||||||
|
|
||||||
val argumentList = type.argumentList
|
|
||||||
if (argumentList.isNotEmpty()) {
|
|
||||||
buff.append("<")
|
|
||||||
argumentList.dropLast(1).forEach { buff.append("${typeToString(it.typeId)}, ") }
|
|
||||||
argumentList.lastOrNull()?.let {
|
|
||||||
if (typeId != it.typeId)
|
|
||||||
buff.append(typeToString(it.typeId))
|
|
||||||
}
|
|
||||||
buff.append(">")
|
|
||||||
}
|
|
||||||
if (type.nullable) buff.append("?")
|
|
||||||
return buff.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun isCallableType(typeId: Int): Boolean {
|
|
||||||
val type = typeTableStack.last().getType(typeId)
|
|
||||||
return typeName(type).contains("Function")
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun callableTypeToString(typeId: Int): String {
|
|
||||||
val callableType = typeTableStack.last().getType(typeId)
|
|
||||||
val buff = StringBuilder("(")
|
|
||||||
val argumentTypes = callableType.argumentList.dropLast(1)
|
|
||||||
val returnType = callableType.argumentList.lastOrNull()
|
|
||||||
|
|
||||||
argumentTypes.dropLast(1).forEach { buff.append("${typeToString(it.typeId)}, ") }
|
|
||||||
argumentTypes.lastOrNull()?.let { buff.append(typeToString(it.typeId)) }
|
|
||||||
buff.append(") -> ")
|
|
||||||
if (returnType != null) buff.append(typeToString(returnType.typeId))
|
|
||||||
return buff.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun typeName(type: ProtoBuf.Type): String {
|
|
||||||
var typeName = "undefined"
|
|
||||||
if (type.hasClassName()) {
|
|
||||||
val qualifiedName = qualifiedNameTable.getQualifiedName(type.className)
|
|
||||||
val typeNameId = qualifiedName.shortName
|
|
||||||
typeName = stringTable.getString(typeNameId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.hasTypeParameterName()) {
|
|
||||||
typeName = stringTable.getString(type.typeParameterName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.hasTypeAliasName()) {
|
|
||||||
typeName = stringTable.getString(type.typeAliasName)
|
|
||||||
}
|
|
||||||
return typeName
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun modalityToString(modality: ProtoBuf.Modality) =
|
|
||||||
when (modality) {
|
|
||||||
ProtoBuf.Modality.FINAL -> ""
|
ProtoBuf.Modality.FINAL -> ""
|
||||||
ProtoBuf.Modality.OPEN -> "open "
|
ProtoBuf.Modality.OPEN -> "open "
|
||||||
ProtoBuf.Modality.ABSTRACT -> "abstract "
|
ProtoBuf.Modality.ABSTRACT -> "abstract "
|
||||||
@@ -523,64 +337,57 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
|
|||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun visibilityToString(visibility: ProtoBuf.Visibility) =
|
private fun ProtoBuf.Visibility.asString() =
|
||||||
when (visibility) {
|
when (this) {
|
||||||
ProtoBuf.Visibility.INTERNAL -> "internal "
|
ProtoBuf.Visibility.INTERNAL -> "internal "
|
||||||
ProtoBuf.Visibility.PRIVATE -> "private "
|
ProtoBuf.Visibility.PRIVATE -> "private "
|
||||||
ProtoBuf.Visibility.PROTECTED -> "protected "
|
ProtoBuf.Visibility.PROTECTED -> "protected "
|
||||||
ProtoBuf.Visibility.PUBLIC -> ""
|
ProtoBuf.Visibility.PUBLIC -> ""
|
||||||
ProtoBuf.Visibility.PRIVATE_TO_THIS -> "private "
|
ProtoBuf.Visibility.PRIVATE_TO_THIS -> "private "
|
||||||
ProtoBuf.Visibility.LOCAL -> "local "
|
ProtoBuf.Visibility.LOCAL -> "local "
|
||||||
|
else -> "invalid visibility "
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
private fun inlineToString(isInline: Boolean): String {
|
private fun ProtoBuf.Class.Kind.asString() =
|
||||||
if (isInline) return "inline "
|
when (this) {
|
||||||
return ""
|
ProtoBuf.Class.Kind.CLASS -> "class "
|
||||||
}
|
ProtoBuf.Class.Kind.INTERFACE -> "interface "
|
||||||
|
ProtoBuf.Class.Kind.ENUM_CLASS -> "enum class "
|
||||||
//-------------------------------------------------------------------------//
|
ProtoBuf.Class.Kind.ENUM_ENTRY -> "enum "
|
||||||
|
ProtoBuf.Class.Kind.ANNOTATION_CLASS -> "annotation class "
|
||||||
private fun receiverTypeToString(protoFunction: ProtoBuf.Function): String {
|
ProtoBuf.Class.Kind.OBJECT -> "object "
|
||||||
if (!protoFunction.hasReceiverTypeId()) return ""
|
ProtoBuf.Class.Kind.COMPANION_OBJECT -> "companion object "
|
||||||
val receiverType = typeToString(protoFunction.receiverTypeId)
|
else -> "invalid member kind "
|
||||||
return receiverType + "."
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun crossInlineToString(isCrossInline: Boolean): String {
|
|
||||||
if (isCrossInline) return "crossinline "
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun returnTypeToString(protoFunction: ProtoBuf.Function): String {
|
|
||||||
val returnType = typeToString(protoFunction.returnTypeId)
|
|
||||||
if (returnType == "Unit") return ""
|
|
||||||
return ": " + returnType
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
private fun classOrInterface(protoClass: ProtoBuf.Class): String {
|
|
||||||
val flags = protoClass.flags
|
|
||||||
val classKind = Flags.CLASS_KIND.get(flags)!!
|
|
||||||
val modality = modalityToString(Flags.MODALITY.get(flags))
|
|
||||||
val visibility = visibilityToString(Flags.VISIBILITY.get(flags))
|
|
||||||
|
|
||||||
val buff = StringBuilder("$modality$visibility")
|
|
||||||
when (classKind) {
|
|
||||||
ProtoBuf.Class.Kind.CLASS -> buff.append("class")
|
|
||||||
ProtoBuf.Class.Kind.INTERFACE -> buff.append("interface")
|
|
||||||
ProtoBuf.Class.Kind.ENUM_CLASS -> buff.append("enum class")
|
|
||||||
ProtoBuf.Class.Kind.ENUM_ENTRY -> buff.append("enum")
|
|
||||||
ProtoBuf.Class.Kind.ANNOTATION_CLASS -> buff.append("annotation class")
|
|
||||||
ProtoBuf.Class.Kind.OBJECT -> buff.append("object")
|
|
||||||
ProtoBuf.Class.Kind.COMPANION_OBJECT -> buff.append("companion object")
|
|
||||||
}
|
}
|
||||||
return buff.toString()
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun Flags.BooleanFlagField.asString(flags: Int) =
|
||||||
|
when(this) {
|
||||||
|
IS_INLINE -> if (Flags.IS_INLINE .get(flags)) "inline " else ""
|
||||||
|
IS_VAR -> if (Flags.IS_VAR .get(flags)) "var " else "val "
|
||||||
|
IS_CROSSINLINE -> if (Flags.IS_CROSSINLINE.get(flags)) "crossinline " else ""
|
||||||
|
IS_NOINLINE -> if (Flags.IS_NOINLINE .get(flags)) "noinline " else ""
|
||||||
|
else -> "unknown flag"
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun ProtoBuf.Class.isRoot() = getParentName(fqName) == -1
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun <T> Collection<T>.joinTo(builder: StringBuilder, prefix: CharSequence = "", postfix: CharSequence = "", transform: ((T) -> CharSequence)? = null): String {
|
||||||
|
if (isEmpty()) return ""
|
||||||
|
return joinTo(builder, ", ", prefix, postfix, -1, "...", transform).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------//
|
||||||
|
|
||||||
|
private fun <T> Collection<T>.joinToString(prefix: CharSequence = "", postfix: CharSequence = "", transform: ((T) -> CharSequence)? = null): String {
|
||||||
|
if (isEmpty()) return ""
|
||||||
|
return joinTo(StringBuilder(), ", ", prefix, postfix, -1, "...", transform).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user