[klib] Fix stack overflow during type parameters printing

This commit is contained in:
Ilya Matveev
2018-02-09 18:44:31 +07:00
committed by ilmat192
parent f4cb918a1c
commit 4c9e679180
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -19,11 +19,13 @@ package org.jetbrains.kotlin.cli.klib
import org.jetbrains.kotlin.backend.konan.serialization.KonanSerializerProtocol 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.codegen.mapSupertypesNames
import org.jetbrains.kotlin.renderer.renderFqName import org.jetbrains.kotlin.renderer.renderFqName
import org.jetbrains.kotlin.serialization.Flags 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.KonanLinkData
import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.*
import org.jetbrains.kotlin.utils.Printer import org.jetbrains.kotlin.utils.Printer
import java.lang.System.out import java.lang.System.out
@@ -72,10 +74,11 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
object TypeTables { object TypeTables {
val tables = mutableListOf<ProtoBuf.TypeTable>() val tables = mutableListOf<TypeTable>()
fun push(table: ProtoBuf.TypeTable) { tables.add(table) } fun push(table: ProtoBuf.TypeTable) { tables.add(TypeTable(table)) }
fun pop() = tables.removeAt(tables.lastIndex) fun pop() = tables.removeAt(tables.lastIndex)
fun type(typeId: Int) = tables.last().getType(typeId)!! fun peek() = tables.last()
fun type(typeId: Int) = tables.last().get(typeId)
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
@@ -124,10 +127,16 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
val typeParameters = typeParameterList val typeParameters = typeParameterList
.joinToString("<", "> ") { it.asString() } .joinToString("<", "> ") { it.asString() }
.let { if (it.isEmpty()) " " else it } .let { if (it.isEmpty()) " " else it }
val primaryConstructor = primaryConstructor()?.asString(isEnumClass, true) ?: ""
val supertypes = supertypesToString(supertypeIdList) val supertypes = supertypesToString(supertypes(TypeTables.peek()))
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.classAnnotation), "\n") val annotations = annotationsToString(getExtension(KonanSerializerProtocol.classAnnotation), "\n")
val primaryConstructor = if (!isObject) {
primaryConstructor()?.asString(isEnumClass, true) ?: ""
} else {
""
}
append("$annotations$Indent$modality$visibility$classKind$className$typeParameters$primaryConstructor$supertypes") append("$annotations$Indent$modality$visibility$classKind$className$typeParameters$primaryConstructor$supertypes")
buildBody { buildBody {
@@ -158,9 +167,9 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun supertypesToString(supertypesId: List<Int>): String { private fun supertypesToString(supertypes: List<ProtoBuf.Type>): String {
val result = supertypesId.joinToString { val result = supertypes.joinToString {
val supertype = TypeTables.type(it).asString() val supertype = it.asString()
// TODO: Use fq names here. // TODO: Use fq names here.
if (supertype == "Any" || supertype.startsWith("Enum<")) "" else supertype if (supertype == "Any" || supertype.startsWith("Enum<")) "" else supertype
} }
@@ -218,7 +227,7 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
val isVar = Flags.IS_VAR.asString(flags) val isVar = Flags.IS_VAR.asString(flags)
val modality = Flags.MODALITY.get(flags).asString() val modality = Flags.MODALITY.get(flags).asString()
val visibility = Flags.VISIBILITY.get(flags).asString() val visibility = Flags.VISIBILITY.get(flags).asString()
val returnType = TypeTables.type(returnTypeId).asString() val returnType = returnType(TypeTables.peek()).asString()
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.propertyAnnotation), "\n") val annotations = annotationsToString(getExtension(KonanSerializerProtocol.propertyAnnotation), "\n")
append("$annotations$Indent$modality$visibility$isVar$name: $returnType\n") append("$annotations$Indent$modality$visibility$isVar$name: $returnType\n")
} }
@@ -233,14 +242,13 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun ProtoBuf.Function.receiverType(): String { private fun ProtoBuf.Function.receiverType(): String {
if (!hasReceiverTypeId()) return "" return receiverType(TypeTables.peek())?.let { it.asString() + "." } ?: ""
return TypeTables.type(receiverTypeId).asString() + "."
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun ProtoBuf.Function.returnType(): String { private fun ProtoBuf.Function.returnType(): String {
val returnType = TypeTables.type(returnTypeId).asString() val returnType = returnType(TypeTables.peek()).asString()
return if (returnType == "Unit") "" else ": " + returnType return if (returnType == "Unit") "" else ": " + returnType
} }
@@ -248,23 +256,23 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
private fun ProtoBuf.TypeParameter.asString(): String { private fun ProtoBuf.TypeParameter.asString(): String {
val parameterName = stringTable.getString(name) val parameterName = stringTable.getString(name)
val upperBounds = upperBoundsToString(upperBoundIdList) val upperBounds = upperBoundsToString(upperBounds(TypeTables.peek()))
val isReified = if (reified) "reified " else "" val isReified = if (reified) "reified " else ""
val variance = variance.asString() val variance = variance.asString()
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.typeParameterAnnotation)) 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 { private fun upperBoundsToString(upperBounds: List<ProtoBuf.Type>): String {
if (upperBounds.isEmpty()) return "" if (upperBounds.isEmpty()) return ""
return ": " + TypeTables.type(upperBounds.first()).asString() return ": " + upperBounds.first().asString()
} }
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private fun ProtoBuf.ValueParameter.asString(): String { private fun ProtoBuf.ValueParameter.asString(): String {
val parameterName = stringTable.getString(name) val parameterName = stringTable.getString(name)
val type = TypeTables.type(typeId).asString() val type = type(TypeTables.peek()).asString()
val isCrossInline = Flags.IS_CROSSINLINE.asString(flags) val isCrossInline = Flags.IS_CROSSINLINE.asString(flags)
val annotations = annotationsToString(getExtension(KonanSerializerProtocol.parameterAnnotation)) val annotations = annotationsToString(getExtension(KonanSerializerProtocol.parameterAnnotation))
@@ -310,7 +318,8 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
private fun ProtoBuf.Type.asValueString(): String { private fun ProtoBuf.Type.asValueString(): String {
val builder = StringBuilder(name()) val builder = StringBuilder(name())
argumentList.joinTo(builder, "<", ">") { it.type.asString() } // TODO: Use more clever check for <*>
argumentList.joinTo(builder, "<", ">") { it.type(TypeTables.peek())?.asString() ?: "*" }
if (nullable) builder.append("?") if (nullable) builder.append("?")
return builder.toString() return builder.toString()
} }
@@ -319,9 +328,15 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
private fun ProtoBuf.Type.asCallableString(): String { private fun ProtoBuf.Type.asCallableString(): String {
val builder = StringBuilder() val builder = StringBuilder()
argumentList.dropLast(1).joinTo(builder, ", ", "(", ") -> ") { TypeTables.type(it.typeId).asString() } argumentList.dropLast(1).joinTo(builder, ", ", "(", ") -> ") {
// TODO: We use Nothing because Function<*, R> is (Nothing) -> R. Can we make a more clever check?
it.type(TypeTables.peek())?.asString() ?: "Nothing"
}
val returnType = argumentList.lastOrNull() val returnType = argumentList.lastOrNull()
if (returnType != null) builder.append(TypeTables.type(returnType.typeId).asString()) if (returnType != null) {
// TODO: We use Any? because Function<P, *> is (P) -> Any?. Can we make a more clever check?
builder.append(returnType.type(TypeTables.peek())?.asString() ?: "Any?")
}
return builder.toString() return builder.toString()
} }
@@ -394,6 +409,11 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment,
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
private val ProtoBuf.Class.isObject: Boolean
get() = Flags.CLASS_KIND.get(flags).let {
it == ProtoBuf.Class.Kind.OBJECT || it == ProtoBuf.Class.Kind.COMPANION_OBJECT
}
private fun ProtoBuf.Class.Kind.asString() = private fun ProtoBuf.Class.Kind.asString() =
when (this) { when (this) {
ProtoBuf.Class.Kind.CLASS -> "class " ProtoBuf.Class.Kind.CLASS -> "class "