[KLIB] Fix references to type made from TypeParameter in KotlinMangler

- promote ABI version
This commit is contained in:
Roman Artemev
2019-12-04 17:45:09 +03:00
committed by romanart
parent 3544bf17f4
commit cad3cb1bbe
7 changed files with 139 additions and 42 deletions
@@ -9,7 +9,7 @@ import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
import org.jetbrains.kotlin.backend.common.ir.isTopLevel import org.jetbrains.kotlin.backend.common.ir.isTopLevel
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin 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.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.ir.types.isUnit
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
fun <T> mapToKey(declaration: T): String { fun <T> mapToKey(declaration: T): String {
return with(JsMangler) { return with(JsManglerForBE) {
if (declaration is IrDeclaration && isPublic(declaration)) { if (declaration is IrDeclaration && isPublic(declaration)) {
declaration.hashedMangle.toString() declaration.hashedMangle.toString()
} else if (declaration is Signature) { } else if (declaration is Signature) {
@@ -32,7 +32,7 @@ fun <T> 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 declaration.isExported() && declaration !is IrScript && declaration !is IrVariable && declaration !is IrValueParameter
class NameTable<T>( class NameTable<T>(
@@ -6,7 +6,9 @@
package org.jetbrains.kotlin.backend.common.serialization package org.jetbrains.kotlin.backend.common.serialization
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* 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.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
@@ -98,24 +100,52 @@ abstract class KotlinManglerImpl : KotlinMangler {
return true 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<IrTypeParameter>, type: IrType): String { private fun collectTypeParameterContainers(element: IrElement): List<IrDeclaration> {
val descriptor = (type.classifierOrNull as? IrTypeParameterSymbol)?.owner val result = mutableListOf<IrDeclaration>()
if (descriptor != null) {
val upperBounds = if (visited.contains(descriptor)) "" else {
visited.add(descriptor) tailrec fun collectTypeParameterContainersImpl(element: IrElement) {
when (element) {
descriptor.superTypes.map { is IrConstructor -> result += element
val bound = acyclicTypeMangler(visited, it) is IrSimpleFunction -> result.add(element.correspondingPropertySymbol?.owner ?: element)
if (bound == "kotlin.Any?") "" else "_$bound" is IrProperty -> result += element
}.joinToString("") 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() } ?: "<dynamic>" collectTypeParameterContainersImpl(element)
return result
}
private fun mapTypeParameterContainers(element: IrElement): Map<IrDeclaration, Int> {
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")
}
} ?: "<dynamic>"
when (type) { when (type) {
is IrSimpleType -> { is IrSimpleType -> {
@@ -126,7 +156,7 @@ abstract class KotlinManglerImpl : KotlinMangler {
is IrTypeProjection -> { is IrTypeProjection -> {
val variance = it.variance.label val variance = it.variance.label
val projection = if (variance == "") "" else "${variance}_" val projection = if (variance == "") "" else "${variance}_"
projection + acyclicTypeMangler(visited, it.type) projection + acyclicTypeMangler(it.type, typeParameterNamer)
} }
else -> error(it) else -> error(it)
} }
@@ -142,32 +172,51 @@ abstract class KotlinManglerImpl : KotlinMangler {
return hashString 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 fun IrValueParameter.extensionReceiverNamePart(typeParameterNamer: (IrTypeParameter) -> String): String =
get() = "@${typeToHashString(this.type)}." "@${typeToHashString(this.type, typeParameterNamer)}."
open val IrFunction.argsPart open fun IrFunction.valueParamsPart(typeParameterNamer: (IrTypeParameter) -> String): String {
get() = this.valueParameters.map { return this.valueParameters.map {
"${typeToHashString(it.type)}${if (it.isVararg) "_VarArg" else ""}" "${typeToHashString(it.type, typeParameterNamer)}${if (it.isVararg) "_VarArg" else ""}"
}.joinToString(";") }.joinToString(";")
}
open val IrFunction.signature: String open fun IrFunction.typeParamsPart(typeParameters: List<IrTypeParameter>, typeParameterNamer: (IrTypeParameter) -> String): String {
get() { if (typeParameters.isEmpty()) return ""
val extensionReceiverPart = this.extensionReceiverParameter?.extensionReceiverNamePart ?: ""
val argsPart = this.argsPart fun mangleTypeParameter(index: Int, typeParameter: IrTypeParameter): String {
// Distinguish value types and references - it's needed for calling virtual methods through bridges. // We use type parameter index instead of name since changing name is not a binary-incompatible change
// Also is function has type arguments - frontend allows exactly matching overrides. return typeParameter.superTypes.joinToString("&", "$index<", ">") {
val signatureSuffix = acyclicTypeMangler(it, typeParameterNamer)
when { }
this.typeParameters.isNotEmpty() -> "Generic"
returnType.isInlined -> "ValueType"
!returnType.isUnitOrNullableUnit() -> typeToHashString(returnType)
else -> ""
}
return "$extensionReceiverPart($argsPart)$signatureSuffix"
} }
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 open val IrFunction.platformSpecificFunctionName: String? get() = null
// TODO: rename to indicate that it has signature included // TODO: rename to indicate that it has signature included
@@ -175,8 +224,15 @@ abstract class KotlinManglerImpl : KotlinMangler {
get() { get() {
// TODO: Again. We can't call super in children, so provide a hook for now. // TODO: Again. We can't call super in children, so provide a hook for now.
this.platformSpecificFunctionName?.let { return it } 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) val name = this.name.mangleIfInternal(this.module, this.visibility)
return "$name$signature" return "$name${signature(typeParameterNamer)}"
} }
override val Long.isSpecial: Boolean override val Long.isSpecial: Boolean
@@ -257,7 +313,13 @@ abstract class KotlinManglerImpl : KotlinMangler {
private val IrProperty.symbolName: String private val IrProperty.symbolName: String
get() { 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 { val containingDeclarationPart = parent.fqNameForIrSerialization.let {
if (it.isRoot) "" else "$it." if (it.isRoot) "" else "$it."
@@ -6,12 +6,19 @@
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir 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.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.types.IrType
import org.jetbrains.kotlin.ir.util.isInlined import org.jetbrains.kotlin.ir.util.isInlined
object JsMangler : KotlinManglerImpl() { abstract class AbstractJsMangler : KotlinManglerImpl() {
override val IrType.isInlined: Boolean override val IrType.isInlined: Boolean
get() = this.isInlined() get() = this.isInlined()
} }
object JsMangler : AbstractJsMangler()
object JsManglerForBE : AbstractJsMangler() {
override fun mangleTypeParameter(typeParameter: IrTypeParameter, typeParameterNamer: (IrTypeParameter) -> String): String =
typeParameter.name.asString()
}
@@ -22,7 +22,7 @@ fun String.parseKonanAbiVersion(): KotlinAbiVersion {
data class KotlinAbiVersion(val version: Int) { data class KotlinAbiVersion(val version: Int) {
companion object { companion object {
val CURRENT = KotlinAbiVersion(21) val CURRENT = KotlinAbiVersion(22)
} }
override fun toString() = "$version" override fun toString() = "$version"
@@ -4477,6 +4477,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
runTest("js/js.translator/testData/box/inlineMultiModule/topLevelNestedInline.kt"); 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") @TestMetadata("typealiases.kt")
public void testTypealiases() throws Exception { public void testTypealiases() throws Exception {
runTest("js/js.translator/testData/box/inlineMultiModule/typealiases.kt"); runTest("js/js.translator/testData/box/inlineMultiModule/typealiases.kt");
@@ -4492,6 +4492,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
runTest("js/js.translator/testData/box/inlineMultiModule/topLevelNestedInline.kt"); 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") @TestMetadata("typealiases.kt")
public void testTypealiases() throws Exception { public void testTypealiases() throws Exception {
runTest("js/js.translator/testData/box/inlineMultiModule/typealiases.kt"); runTest("js/js.translator/testData/box/inlineMultiModule/typealiases.kt");
@@ -0,0 +1,18 @@
// EXPECTED_REACHABLE_NODES: 1286
// MODULE: lib
// FILE: lib.kt
class C<V>(val v: V) {
fun <R1, R2: List<R1>> reduce(reducer: (reduction: V) -> R2) {}
fun <R1, R2: List<R1>> reduce(reducer: (reduction: R1) -> R2) {}
}
// MODULE: main(lib)
// FILE: main.kt
fun box(): String {
val c = C(42)
return "OK"
}