[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 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}"}
@@ -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
}
}
}
@@ -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 {
@@ -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"
}