From cad3cb1bbe84294b8bb7100082bded14e8a90fd0 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Wed, 4 Dec 2019 17:45:09 +0300 Subject: [PATCH] [KLIB] Fix references to type made from TypeParameter in KotlinMangler - promote ABI version --- .../kotlin/ir/backend/js/utils/NameTables.kt | 6 +- .../common/serialization/KotlinMangler.kt | 134 +++++++++++++----- .../js/lower/serialization/ir/JsMangler.kt | 11 +- .../kotlin/library/KotlinAbiVersion.kt | 2 +- .../ir/semantics/IrBoxJsTestGenerated.java | 5 + .../js/test/semantics/BoxJsTestGenerated.java | 5 + .../typeParametersMangling.kt | 18 +++ 7 files changed, 139 insertions(+), 42 deletions(-) create mode 100644 js/js.translator/testData/box/inlineMultiModule/typeParametersMangling.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt index ee2cff5abb1..ede28dfa5c9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt @@ -9,7 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny import org.jetbrains.kotlin.backend.common.ir.isTopLevel import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin -import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsMangler +import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerForBE import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.types.isUnit @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty fun mapToKey(declaration: T): String { - return with(JsMangler) { + return with(JsManglerForBE) { if (declaration is IrDeclaration && isPublic(declaration)) { declaration.hashedMangle.toString() } else if (declaration is Signature) { @@ -32,7 +32,7 @@ fun mapToKey(declaration: T): String { } } -fun JsMangler.isPublic(declaration: IrDeclaration) = +fun JsManglerForBE.isPublic(declaration: IrDeclaration) = declaration.isExported() && declaration !is IrScript && declaration !is IrVariable && declaration !is IrValueParameter class NameTable( diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinMangler.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinMangler.kt index 80856a26f56..224b68184c2 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinMangler.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinMangler.kt @@ -6,7 +6,9 @@ package org.jetbrains.kotlin.backend.common.serialization import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* @@ -98,24 +100,52 @@ abstract class KotlinManglerImpl : KotlinMangler { return true } - private val publishedApiAnnotation = FqName("kotlin.PublishedApi") + private fun IrTypeParameter.effectiveParent(): IrDeclaration = when (val irParent = parent) { + is IrClass -> irParent + is IrConstructor -> irParent + is IrSimpleFunction -> irParent.correspondingPropertySymbol?.owner ?: irParent + else -> error("Unexpected type parameter container") + } - protected fun acyclicTypeMangler(visited: MutableSet, type: IrType): String { - val descriptor = (type.classifierOrNull as? IrTypeParameterSymbol)?.owner - if (descriptor != null) { - val upperBounds = if (visited.contains(descriptor)) "" else { + private fun collectTypeParameterContainers(element: IrElement): List { + val result = mutableListOf() - visited.add(descriptor) - - descriptor.superTypes.map { - val bound = acyclicTypeMangler(visited, it) - if (bound == "kotlin.Any?") "" else "_$bound" - }.joinToString("") + tailrec fun collectTypeParameterContainersImpl(element: IrElement) { + when (element) { + is IrConstructor -> result += element + is IrSimpleFunction -> result.add(element.correspondingPropertySymbol?.owner ?: element) + is IrProperty -> result += element + is IrClass -> result += element + else -> return } - return "#GENERIC${if (type.isMarkedNullable()) "?" else ""}$upperBounds" + + collectTypeParameterContainersImpl((element as IrDeclaration).parent) } - var hashString = type.getClass()?.run { fqNameForIrSerialization.asString() } ?: "" + collectTypeParameterContainersImpl(element) + + return result + } + + private fun mapTypeParameterContainers(element: IrElement): Map { + return collectTypeParameterContainers(element).mapIndexed { i, d -> d to i }.toMap() + } + + private val publishedApiAnnotation = FqName("kotlin.PublishedApi") + + protected open fun mangleTypeParameter(typeParameter: IrTypeParameter, typeParameterNamer: (IrTypeParameter) -> String): String { + return typeParameterNamer(typeParameter) + } + + protected fun acyclicTypeMangler(type: IrType, typeParameterNamer: (IrTypeParameter) -> String): String { + + var hashString = type.classifierOrNull?.let { + when (it) { + is IrClassSymbol -> it.owner.fqNameForIrSerialization.asString() + is IrTypeParameterSymbol -> mangleTypeParameter(it.owner, typeParameterNamer) + else -> error("Unexpected type constructor") + } + } ?: "" when (type) { is IrSimpleType -> { @@ -126,7 +156,7 @@ abstract class KotlinManglerImpl : KotlinMangler { is IrTypeProjection -> { val variance = it.variance.label val projection = if (variance == "") "" else "${variance}_" - projection + acyclicTypeMangler(visited, it.type) + projection + acyclicTypeMangler(it.type, typeParameterNamer) } else -> error(it) } @@ -142,32 +172,51 @@ abstract class KotlinManglerImpl : KotlinMangler { return hashString } - protected fun typeToHashString(type: IrType) = acyclicTypeMangler(mutableSetOf(), type) + protected fun typeToHashString(type: IrType, typeParameterNamer: (IrTypeParameter) -> String) = + acyclicTypeMangler(type, typeParameterNamer) - val IrValueParameter.extensionReceiverNamePart: String - get() = "@${typeToHashString(this.type)}." + fun IrValueParameter.extensionReceiverNamePart(typeParameterNamer: (IrTypeParameter) -> String): String = + "@${typeToHashString(this.type, typeParameterNamer)}." - open val IrFunction.argsPart - get() = this.valueParameters.map { - "${typeToHashString(it.type)}${if (it.isVararg) "_VarArg" else ""}" + open fun IrFunction.valueParamsPart(typeParameterNamer: (IrTypeParameter) -> String): String { + return this.valueParameters.map { + "${typeToHashString(it.type, typeParameterNamer)}${if (it.isVararg) "_VarArg" else ""}" }.joinToString(";") + } - open val IrFunction.signature: String - get() { - val extensionReceiverPart = this.extensionReceiverParameter?.extensionReceiverNamePart ?: "" - val argsPart = this.argsPart - // Distinguish value types and references - it's needed for calling virtual methods through bridges. - // Also is function has type arguments - frontend allows exactly matching overrides. - val signatureSuffix = - when { - this.typeParameters.isNotEmpty() -> "Generic" - returnType.isInlined -> "ValueType" - !returnType.isUnitOrNullableUnit() -> typeToHashString(returnType) - else -> "" - } - return "$extensionReceiverPart($argsPart)$signatureSuffix" + open fun IrFunction.typeParamsPart(typeParameters: List, typeParameterNamer: (IrTypeParameter) -> String): String { + if (typeParameters.isEmpty()) return "" + + fun mangleTypeParameter(index: Int, typeParameter: IrTypeParameter): String { + // We use type parameter index instead of name since changing name is not a binary-incompatible change + return typeParameter.superTypes.joinToString("&", "$index<", ">") { + acyclicTypeMangler(it, typeParameterNamer) + } } + return typeParameters.withIndex().joinToString(";", "{", "}") { (i, tp) -> + mangleTypeParameter(i, tp) + } + } + + open fun IrFunction.signature(typeParameterNamer: (IrTypeParameter) -> String): String { + val extensionReceiverPart = this.extensionReceiverParameter?.extensionReceiverNamePart(typeParameterNamer) ?: "" + val valueParamsPart = this.valueParamsPart(typeParameterNamer) + // Distinguish value types and references - it's needed for calling virtual methods through bridges. + // Also is function has type arguments - frontend allows exactly matching overrides. + val signatureSuffix = + when { + this.typeParameters.isNotEmpty() -> "Generic" + returnType.isInlined -> "ValueType" + !returnType.isUnitOrNullableUnit() -> typeToHashString(returnType, typeParameterNamer) + else -> "" + } + + val typesParamsPart = this.typeParamsPart(typeParameters, typeParameterNamer) + + return "$extensionReceiverPart($valueParamsPart)$typesParamsPart$signatureSuffix" + } + open val IrFunction.platformSpecificFunctionName: String? get() = null // TODO: rename to indicate that it has signature included @@ -175,8 +224,15 @@ abstract class KotlinManglerImpl : KotlinMangler { get() { // TODO: Again. We can't call super in children, so provide a hook for now. this.platformSpecificFunctionName?.let { return it } + + val typeContainerMap = mapTypeParameterContainers(this) + val typeParameterNamer: (IrTypeParameter) -> String = { + val eParent = it.effectiveParent() + "${typeContainerMap[eParent] ?: error("No parent for ${it.render()}")}:${it.index}" + } + val name = this.name.mangleIfInternal(this.module, this.visibility) - return "$name$signature" + return "$name${signature(typeParameterNamer)}" } override val Long.isSpecial: Boolean @@ -257,7 +313,13 @@ abstract class KotlinManglerImpl : KotlinMangler { private val IrProperty.symbolName: String get() { - val extensionReceiver: String = getter?.extensionReceiverParameter?.extensionReceiverNamePart ?: "" + val typeContainerMap = mapTypeParameterContainers(this) + val typeParameterNamer: (IrTypeParameter) -> String = { + val eParent = it.effectiveParent() + "${typeContainerMap[eParent] ?: error("No parent for ${it.render()}")}:${it.index}" + } + + val extensionReceiver: String = getter?.extensionReceiverParameter?.extensionReceiverNamePart(typeParameterNamer) ?: "" val containingDeclarationPart = parent.fqNameForIrSerialization.let { if (it.isRoot) "" else "$it." diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsMangler.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsMangler.kt index d15df74e794..4e344db97bd 100644 --- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsMangler.kt +++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/lower/serialization/ir/JsMangler.kt @@ -6,12 +6,19 @@ package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir import org.jetbrains.kotlin.backend.common.serialization.KotlinManglerImpl -import org.jetbrains.kotlin.backend.common.serialization.cityHash64 +import org.jetbrains.kotlin.ir.declarations.IrTypeParameter import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.util.isInlined -object JsMangler : KotlinManglerImpl() { +abstract class AbstractJsMangler : KotlinManglerImpl() { override val IrType.isInlined: Boolean get() = this.isInlined() } +object JsMangler : AbstractJsMangler() + +object JsManglerForBE : AbstractJsMangler() { + + override fun mangleTypeParameter(typeParameter: IrTypeParameter, typeParameterNamer: (IrTypeParameter) -> String): String = + typeParameter.name.asString() +} \ No newline at end of file diff --git a/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinAbiVersion.kt b/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinAbiVersion.kt index eccf0e835f4..b14412fdc86 100644 --- a/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinAbiVersion.kt +++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinAbiVersion.kt @@ -22,7 +22,7 @@ fun String.parseKonanAbiVersion(): KotlinAbiVersion { data class KotlinAbiVersion(val version: Int) { companion object { - val CURRENT = KotlinAbiVersion(21) + val CURRENT = KotlinAbiVersion(22) } override fun toString() = "$version" diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java index d5b01eb8947..e92eefba6f5 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java @@ -4477,6 +4477,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/inlineMultiModule/topLevelNestedInline.kt"); } + @TestMetadata("typeParametersMangling.kt") + public void testTypeParametersMangling() throws Exception { + runTest("js/js.translator/testData/box/inlineMultiModule/typeParametersMangling.kt"); + } + @TestMetadata("typealiases.kt") public void testTypealiases() throws Exception { runTest("js/js.translator/testData/box/inlineMultiModule/typealiases.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index b5ceb52878f..a7736e12093 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -4492,6 +4492,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/inlineMultiModule/topLevelNestedInline.kt"); } + @TestMetadata("typeParametersMangling.kt") + public void testTypeParametersMangling() throws Exception { + runTest("js/js.translator/testData/box/inlineMultiModule/typeParametersMangling.kt"); + } + @TestMetadata("typealiases.kt") public void testTypealiases() throws Exception { runTest("js/js.translator/testData/box/inlineMultiModule/typealiases.kt"); diff --git a/js/js.translator/testData/box/inlineMultiModule/typeParametersMangling.kt b/js/js.translator/testData/box/inlineMultiModule/typeParametersMangling.kt new file mode 100644 index 00000000000..99725a50f34 --- /dev/null +++ b/js/js.translator/testData/box/inlineMultiModule/typeParametersMangling.kt @@ -0,0 +1,18 @@ +// EXPECTED_REACHABLE_NODES: 1286 +// MODULE: lib +// FILE: lib.kt + +class C(val v: V) { + + fun > reduce(reducer: (reduction: V) -> R2) {} + fun > reduce(reducer: (reduction: R1) -> R2) {} +} + +// MODULE: main(lib) +// FILE: main.kt + +fun box(): String { + val c = C(42) + + return "OK" +} \ No newline at end of file