diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt index 21a29fea2e8..09c8e3eea16 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt @@ -68,11 +68,7 @@ class DeclarationGenerator( } val wasmImportModule = declaration.getWasmImportDescriptor() - - val jsCode = declaration.getJsFunAnnotation() - // TODO: Why are we importing declarations by with raw declaration.name.asString() jsCode? - ?: if (declaration.isExternal && wasmImportModule == null) declaration.name.asString() else null - + val jsCode = if (declaration.isExternal) declaration.getJsFunAnnotation() else null val importedName = when { wasmImportModule != null -> { check(declaration.isExternal) { "Non-external fun with @WasmImport ${declaration.fqNameWhenAvailable}"} diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt index 1d2bc31494f..f4099718331 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ComplexExternalDeclarationsToTopLevelFunctionsLowering.kt @@ -5,7 +5,6 @@ package org.jetbrains.kotlin.backend.wasm.lower -import org.jetbrains.kotlin.backend.common.BodyLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.wasm.WasmBackendContext @@ -333,61 +332,87 @@ class ComplexExternalDeclarationsToTopLevelFunctionsLowering(val context: WasmBa /** * Redirect usages of complex declarations to top-level functions */ -class ComplexExternalDeclarationsUsageLowering(val context: WasmBackendContext) : BodyLoweringPass { - override fun lower(irBody: IrBody, container: IrDeclaration) { - irBody.transformChildrenVoid(object : IrElementTransformerVoid() { - override fun visitCall(expression: IrCall): IrExpression { - expression.transformChildrenVoid() - return transformCall(expression) - } +class ComplexExternalDeclarationsUsageLowering(val context: WasmBackendContext) : FileLoweringPass { + private val nestedExternalToNewTopLevelFunctions = context.mapping.wasmNestedExternalToNewTopLevelFunction + private val objectToGetInstanceFunctions = context.mapping.wasmExternalObjectToGetInstanceFunction - override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { - expression.transformChildrenVoid() - return transformCall(expression) - } + override fun lower(irFile: IrFile) { + irFile.acceptVoid(declarationTransformer) + } - override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { - val externalGetInstance = context.mapping.wasmExternalObjectToGetInstanceFunction[expression.symbol.owner] - return if (externalGetInstance != null) { - IrCallImpl( - expression.startOffset, - expression.endOffset, - expression.type, - externalGetInstance.symbol, - valueArgumentsCount = 0, - typeArgumentsCount = 0 - ) + private val declarationTransformer = object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitFile(declaration: IrFile) { + process(declaration) + } + + override fun visitClass(declaration: IrClass) { + if (!declaration.isExternal) { + process(declaration) + } + } + + private fun process(container: IrDeclarationContainer) { + container.declarations.transformFlat { member -> + if (nestedExternalToNewTopLevelFunctions.keys.contains(member)) { + emptyList() } else { - expression + member.acceptVoid(this) + null } } + } - fun transformCall(call: IrFunctionAccessExpression): IrExpression { - val oldFun = call.symbol.owner.realOverrideTarget - val newFun: IrSimpleFunction? = - context.mapping.wasmNestedExternalToNewTopLevelFunction[oldFun] + override fun visitBody(body: IrBody) { + body.transformChildrenVoid(usagesTransformer) + } + } - return if (newFun != null) { - val newCall = irCall(call, newFun, receiversAsArguments = true) + private val usagesTransformer = object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid() + return transformCall(expression) + } - // Add default arguments flags if needed - val numDefaultParameters = numDefaultParametersForExternalFunction(oldFun) - val firstDefaultFlagArgumentIdx = newFun.valueParameters.size - numDefaultParameters - val firstOldDefaultArgumentIdx = call.valueArgumentsCount - numDefaultParameters - repeat(numDefaultParameters) { - val value = if (call.getValueArgument(firstOldDefaultArgumentIdx + it) == null) 1 else 0 - newCall.putValueArgument( - firstDefaultFlagArgumentIdx + it, - IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.intType, value) - ) - } + override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { + expression.transformChildrenVoid() + return transformCall(expression) + } - newCall - } else { - call - } + override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { + val externalGetInstance = objectToGetInstanceFunctions[expression.symbol.owner] ?: return expression + return IrCallImpl( + startOffset = expression.startOffset, + endOffset = expression.endOffset, + type = expression.type, + symbol = externalGetInstance.symbol, + valueArgumentsCount = 0, + typeArgumentsCount = 0 + ) + } + + fun transformCall(call: IrFunctionAccessExpression): IrExpression { + val oldFun = call.symbol.owner.realOverrideTarget + val newFun: IrSimpleFunction = nestedExternalToNewTopLevelFunctions[oldFun] ?: return call + + val newCall = irCall(call, newFun, receiversAsArguments = true) + + // Add default arguments flags if needed + val numDefaultParameters = numDefaultParametersForExternalFunction(oldFun) + val firstDefaultFlagArgumentIdx = newFun.valueParameters.size - numDefaultParameters + val firstOldDefaultArgumentIdx = call.valueArgumentsCount - numDefaultParameters + repeat(numDefaultParameters) { + val value = if (call.getValueArgument(firstOldDefaultArgumentIdx + it) == null) 1 else 0 + newCall.putValueArgument( + firstDefaultFlagArgumentIdx + it, + IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.intType, value) + ) } - }) + return newCall + } } } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt index ef9b8e408fb..a46f61175ce 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt @@ -53,6 +53,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT if (declaration.getWasmImportDescriptor() != null) return null check(!(isExported && isExternal)) { "Exported external declarations are not supported: ${declaration.fqNameWhenAvailable}" } check(declaration.parent !is IrClass) { "Interop members are not supported: ${declaration.fqNameWhenAvailable}" } + if (context.mapping.wasmNestedExternalToNewTopLevelFunction.keys.contains(declaration)) return null additionalDeclarations.clear() currentParent = declaration.parent @@ -435,6 +436,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT val result = context.irFactory.buildFun { name = Name.identifier("__convertKotlinClosureToJsClosure_${info.hashString}") returnType = context.wasmSymbols.externalInterfaceType + isExternal = true } result.parent = currentParent result.addValueParameter { @@ -551,6 +553,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT val result = context.irFactory.buildFun { name = Name.identifier("__callJsClosure_${info.hashString}") returnType = info.adaptedResultType + isExternal = true } result.parent = currentParent result.addValueParameter { diff --git a/compiler/testData/codegen/boxWasmJsInterop/externals.kt b/compiler/testData/codegen/boxWasmJsInterop/externals.kt index 36d6f30355f..f93c6b8d197 100644 --- a/compiler/testData/codegen/boxWasmJsInterop/externals.kt +++ b/compiler/testData/codegen/boxWasmJsInterop/externals.kt @@ -55,6 +55,9 @@ const externalObj = { c: class { x = "(new externalObj.c()).x" } } +function jsRenamed() { + return 'renamed' +} // FILE: externals.kt external interface Obj { @@ -112,6 +115,9 @@ external object externalObj { } } +@JsName("jsRenamed") +external fun testJsName(): String + fun box(): String { val obj = createObject() setX(obj, 100) @@ -164,6 +170,7 @@ fun box(): String { if (10 as Any is C1) return "Fail 23" if (c1 as Any is C2) return "Fail 24" + if (testJsName() != "renamed") return "Fail 25" return "OK" }