[Wasm] Fix JsName external declarations

This commit is contained in:
Igor Yakovlev
2022-11-25 19:09:43 +01:00
committed by teamcity
parent 4bd187ccea
commit 4dedd47172
4 changed files with 82 additions and 51 deletions
@@ -68,11 +68,7 @@ class DeclarationGenerator(
} }
val wasmImportModule = declaration.getWasmImportDescriptor() val wasmImportModule = declaration.getWasmImportDescriptor()
val jsCode = if (declaration.isExternal) declaration.getJsFunAnnotation() else null
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 importedName = when { val importedName = when {
wasmImportModule != null -> { wasmImportModule != null -> {
check(declaration.isExternal) { "Non-external fun with @WasmImport ${declaration.fqNameWhenAvailable}"} check(declaration.isExternal) { "Non-external fun with @WasmImport ${declaration.fqNameWhenAvailable}"}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.backend.wasm.lower 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.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext 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 * Redirect usages of complex declarations to top-level functions
*/ */
class ComplexExternalDeclarationsUsageLowering(val context: WasmBackendContext) : BodyLoweringPass { class ComplexExternalDeclarationsUsageLowering(val context: WasmBackendContext) : FileLoweringPass {
override fun lower(irBody: IrBody, container: IrDeclaration) { private val nestedExternalToNewTopLevelFunctions = context.mapping.wasmNestedExternalToNewTopLevelFunction
irBody.transformChildrenVoid(object : IrElementTransformerVoid() { private val objectToGetInstanceFunctions = context.mapping.wasmExternalObjectToGetInstanceFunction
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid()
return transformCall(expression)
}
override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { override fun lower(irFile: IrFile) {
expression.transformChildrenVoid() irFile.acceptVoid(declarationTransformer)
return transformCall(expression) }
}
override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { private val declarationTransformer = object : IrElementVisitorVoid {
val externalGetInstance = context.mapping.wasmExternalObjectToGetInstanceFunction[expression.symbol.owner] override fun visitElement(element: IrElement) {
return if (externalGetInstance != null) { element.acceptChildrenVoid(this)
IrCallImpl( }
expression.startOffset,
expression.endOffset, override fun visitFile(declaration: IrFile) {
expression.type, process(declaration)
externalGetInstance.symbol, }
valueArgumentsCount = 0,
typeArgumentsCount = 0 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 { } else {
expression member.acceptVoid(this)
null
} }
} }
}
fun transformCall(call: IrFunctionAccessExpression): IrExpression { override fun visitBody(body: IrBody) {
val oldFun = call.symbol.owner.realOverrideTarget body.transformChildrenVoid(usagesTransformer)
val newFun: IrSimpleFunction? = }
context.mapping.wasmNestedExternalToNewTopLevelFunction[oldFun] }
return if (newFun != null) { private val usagesTransformer = object : IrElementTransformerVoid() {
val newCall = irCall(call, newFun, receiversAsArguments = true) override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid()
return transformCall(expression)
}
// Add default arguments flags if needed override fun visitConstructorCall(expression: IrConstructorCall): IrExpression {
val numDefaultParameters = numDefaultParametersForExternalFunction(oldFun) expression.transformChildrenVoid()
val firstDefaultFlagArgumentIdx = newFun.valueParameters.size - numDefaultParameters return transformCall(expression)
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)
)
}
newCall override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression {
} else { val externalGetInstance = objectToGetInstanceFunctions[expression.symbol.owner] ?: return expression
call 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
}
} }
} }
@@ -53,6 +53,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
if (declaration.getWasmImportDescriptor() != null) return null if (declaration.getWasmImportDescriptor() != null) return null
check(!(isExported && isExternal)) { "Exported external declarations are not supported: ${declaration.fqNameWhenAvailable}" } check(!(isExported && isExternal)) { "Exported external declarations are not supported: ${declaration.fqNameWhenAvailable}" }
check(declaration.parent !is IrClass) { "Interop members 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() additionalDeclarations.clear()
currentParent = declaration.parent currentParent = declaration.parent
@@ -435,6 +436,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
val result = context.irFactory.buildFun { val result = context.irFactory.buildFun {
name = Name.identifier("__convertKotlinClosureToJsClosure_${info.hashString}") name = Name.identifier("__convertKotlinClosureToJsClosure_${info.hashString}")
returnType = context.wasmSymbols.externalInterfaceType returnType = context.wasmSymbols.externalInterfaceType
isExternal = true
} }
result.parent = currentParent result.parent = currentParent
result.addValueParameter { result.addValueParameter {
@@ -551,6 +553,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
val result = context.irFactory.buildFun { val result = context.irFactory.buildFun {
name = Name.identifier("__callJsClosure_${info.hashString}") name = Name.identifier("__callJsClosure_${info.hashString}")
returnType = info.adaptedResultType returnType = info.adaptedResultType
isExternal = true
} }
result.parent = currentParent result.parent = currentParent
result.addValueParameter { result.addValueParameter {
@@ -55,6 +55,9 @@ const externalObj = {
c: class { x = "(new externalObj.c()).x" } c: class { x = "(new externalObj.c()).x" }
} }
function jsRenamed() {
return 'renamed'
}
// FILE: externals.kt // FILE: externals.kt
external interface Obj { external interface Obj {
@@ -112,6 +115,9 @@ external object externalObj {
} }
} }
@JsName("jsRenamed")
external fun testJsName(): String
fun box(): String { fun box(): String {
val obj = createObject() val obj = createObject()
setX(obj, 100) setX(obj, 100)
@@ -164,6 +170,7 @@ fun box(): String {
if (10 as Any is C1) return "Fail 23" if (10 as Any is C1) return "Fail 23"
if (c1 as Any is C2) return "Fail 24" if (c1 as Any is C2) return "Fail 24"
if (testJsName() != "renamed") return "Fail 25"
return "OK" return "OK"
} }