diff --git a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/PrettyPrinter.kt b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/PrettyPrinter.kt index eff9ead9023..b25a1573647 100644 --- a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/PrettyPrinter.kt +++ b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/PrettyPrinter.kt @@ -21,6 +21,12 @@ import org.jetbrains.kotlin.backend.konan.serialization.Base64 import org.jetbrains.kotlin.backend.konan.serialization.parseModuleHeader 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.ProtoBuf +import org.jetbrains.kotlin.utils.Printer +import java.lang.System.out + class PrettyPrinter(val library: Base64, val packageLoader: (String) -> Base64) { private val moduleHeader: KonanLinkData.Library @@ -33,9 +39,462 @@ class PrettyPrinter(val library: Base64, val packageLoader: (String) -> Base64) get() = moduleHeader.packageFragmentNameList fun printPackageFragment(fqname: String) { - if (fqname.isNotEmpty()) println("package $fqname" ) - println("\tHere goes the \"$fqname\" package body.\n\tIt is not implemented yet.\n") - // TODO: implement deserialized package protobuf print out. + if (fqname.isNotEmpty()) println("package $fqname" ) + if (fqname.isNotEmpty()) println("package $fqname" ) + val fragment = packageFragment(fqname) + PackageFragmentPrinter(fragment, out).print() } } +class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, out: Appendable) { + val printer = Printer(out, " ") + val stringTable = packageFragment.stringTable!! + val qualifiedNameTable = packageFragment.nameTable!! + var typeTable: ProtoBuf.TypeTable? = packageFragment.`package`.typeTable + + //-------------------------------------------------------------------------// + + fun print() { + val protoTypeAliases = packageFragment.`package`.typeAliasList + protoTypeAliases.forEach { protoTypeAlias -> + printTypeAlias(protoTypeAlias) + } + + val protoClasses = packageFragment.classes.classesList // ProtoBuf classes + protoClasses.forEach { protoClass -> + val classKind = Flags.CLASS_KIND.get(protoClass.flags) + if (protoClass.hasTypeTable()) typeTable = protoClass.typeTable + when (classKind) { + ProtoBuf.Class.Kind.CLASS -> printClass(protoClass) + 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 -> printObject(protoClass) + ProtoBuf.Class.Kind.COMPANION_OBJECT -> printObject(protoClass) + } + } + + val protoFunctions = packageFragment.`package`.functionList + protoFunctions.forEach { protoFunction -> + if (protoFunction.hasTypeTable()) typeTable = protoFunction.typeTable + else typeTable = packageFragment.`package`.typeTable + printFunction(protoFunction) + } + + val protoProperties = packageFragment.`package`.propertyList + protoProperties.forEach { protoProperty -> + printProperty(protoProperty) + } + } + + //-------------------------------------------------------------------------// + + fun printTypeAlias(protoTypeAlias: ProtoBuf.TypeAlias) { + val aliasName = stringTable.getString(protoTypeAlias.name) + val typeName = typeToString(protoTypeAlias.expandedTypeId) + printer.println("typealias $aliasName = $typeName") + } + + //-------------------------------------------------------------------------// + + fun printClass(protoClass: ProtoBuf.Class) { + 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") + } + + //-------------------------------------------------------------------------// + + fun printFunction(protoFunction: ProtoBuf.Function) { + 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) + + printAnnotations(annotations) + printer.println("$visibility${isInline}fun $typeParameters$receiverType$name$valueParameters") + } + + //-------------------------------------------------------------------------// + + 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 printEnum(protoEnum: ProtoBuf.Class) { + 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 + + 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") + } + + //-------------------------------------------------------------------------// + + 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") + } + + //-------------------------------------------------------------------------// + + fun printAnnotations(protoAnnotations: List) { + val annotations = annotationsToString(protoAnnotations) + if (annotations.isEmpty()) return + printer.println(annotations) + } + + //-------------------------------------------------------------------------// + + fun annotationsToString(protoAnnotations: List): String { + val buff = StringBuilder() + protoAnnotations.forEach { protoAnnotation -> + val annotation = getShortName(protoAnnotation.id) + buff.append("@$annotation ") + } + return buff.toString() + } + + //-------------------------------------------------------------------------// + + fun supertypesToString(supertypesId: List): String { + val buff = StringBuilder(": ") + supertypesId.dropLast(1).forEach { supertypeId -> + val supertype = typeToString(supertypeId) + if (supertype != "Any") buff.append("$supertype, ") + } + supertypesId.lastOrNull()?.let { supertypeId -> + val supertype = typeToString(supertypeId) + if (supertype != "Any") buff.append(supertype) + } + + if (buff.toString() == ": ") return "" + return buff.toString() + } + + //-------------------------------------------------------------------------// + + fun primaryConstructorToString(protoConstructors: List): String { + val primaryConstructor = protoConstructors.firstOrNull { protoConstructor -> + !Flags.IS_SECONDARY.get(protoConstructor.flags) + } ?: return "" + + val flags = primaryConstructor.flags + val visibility = visibilityToString(Flags.VISIBILITY.get(flags)) + val annotations = annotationsToString(primaryConstructor.getExtension(KonanSerializerProtocol.constructorAnnotation)) + val valueParameters = constructorValueParametersToString(primaryConstructor.valueParameterList) + + val buff = "$visibility$annotations" + if (buff.isNotEmpty()) return " ${buff}constructor$valueParameters" + else return valueParameters + } + + //-------------------------------------------------------------------------// + + fun printSecondaryConstructors(protoConstructors: List) { + 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") + } + } + + //-------------------------------------------------------------------------// + + fun typeParametersToString(typeParameters: List): String { + if (typeParameters.isEmpty()) return "" + + val buff = StringBuilder("<") + typeParameters.dropLast(1).forEach { buff.append(typeParameterToString(it) + ", ") } + typeParameters.lastOrNull()?.let { buff.append(typeParameterToString(it)) } + return buff.append("> ").toString() + } + + //-------------------------------------------------------------------------// + + fun typeParameterToString(protoTypeParameter: ProtoBuf.TypeParameter): String { + val parameterName = stringTable.getString(protoTypeParameter.name) + val upperBounds = upperBoundsToString(protoTypeParameter.upperBoundIdList) + val isReified = if (protoTypeParameter.reified) "reified " else "" + val variance = varianceToString(protoTypeParameter.variance) + val protoAnnotations = protoTypeParameter.getExtension(KonanSerializerProtocol.typeParameterAnnotation) + val annotations = annotationsToString(protoAnnotations) + + return "$annotations$isReified$variance$parameterName$upperBounds" + } + + //-------------------------------------------------------------------------// + + fun constructorValueParametersToString(valueParameters: List): String { + if (valueParameters.isEmpty()) return "" + + val buff = StringBuilder("(") + valueParameters.dropLast(1).forEach { valueParameter -> + val parameter = constructorValueParameterToString(valueParameter) + buff.append("$parameter, ") + } + valueParameters.lastOrNull()?.let { valueParameter -> + val parameter = constructorValueParameterToString(valueParameter) + buff.append(parameter) + } + return buff.append(")").toString() + } + + //-------------------------------------------------------------------------// + + fun valueParametersToString(valueParameters: List): String { + if (valueParameters.isEmpty()) return "" + + 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() + } + + //-------------------------------------------------------------------------// + + fun valueParameterToString(protoValueParameter: ProtoBuf.ValueParameter): String { + val parameterName = stringTable.getString(protoValueParameter.name) + val type = typeToString(protoValueParameter.typeId) + val flags = protoValueParameter.flags + val isCrossInline = crossInlineToString(Flags.IS_CROSSINLINE.get(flags)) + val protoAnnotations = protoValueParameter.getExtension(KonanSerializerProtocol.parameterAnnotation) + val annotations = annotationsToString(protoAnnotations) + + return "$annotations$isCrossInline$parameterName: $type" + } + + //-------------------------------------------------------------------------// + + fun constructorValueParameterToString(protoValueParameter: ProtoBuf.ValueParameter): String { + val flags = protoValueParameter.flags + val isVar = if (Flags.IS_VAR.get(flags)) "var " else "val " + val parameterName = stringTable.getString(protoValueParameter.name) + val type = typeToString(protoValueParameter.typeId) + val protoAnnotations = protoValueParameter.getExtension(KonanSerializerProtocol.parameterAnnotation) + val annotations = annotationsToString(protoAnnotations) + + return "$annotations$isVar$parameterName: $type" + } + + //-------------------------------------------------------------------------// + + fun enumEntryToString(protoEnumEntry: ProtoBuf.EnumEntry): String { + val buff = stringTable.getString(protoEnumEntry.name) + return buff + } + + //-------------------------------------------------------------------------// + + fun printCompanionObject(protoClass: ProtoBuf.Class) { + if (!protoClass.hasCompanionObjectName()) return + val companionObjectName = stringTable.getString(protoClass.companionObjectName) + printer.println("companion object $companionObjectName") + } + + //-------------------------------------------------------------------------// + + fun printNestedClass(nestedClass: Int) { + val nestedClassName = stringTable.getString(nestedClass) + printer.println("class $nestedClassName") + } + + //--- Helpers -------------------------------------------------------------// + + fun getShortName(id: Int): String { + val shortQualifiedName = qualifiedNameTable.getQualifiedName(id) + val shortStringId = shortQualifiedName.shortName + val shortName = stringTable.getString(shortStringId) + return shortName + } + + //-------------------------------------------------------------------------// + + fun getParentName(id: Int): String { + val childQualifiedName = qualifiedNameTable.getQualifiedName(id) + val parentQualifiedId = childQualifiedName.parentQualifiedName + val parentQualifiedName = qualifiedNameTable.getQualifiedName(parentQualifiedId) + val parentStringId = parentQualifiedName.shortName + val parentName = stringTable.getString(parentStringId) + return parentName + } + + //-------------------------------------------------------------------------// + + fun varianceToString(variance: ProtoBuf.TypeParameter.Variance): String { + if (variance == ProtoBuf.TypeParameter.Variance.INV) return "" + return variance.toString().toLowerCase() + } + + //-------------------------------------------------------------------------// + + fun upperBoundsToString(upperBounds: List): String { + val buff = StringBuilder() + upperBounds.forEach { buff.append(": " + typeToString(it)) } + return buff.toString() + } + + //-------------------------------------------------------------------------// + + fun typeToString(typeId: Int): String { + val type = typeTable!!.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() + } + + //-------------------------------------------------------------------------// + + 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 + } + + //-------------------------------------------------------------------------// + + fun modalityToString(modality: ProtoBuf.Modality) = + when (modality) { + ProtoBuf.Modality.FINAL -> "" + ProtoBuf.Modality.OPEN -> "open " + ProtoBuf.Modality.ABSTRACT -> "abstract " + ProtoBuf.Modality.SEALED -> "sealed " + } + + //-------------------------------------------------------------------------// + + fun visibilityToString(visibility: ProtoBuf.Visibility) = + when (visibility) { + ProtoBuf.Visibility.INTERNAL -> "internal " + ProtoBuf.Visibility.PRIVATE -> "private " + ProtoBuf.Visibility.PROTECTED -> "protected " + ProtoBuf.Visibility.PUBLIC -> "" + ProtoBuf.Visibility.PRIVATE_TO_THIS -> "private " + ProtoBuf.Visibility.LOCAL -> "local " + } + + //-------------------------------------------------------------------------// + + fun inlineToString(isInline: Boolean): String { + if (isInline) return "inline " + return "" + } + + //-------------------------------------------------------------------------// + + fun receiverTypeToString(protoFunction: ProtoBuf.Function): String { + if (!protoFunction.hasReceiverTypeId()) return "" + val receiverType = typeToString(protoFunction.receiverTypeId) + return receiverType + "." + } + + //-------------------------------------------------------------------------// + + fun crossInlineToString(isCrossInline: Boolean): String { + if (isCrossInline) return "crossinline " + return "" + } + + //-------------------------------------------------------------------------// + + 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() + when (classKind) { + ProtoBuf.Class.Kind.CLASS -> buff.append("$visibility${modality}class") + ProtoBuf.Class.Kind.INTERFACE -> buff.append("interface") + ProtoBuf.Class.Kind.ANNOTATION_CLASS -> buff.append("annotation") + ProtoBuf.Class.Kind.OBJECT -> buff.append("object") + else -> assert(false) + } + return buff.toString() + } +} \ No newline at end of file