From 4c9e67918004e6b583debeffba0193a2f446041f Mon Sep 17 00:00:00 2001 From: Ilya Matveev Date: Fri, 9 Feb 2018 18:44:31 +0700 Subject: [PATCH] [klib] Fix stack overflow during type parameters printing --- .../kotlin/cli/klib/PrettyPrinter.kt | 60 ++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) 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 c8d0ea79f21..b34a55d557f 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 @@ -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"); * 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.parseModuleHeader import org.jetbrains.kotlin.backend.konan.serialization.parsePackageFragment +import org.jetbrains.kotlin.codegen.mapSupertypesNames import org.jetbrains.kotlin.renderer.renderFqName 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.deserialization.* import org.jetbrains.kotlin.utils.Printer import java.lang.System.out @@ -72,10 +74,11 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, //-------------------------------------------------------------------------// object TypeTables { - val tables = mutableListOf() - fun push(table: ProtoBuf.TypeTable) { tables.add(table) } + val tables = mutableListOf() + fun push(table: ProtoBuf.TypeTable) { tables.add(TypeTable(table)) } 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 .joinToString("<", "> ") { it.asString() } .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 primaryConstructor = if (!isObject) { + primaryConstructor()?.asString(isEnumClass, true) ?: "" + } else { + "" + } + append("$annotations$Indent$modality$visibility$classKind$className$typeParameters$primaryConstructor$supertypes") buildBody { @@ -158,9 +167,9 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, //-------------------------------------------------------------------------// - private fun supertypesToString(supertypesId: List): String { - val result = supertypesId.joinToString { - val supertype = TypeTables.type(it).asString() + private fun supertypesToString(supertypes: List): String { + val result = supertypes.joinToString { + val supertype = it.asString() // TODO: Use fq names here. 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 modality = Flags.MODALITY.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") 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 { - if (!hasReceiverTypeId()) return "" - return TypeTables.type(receiverTypeId).asString() + "." + return receiverType(TypeTables.peek())?.let { it.asString() + "." } ?: "" } //-------------------------------------------------------------------------// private fun ProtoBuf.Function.returnType(): String { - val returnType = TypeTables.type(returnTypeId).asString() + val returnType = returnType(TypeTables.peek()).asString() return if (returnType == "Unit") "" else ": " + returnType } @@ -248,23 +256,23 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, private fun ProtoBuf.TypeParameter.asString(): String { val parameterName = stringTable.getString(name) - val upperBounds = upperBoundsToString(upperBoundIdList) + val upperBounds = upperBoundsToString(upperBounds(TypeTables.peek())) val isReified = if (reified) "reified " else "" val variance = variance.asString() val annotations = annotationsToString(getExtension(KonanSerializerProtocol.typeParameterAnnotation)) return "$annotations$isReified$variance$parameterName$upperBounds" } - private fun upperBoundsToString(upperBounds: List): String { + private fun upperBoundsToString(upperBounds: List): String { if (upperBounds.isEmpty()) return "" - return ": " + TypeTables.type(upperBounds.first()).asString() + return ": " + upperBounds.first().asString() } //-------------------------------------------------------------------------// private fun ProtoBuf.ValueParameter.asString(): String { 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 annotations = annotationsToString(getExtension(KonanSerializerProtocol.parameterAnnotation)) @@ -310,7 +318,8 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, private fun ProtoBuf.Type.asValueString(): String { 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("?") return builder.toString() } @@ -319,9 +328,15 @@ class PackageFragmentPrinter(val packageFragment: KonanLinkData.PackageFragment, private fun ProtoBuf.Type.asCallableString(): String { 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() - if (returnType != null) builder.append(TypeTables.type(returnType.typeId).asString()) + if (returnType != null) { + // TODO: We use Any? because Function is (P) -> Any?. Can we make a more clever check? + builder.append(returnType.type(TypeTables.peek())?.asString() ?: "Any?") + } 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() = when (this) { ProtoBuf.Class.Kind.CLASS -> "class "