diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt index 6aa86742db8..3d66064ba2b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt @@ -74,10 +74,6 @@ class ExportModelGenerator( } private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? { - if (function.origin == JsLoweredDeclarationOrigin.ENUM_GET_INSTANCE_FUNCTION) { - return null - } - return when (val exportability = functionExportability(function)) { is Exportability.NotNeeded -> null is Exportability.Prohibited -> ErrorDeclaration(exportability.reason) @@ -533,7 +529,8 @@ class ExportModelGenerator( function.origin == JsLoweredDeclarationOrigin.BRIDGE_WITH_STABLE_NAME || function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER || function.origin == JsLoweredDeclarationOrigin.OBJECT_GET_INSTANCE_FUNCTION || - function.origin == JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT + function.origin == JsLoweredDeclarationOrigin.JS_SHADOWED_EXPORT || + function.origin == JsLoweredDeclarationOrigin.ENUM_GET_INSTANCE_FUNCTION ) { return Exportability.NotNeeded } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt index edad298cc64..333de693ed7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt @@ -13,9 +13,7 @@ import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext -import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin -import org.jetbrains.kotlin.ir.backend.js.utils.hasStableJsName import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.buildFun diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt index e9caad78314..19af4d40eb6 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsBridgesConstruction.kt @@ -7,14 +7,31 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin +import org.jetbrains.kotlin.ir.backend.js.utils.eraseGenerics +import org.jetbrains.kotlin.ir.backend.js.utils.getJsInlinedClass +import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName import org.jetbrains.kotlin.ir.backend.js.utils.hasStableJsName -import org.jetbrains.kotlin.ir.backend.js.utils.jsFunctionSignature import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.isUnit +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.name.Name class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction(context) { - override fun getFunctionSignature(function: IrSimpleFunction): String = - jsFunctionSignature(function, context) + override fun getFunctionSignature(function: IrSimpleFunction): JsSignature = + if (function.hasStableJsName(context)) { + JsStableNameSignature(function.getJsNameOrKotlinName()) + } else { + JsNonStableSignature( + function.name, + function.extensionReceiverParameter?.type?.eraseGenerics(context.irBuiltIns), + function.valueParameters.map { it.type.eraseGenerics(context.irBuiltIns) }, + function.returnType.takeIf { + it.getJsInlinedClass() != null || it.isUnit() + } + ) + } override fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin = if (bridge.hasStableJsName(context)) @@ -22,3 +39,24 @@ class JsBridgesConstruction(context: JsIrBackendContext) : BridgesConstruction, + val returnType: IrType?, +) : JsSignature { + override fun toString(): String { + val er = extensionReceiverType?.let { "(er: ${it.render()}) " } ?: "" + val parameters = valueParametersType.joinToString(", ") { it.render() } + return "[$er$name($parameters) -> ${returnType?.let { " -> ${it.render()}" } ?: ""}]" + } +} + +data class JsStableNameSignature( + override val name: Name, +) : JsSignature \ No newline at end of file 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 c2af3a05fa5..8ff48675e6a 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 @@ -13,11 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol import org.jetbrains.kotlin.ir.types.isUnit -import org.jetbrains.kotlin.ir.util.file -import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable -import org.jetbrains.kotlin.ir.util.isEffectivelyExternal -import org.jetbrains.kotlin.ir.util.isInterface -import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.js.common.isES5IdentifierPart @@ -109,7 +105,7 @@ fun NameTable.dump(): String = private const val RESERVED_MEMBER_NAME_SUFFIX = "_k$" -fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?): String { +fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext): String { require(!declaration.isStaticMethodOfClass) require(declaration.dispatchReceiverParameter != null) @@ -126,16 +122,11 @@ fun jsFunctionSignature(declaration: IrFunction, context: JsIrBackendContext?): val nameBuilder = StringBuilder() nameBuilder.append(declarationName) - // TODO should we skip type parameters and use upper bound of type parameter when print type of value parameters? - declaration.typeParameters.ifNotEmpty { - nameBuilder.append("_\$t") - joinTo(nameBuilder, "") { "_${it.name.asString()}" } - } declaration.extensionReceiverParameter?.let { - nameBuilder.append("_r$${it.type.asString()}") + nameBuilder.append("_r$${it.type.eraseGenerics(context.irBuiltIns).asString()}") } declaration.valueParameters.ifNotEmpty { - joinTo(nameBuilder, "") { "_${it.type.asString()}" } + joinTo(nameBuilder, "") { "_${it.type.eraseGenerics(context.irBuiltIns).asString()}" } } declaration.returnType.let { // Return type is only used in signature for inline class and Unit types because diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/TypeTranformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/TypeTranformer.kt new file mode 100644 index 00000000000..2d783d918bd --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/TypeTranformer.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.utils + +import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrTypeParameter +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.defaultType + +fun IrType.eraseGenerics(irBuiltIns: IrBuiltIns): IrType { + if (this is IrDynamicType) return this + val defaultType = this.erasedUpperBound?.defaultType ?: irBuiltIns.anyType + if (!this.isNullable()) return defaultType + return defaultType.makeNullable() +} + +// Return null if upper bound is Any +private val IrTypeParameter.erasedUpperBound: IrClass? + get() { + // Pick the (necessarily unique) non-interface upper bound if it exists + for (type in superTypes) { + return type.erasedUpperBound ?: continue + } + + return null + } + +val IrType.erasedUpperBound: IrClass? + get() = when (val classifier = classifierOrNull) { + is IrClassSymbol -> classifier.owner + is IrTypeParameterSymbol -> classifier.owner.erasedUpperBound + else -> throw IllegalStateException() + } \ No newline at end of file diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt index 2167e4750b1..f787b5729dc 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/TypeTransformer.kt @@ -6,14 +6,10 @@ package org.jetbrains.kotlin.backend.wasm.ir2wasm import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.backend.js.utils.erasedUpperBound import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrTypeParameter -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.classifierOrNull -import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.ir.util.packageFqName import org.jetbrains.kotlin.name.FqName @@ -126,24 +122,6 @@ fun isBuiltInWasmRefType(type: IrType): Boolean { fun isExternalType(type: IrType): Boolean = type.erasedUpperBound?.isExternal ?: false -// Return null if upper bound is Any -private val IrTypeParameter.erasedUpperBound: IrClass? - get() { - // Pick the (necessarily unique) non-interface upper bound if it exists - for (type in superTypes) { - return type.erasedUpperBound ?: continue - } - - return null - } - -val IrType.erasedUpperBound: IrClass? - get() = when (val classifier = classifierOrNull) { - is IrClassSymbol -> classifier.owner - is IrTypeParameterSymbol -> classifier.owner.erasedUpperBound - else -> throw IllegalStateException() - } - val IrType.getRuntimeClass: IrClass? get() = erasedUpperBound.let { if (it?.isInterface == true) null diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt index 8bdf0c449d2..c8f89931e40 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/GenericReturnTypeLowering.kt @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irComposite import org.jetbrains.kotlin.backend.wasm.WasmBackendContext -import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound +import org.jetbrains.kotlin.ir.backend.js.utils.erasedUpperBound import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget import org.jetbrains.kotlin.ir.builders.irImplicitCast import org.jetbrains.kotlin.ir.declarations.IrFile diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmBridgesConstruction.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmBridgesConstruction.kt index f5a9cb9a0b2..76f26a245dd 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmBridgesConstruction.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmBridgesConstruction.kt @@ -6,16 +6,13 @@ package org.jetbrains.kotlin.backend.wasm.lower import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides -import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext import org.jetbrains.kotlin.ir.backend.js.lower.BridgesConstruction +import org.jetbrains.kotlin.ir.backend.js.utils.eraseGenerics import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.isNullable -import org.jetbrains.kotlin.ir.types.makeNullable -import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.name.Name @@ -44,7 +41,7 @@ data class WasmSignature( override fun toString(): String { val er = extensionReceiverType?.let { "(er: ${it.render()}) " } ?: "" val parameters = valueParametersType.joinToString(", ") { it.render() } - val nonVirtual = if (!isVirtual) "(non-virtual) " else "" + val nonVirtual = if (!isVirtual) "(non-virtual) " else "" return "[$nonVirtual$er$name($parameters) -> ${returnType.render()}]" } } @@ -56,11 +53,4 @@ fun IrSimpleFunction.wasmSignature(irBuiltIns: IrBuiltIns): WasmSignature = valueParameters.map { it.type.eraseGenerics(irBuiltIns) }, returnType.eraseGenerics(irBuiltIns), isOverridableOrOverrides - ) - -private fun IrType.eraseGenerics(irBuiltIns: IrBuiltIns): IrType { - val defaultType = this.erasedUpperBound?.defaultType ?: irBuiltIns.anyType - if (!this.isNullable()) return defaultType - return defaultType.makeNullable() -} - + ) \ No newline at end of file diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt index 77ea6310319..3e9ab2a3c14 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmTypeOperatorLowering.kt @@ -13,10 +13,10 @@ import org.jetbrains.kotlin.backend.common.lower.at import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irNot import org.jetbrains.kotlin.backend.wasm.WasmBackendContext -import org.jetbrains.kotlin.backend.wasm.ir2wasm.erasedUpperBound import org.jetbrains.kotlin.backend.wasm.ir2wasm.getRuntimeClass import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.backend.js.utils.erasedUpperBound import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrFile diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index 4dbc063a873..61adc5fc301 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -2448,6 +2448,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/box/export"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true); } + @Test + @TestMetadata("bridgeSavingAfterExport.kt") + public void testBridgeSavingAfterExport() throws Exception { + runTest("js/js.translator/testData/box/export/bridgeSavingAfterExport.kt"); + } + @Test @TestMetadata("defaultInlineClassConstructorParam.kt") public void testDefaultInlineClassConstructorParam() throws Exception { diff --git a/js/js.translator/testData/box/export/bridgeSavingAfterExport.kt b/js/js.translator/testData/box/export/bridgeSavingAfterExport.kt new file mode 100644 index 00000000000..4fd28b3bf2a --- /dev/null +++ b/js/js.translator/testData/box/export/bridgeSavingAfterExport.kt @@ -0,0 +1,29 @@ +// TARGET_BACKEND: JS_IR +// RUN_PLAIN_BOX_FUNCTION +// INFER_MAIN_MODULE + +// MODULE: bridge_saving_after_export +// FILE: lib.kt + +@JsExport +open class A { + open fun foo(value: T): T = value +} + +@JsExport +class B: A() { + override fun foo(value: String): String = value +} + +// FILE: test.js +function box() { + var a = new this["bridge_saving_after_export"].A() + var aFoo = a.foo("ok") + if (aFoo != "ok") return "fail 1" + + var b = new this["bridge_saving_after_export"].B() + var bFoo = b.foo("ok") + if (bFoo != "ok") return "fail 2" + + return "OK" +} \ No newline at end of file