diff --git a/.idea/artifacts/kotlin_test_wasm_js_1_6_255_SNAPSHOT.xml b/.idea/artifacts/kotlin_test_wasm_js_1_6_255_SNAPSHOT.xml
index 6ec66790f30..bf25edaae1f 100644
--- a/.idea/artifacts/kotlin_test_wasm_js_1_6_255_SNAPSHOT.xml
+++ b/.idea/artifacts/kotlin_test_wasm_js_1_6_255_SNAPSHOT.xml
@@ -1,6 +1,8 @@
$PROJECT_DIR$/libraries/kotlin.test/wasm/build/libs
-
+
+
+
\ No newline at end of file
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt
index 5595d89f7a9..e0090ea7e9e 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt
@@ -35,7 +35,6 @@ class WasmBackendContext(
override val irBuiltIns: IrBuiltIns,
symbolTable: SymbolTable,
irModuleFragment: IrModuleFragment,
- val additionalExportedDeclarations: Set,
override val configuration: CompilerConfiguration,
) : JsCommonBackendContext {
override val builtIns = module.builtIns
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
index b79cbe3090f..321532cf7c7 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt
@@ -66,7 +66,7 @@ private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
description = "Remove expect declaration from module fragment"
)
-private val stringConstructorLowering = makeWasmModulePhase(
+private val stringConcatenationLowering = makeWasmModulePhase(
::StringConcatenationLowering,
name = "StringConcatenation",
description = "String concatenation lowering"
@@ -122,6 +122,18 @@ private val tailrecLoweringPhase = makeWasmModulePhase(
description = "Replace `tailrec` call sites with equivalent loop"
)
+private val jsInteropFunctionsLowering = makeWasmModulePhase(
+ ::JsInteropFunctionsLowering,
+ name = "JsInteropFunctionsLowering",
+ description = "Create delegates for JS interop",
+)
+
+private val jsInteropFunctionCallsLowering = makeWasmModulePhase(
+ ::JsInteropFunctionCallsLowering,
+ name = "JsInteropFunctionCallsLowering",
+ description = "Replace calls to delegates",
+)
+
private val enumClassConstructorLoweringPhase = makeWasmModulePhase(
::EnumClassConstructorLowering,
name = "EnumClassConstructorLowering",
@@ -134,7 +146,6 @@ private val enumClassConstructorBodyLoweringPhase = makeWasmModulePhase(
description = "Transform Enum Class into regular Class"
)
-
private val enumEntryInstancesLoweringPhase = makeWasmModulePhase(
::EnumEntryInstancesLowering,
name = "EnumEntryInstancesLowering",
@@ -431,6 +442,12 @@ private val objectUsageLoweringPhase = makeWasmModulePhase(
description = "Transform IrGetObjectValue into instance generator call"
)
+private val explicitlyCastExternalTypesPhase = makeWasmModulePhase(
+ ::ExplicitlyCastExternalTypesLowering,
+ name = "ExplicitlyCastExternalTypesLowering",
+ description = "Add explicit casts when converting between external and non-external types"
+)
+
private val typeOperatorLoweringPhase = makeWasmModulePhase(
::WasmTypeOperatorLowering,
name = "TypeOperatorLowering",
@@ -515,6 +532,9 @@ val wasmPhases = NamedCompilerPhase(
delegateToPrimaryConstructorLoweringPhase then
// Common prefix ends
+ jsInteropFunctionsLowering then
+ jsInteropFunctionCallsLowering then
+
enumEntryInstancesLoweringPhase then
enumEntryInstancesBodyLoweringPhase then
enumClassCreateInitializerLoweringPhase then
@@ -536,7 +556,7 @@ val wasmPhases = NamedCompilerPhase(
forLoopsLoweringPhase then
propertyAccessorInlinerLoweringPhase then
- stringConstructorLowering then
+ stringConcatenationLowering then
defaultArgumentStubGeneratorPhase then
defaultArgumentPatchOverridesPhase then
@@ -564,6 +584,7 @@ val wasmPhases = NamedCompilerPhase(
builtInsLoweringPhase0 then
autoboxingTransformerPhase then
+ explicitlyCastExternalTypesPhase then
objectUsageLoweringPhase then
typeOperatorLoweringPhase then
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
index bb99b2d2b98..7c14b65c829 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.SymbolTable
+import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -157,9 +158,7 @@ class WasmSymbols(
val nullableFloatIeee754Equals = getInternalFunction("nullableFloatIeee754Equals")
val nullableDoubleIeee754Equals = getInternalFunction("nullableDoubleIeee754Equals")
- val exportString = getInternalFunction("exportString")
val unsafeGetScratchRawMemory = getInternalFunction("unsafeGetScratchRawMemory")
-
val startCoroutineUninterceptedOrReturnIntrinsics =
(0..2).map { getInternalFunction("startCoroutineUninterceptedOrReturnIntrinsic$it") }
@@ -206,6 +205,29 @@ class WasmSymbols(
}
}
+ private val wasmDataRefClass = getIrClass(FqName("kotlin.wasm.internal.reftypes.dataref"))
+ val wasmDataRefType by lazy { wasmDataRefClass.defaultType }
+
+
+ inner class JsInteropAdapters {
+ val kotlinToJsStringAdapter = getInternalFunction("kotlinToJsStringAdapter")
+ val kotlinToJsBooleanAdapter = getInternalFunction("kotlinToJsBooleanAdapter")
+ val kotlinToJsAnyAdapter = getInternalFunction("kotlinToJsAnyAdapter")
+ val jsToKotlinAnyAdapter = getInternalFunction("jsToKotlinAnyAdapter")
+ val jsToKotlinStringAdapter = getInternalFunction("jsToKotlinStringAdapter")
+ val jsToKotlinByteAdapter = getInternalFunction("jsToKotlinByteAdapter")
+ val jsToKotlinShortAdapter = getInternalFunction("jsToKotlinShortAdapter")
+ val jsToKotlinCharAdapter = getInternalFunction("jsToKotlinCharAdapter")
+ }
+
+ val jsInteropAdapters = JsInteropAdapters()
+
+ private val jsExportClass = getIrClass(FqName("kotlin.js.JsExport"))
+ val jsExportConstructor by lazy { jsExportClass.constructors.single() }
+
+ private val jsNameClass = getIrClass(FqName("kotlin.js.JsName"))
+ val jsNameConstructor by lazy { jsNameClass.constructors.single() }
+
private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt
index 50500b5e7cb..4afaa8f3a6b 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/compiler.kt
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCompiledModuleFragment
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleFragmentGenerator
+import org.jetbrains.kotlin.backend.wasm.lower.markExportedDeclarations
import org.jetbrains.kotlin.ir.backend.js.MainModule
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
import org.jetbrains.kotlin.ir.backend.js.loadIr
@@ -45,7 +46,7 @@ fun compileWasm(
}
val moduleDescriptor = moduleFragment.descriptor
- val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, exportedDeclarations, configuration)
+ val context = WasmBackendContext(moduleDescriptor, irBuiltIns, symbolTable, moduleFragment, configuration)
// Load declarations referenced during `context` initialization
allModules.forEach {
@@ -63,6 +64,8 @@ fun compileWasm(
deserializer.postProcess()
symbolTable.noUnboundLeft("Unbound symbols at the end of linker")
+ moduleFragment.files.forEach { irFile -> markExportedDeclarations(context, irFile, exportedDeclarations) }
+
wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment)
val compiledWasmModule = WasmCompiledModuleFragment(context.irBuiltIns)
@@ -89,9 +92,12 @@ fun compileWasm(
fun WasmCompiledModuleFragment.generateJs(): String {
+ //language=js
val runtime = """
var wasmInstance = null;
+ const externrefBoxes = new WeakMap();
+
const runtime = {
identity(x) {
return x;
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
index 21eb87599f7..60d12456cfe 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt
@@ -185,18 +185,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
else
context.referenceLocal(valueSymbol)
)
-
- if (valueSymbol.owner is IrValueParameter) {
- val parent = valueDeclaration.parent
- if (parent is IrFunction && parent.isExported(backendContext)) {
- val type = context.transformType(valueDeclaration.type)
- if (type is WasmRefNullType) {
- // TODO: Add these casts as IR-2-IR lowering instead
- generateTypeRTT(valueDeclaration.type)
- body.buildRefCast()
- }
- }
- }
}
override fun visitSetValue(expression: IrSetValue) {
@@ -324,16 +312,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildCall(context.referenceFunction(function.symbol))
}
- // Return types of imported functions cannot have concrete struct/array references.
- // Non-primitive return types are represented as eqref which need to be casted back to expected type on call site.
- if (function.getWasmImportAnnotation() != null || function.getJsFunAnnotation() != null || function.isExternal || function.isExported(backendContext)) {
- val resT = context.transformResultType(function.returnType)
- if (resT is WasmRefNullType) {
- generateTypeRTT(function.returnType)
- body.buildRefCast()
- }
- }
-
// Unit types don't cross function boundaries
if (function.returnType == irBuiltIns.unitType)
body.buildGetUnit()
@@ -460,14 +438,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
body.buildGetLocal(context.referenceLocal(0))
}
- // Handle complex exported parameters.
- // TODO: This should live as a separate lowering which creates separate shims for each exported function.
- if (context.irFunction.isExported(context.backendContext) &&
- expression.value.type.getClass() == irBuiltIns.stringType.getClass()) {
-
- body.buildCall(context.referenceFunction(wasmSymbols.exportString))
- }
-
body.buildInstr(WasmOp.RETURN)
}
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 c6322b6e286..10fb96205d0 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
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
import org.jetbrains.kotlin.ir.backend.js.utils.findUnitGetInstanceFunction
+import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
@@ -69,33 +70,18 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
// Generate function type
val watName = declaration.fqNameWhenAvailable.toString()
val irParameters = declaration.getEffectiveValueParameters()
- // TODO: Exported types should be transformed in a separate lowering by creating shim functions for each export.
- val isExported = declaration.isExported(context.backendContext)
val resultType =
when {
- isExported -> context.transformExportedResultType(declaration.returnType)
// Unit_getInstance returns true Unit reference instead of "void"
declaration == unitGetInstanceFunction -> context.transformType(declaration.returnType)
else -> context.transformResultType(declaration.returnType)
}
- val isExportedOrImported = isExported || importedName != null
val wasmFunctionType =
WasmFunctionType(
name = watName,
- parameterTypes = irParameters.map {
- val t = context.transformValueParameterType(it)
- if (isExportedOrImported && t is WasmRefNullType) {
- WasmRefNullType(WasmHeapType.Simple.Data)
- } else {
- t
- }
- },
- resultTypes = listOfNotNull(
- resultType.let {
- if (isExportedOrImported && it is WasmRefNullType) WasmRefNullType(WasmHeapType.Simple.Data) else it
- }
- )
+ parameterTypes = irParameters.map { context.transformValueParameterType(it) },
+ resultTypes = listOfNotNull(resultType)
)
context.defineFunctionType(declaration.symbol, wasmFunctionType)
@@ -164,12 +150,11 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
if (initPriority != null)
context.registerInitFunction(function, initPriority)
- if (declaration.isExported(backendContext)) {
+ if (declaration.isExported()) {
context.addExport(
WasmExport.Function(
field = function,
- // TODO: Add ability to specify exported name.
- name = declaration.name.identifier
+ name = declaration.getJsNameOrKotlinName().identifier
)
)
}
@@ -371,7 +356,7 @@ fun generateDefaultInitializerForType(type: WasmType, g: WasmExpressionBuilder)
WasmF32 -> g.buildConstF32(0f)
WasmF64 -> g.buildConstF64(0.0)
is WasmRefNullType -> g.buildRefNull(type.heapType)
- is WasmExternRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
+ is WasmExternRef, is WasmAnyRef -> g.buildRefNull(WasmHeapType.Simple.Extern)
WasmUnreachableType -> error("Unreachable type can't be initialized")
else -> error("Unknown value type ${type.name}")
}
@@ -381,5 +366,5 @@ fun IrFunction.getEffectiveValueParameters(): List {
return listOfNotNull(implicitThis, dispatchReceiverParameter, extensionReceiverParameter) + valueParameters
}
-fun IrFunction.isExported(context: WasmBackendContext): Boolean =
- fqNameWhenAvailable in context.additionalExportedDeclarations || isJsExport()
\ No newline at end of file
+fun IrFunction.isExported(): Boolean =
+ isJsExport()
\ 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 4e398a52fce..e09ea0b8800 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
@@ -12,9 +12,12 @@ 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
import org.jetbrains.kotlin.wasm.ir.*
class WasmTypeTransformer(
@@ -93,13 +96,21 @@ class WasmTypeTransformer(
symbols.voidType ->
error("Void type can't be used as a value")
- // this also handles builtIns.stringType
else -> {
val klass = this.getClass()
val ic = context.backendContext.inlineClassesUtils.getInlinedClass(this)
if (klass != null && (klass.hasWasmForeignAnnotation() || klass.isExternal)) {
- WasmExternRef
+ WasmAnyRef
+ } else if (klass != null && isBuiltInWasmRefType(this)) {
+ when (val name = klass.name.identifier) {
+ "anyref" -> WasmAnyRef
+ "eqref" -> WasmEqRef
+ "dataref" -> WasmRefNullType(WasmHeapType.Simple.Data)
+ "i31ref" -> WasmI31Ref
+ "funcref" -> WasmRefNullType(WasmHeapType.Simple.Func)
+ else -> error("Unknown reference type $name")
+ }
} else if (ic != null) {
context.backendContext.inlineClassesUtils.getInlineClassUnderlyingType(ic).toWasmValueType()
} else {
@@ -109,6 +120,12 @@ class WasmTypeTransformer(
}
}
+fun isBuiltInWasmRefType(type: IrType): Boolean {
+ return type.classOrNull?.owner?.packageFqName == FqName("kotlin.wasm.internal.reftypes")
+}
+
+fun isExternalType(type: IrType): Boolean =
+ type.erasedUpperBound?.isExternal ?: false
// Return null if upper bound is Any
private val IrTypeParameter.erasedUpperBound: IrClass?
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt
index c8ee0e470bb..7c1678b736c 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmBaseCodegenContext.kt
@@ -43,7 +43,6 @@ interface WasmBaseCodegenContext {
fun transformBoxedType(irType: IrType): WasmType
fun transformValueParameterType(irValueParameter: IrValueParameter): WasmType
fun transformResultType(irType: IrType): WasmType?
- fun transformExportedResultType(irType: IrType): WasmType?
fun transformBlockResultType(irType: IrType): WasmType?
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt
index faf591dd026..98a125089ac 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContextImpl.kt
@@ -58,13 +58,6 @@ class WasmModuleCodegenContextImpl(
return with(typeTransformer) { irType.toWasmResultType() }
}
- override fun transformExportedResultType(irType: IrType): WasmType? {
- // Exported strings are passed as pointers to the raw memory
- if (irType.getClass() == backendContext.irBuiltIns.stringType.getClass())
- return WasmI32
- return with(typeTransformer) { irType.toWasmResultType() }
- }
-
override fun transformBlockResultType(irType: IrType): WasmType? {
return with(typeTransformer) { irType.toWasmBlockResultType() }
}
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ExplicitlyCastExternalTypesLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ExplicitlyCastExternalTypesLowering.kt
new file mode 100644
index 00000000000..190a86c24e6
--- /dev/null
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/ExplicitlyCastExternalTypesLowering.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.backend.wasm.lower
+
+import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
+import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExternalType
+import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
+import org.jetbrains.kotlin.ir.backend.js.lower.AbstractValueUsageLowering
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.types.IrType
+
+/**
+ * Insert casts between external and non-external types
+ */
+class ExplicitlyCastExternalTypesLowering(wasmContext: WasmBackendContext) : AbstractValueUsageLowering(wasmContext) {
+ override fun IrExpression.useExpressionAsType(actualType: IrType, expectedType: IrType): IrExpression {
+ val expectedExternal = isExternalType(expectedType)
+ val actualExternal = isExternalType(actualType)
+
+ if (expectedExternal != actualExternal) {
+ return JsIrBuilder.buildImplicitCast(this, toType = expectedType)
+ }
+
+ return this
+ }
+}
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
new file mode 100644
index 00000000000..2c75b43f21a
--- /dev/null
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/JsInteropFunctionsLowering.kt
@@ -0,0 +1,308 @@
+/*
+ * 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.backend.wasm.lower
+
+import org.jetbrains.kotlin.backend.common.BodyLoweringPass
+import org.jetbrains.kotlin.backend.common.DeclarationTransformer
+import org.jetbrains.kotlin.backend.common.ir.createStaticFunctionWithReceivers
+import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
+import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
+import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
+import org.jetbrains.kotlin.backend.wasm.ir2wasm.isBuiltInWasmRefType
+import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExported
+import org.jetbrains.kotlin.backend.wasm.ir2wasm.isExternalType
+import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
+import org.jetbrains.kotlin.ir.builders.*
+import org.jetbrains.kotlin.ir.declarations.IrClass
+import org.jetbrains.kotlin.ir.declarations.IrDeclaration
+import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.expressions.IrBody
+import org.jetbrains.kotlin.ir.expressions.IrCall
+import org.jetbrains.kotlin.ir.expressions.IrExpression
+import org.jetbrains.kotlin.ir.types.IrType
+import org.jetbrains.kotlin.ir.types.makeNullable
+import org.jetbrains.kotlin.ir.util.*
+import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
+import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
+import org.jetbrains.kotlin.name.Name
+
+/**
+ * Create wrappers for external and @JsExport functions when type adaptation is needed
+ */
+class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationTransformer {
+ val builtIns = context.irBuiltIns
+ val symbols = context.wasmSymbols
+ val adapters = symbols.jsInteropAdapters
+
+ override fun transformFlat(declaration: IrDeclaration): List? {
+ if (declaration.isFakeOverride) return null
+ if (declaration !is IrSimpleFunction) return null
+ val isExported = declaration.isExported()
+ val isExternal = declaration.isExternal
+ if (!isExported && !isExternal) 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}" }
+
+ return if (isExternal)
+ transformExternalFunction(declaration)
+ else
+ transformExportFunction(declaration)
+ }
+
+ /**
+ * external fun foo(x: KotlinType): KotlinType
+ *
+ * ->
+ *
+ * external fun foo(x: JsType): JsType
+ * fun foo__externalAdapter(x: KotlinType): KotlinType = adaptResult(foo(adaptParameter(x)));
+ */
+ fun transformExternalFunction(function: IrSimpleFunction): List? {
+ val valueParametersAdapters = function.valueParameters.map {
+ it.type.kotlinToJsAdapterIfNeeded(isReturn = false)
+ }
+ val resultAdapter =
+ function.returnType.jsToKotlinAdapterIfNeeded(isReturn = true)
+
+ if (resultAdapter == null && valueParametersAdapters.all { it == null })
+ return null
+
+ val newFun = context.irFactory.createStaticFunctionWithReceivers(
+ function.parent,
+ name = Name.identifier(function.name.asStringStripSpecialMarkers() + "__externalAdapter"),
+ function
+ )
+
+ function.valueParameters.forEachIndexed { index, newParameter ->
+ val adapter = valueParametersAdapters[index]
+ if (adapter != null) {
+ newParameter.type = adapter.toType
+ }
+ }
+ resultAdapter?.let {
+ function.returnType = resultAdapter.fromType
+ }
+
+ val builder = context.createIrBuilder(newFun.symbol)
+ newFun.body = createAdapterFunctionBody(builder, newFun, function, valueParametersAdapters, resultAdapter)
+ newFun.annotations = emptyList()
+
+ context.mapping.wasmJsInteropFunctionToWrapper[function] = newFun
+ return listOf(function, newFun)
+ }
+
+ /**
+ * @JsExport
+ * fun foo(x: KotlinType): KotlinType { }
+ *
+ * ->
+ *
+ * @JsExport
+ * @JsName("foo")
+ * fun foo__JsExportAdapter(x: JsType): JsType =
+ * adaptResult(foo(adaptParameter(x)));
+ *
+ * fun foo(x: KotlinType): KotlinType { }
+ */
+ fun transformExportFunction(function: IrSimpleFunction): List? {
+ val valueParametersAdapters = function.valueParameters.map {
+ it.type.jsToKotlinAdapterIfNeeded(isReturn = false)
+ }
+ val resultAdapter =
+ function.returnType.kotlinToJsAdapterIfNeeded(isReturn = true)
+
+ if (resultAdapter == null && valueParametersAdapters.all { it == null })
+ return null
+
+ val newFun = context.irFactory.createStaticFunctionWithReceivers(
+ function.parent,
+ name = Name.identifier(function.name.asStringStripSpecialMarkers() + "__JsExportAdapter"),
+ function
+ )
+
+ newFun.valueParameters.forEachIndexed { index, newParameter ->
+ val adapter = valueParametersAdapters[index]
+ if (adapter != null) {
+ newParameter.type = adapter.fromType
+ }
+ }
+ resultAdapter?.let {
+ newFun.returnType = resultAdapter.toType
+ }
+
+ // Delegate new function to old function:
+ val builder: DeclarationIrBuilder = context.createIrBuilder(newFun.symbol)
+ newFun.body = createAdapterFunctionBody(builder, newFun, function, valueParametersAdapters, resultAdapter)
+
+ newFun.annotations += builder.irCallConstructor(context.wasmSymbols.jsNameConstructor, typeArguments = emptyList()).also {
+ it.putValueArgument(0, builder.irString(function.getJsNameOrKotlinName().identifier))
+ }
+ function.annotations = function.annotations.filter { it.symbol != context.wasmSymbols.jsExportConstructor }
+
+ return listOf(function, newFun)
+ }
+
+ private fun createAdapterFunctionBody(
+ builder: DeclarationIrBuilder,
+ function: IrSimpleFunction,
+ functionToCall: IrSimpleFunction,
+ valueParametersAdapters: List,
+ resultAdapter: InteropTypeAdapter?
+ ) = builder.irBlockBody {
+ +irReturn(
+ irCall(functionToCall).let { call ->
+ for ((index, valueParameter) in function.valueParameters.withIndex()) {
+ val get = irGet(valueParameter)
+ call.putValueArgument(index, valueParametersAdapters[index]?.adapt(get, builder) ?: get)
+ }
+ resultAdapter?.adapt(call, builder) ?: call
+ }
+ )
+ }
+
+ private fun IrType.kotlinToJsAdapterIfNeeded(isReturn: Boolean): InteropTypeAdapter? {
+ if (isReturn && this == builtIns.unitType)
+ return null
+
+ when (this) {
+ builtIns.stringType -> return FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner)
+ builtIns.stringType.makeNullable() -> return NullOrAdapter(FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner))
+ builtIns.booleanType -> return FunctionBasedAdapter(adapters.kotlinToJsBooleanAdapter.owner)
+ builtIns.anyType -> return FunctionBasedAdapter(adapters.kotlinToJsAnyAdapter.owner)
+
+ builtIns.byteType,
+ builtIns.shortType,
+ builtIns.charType,
+ builtIns.intType,
+ builtIns.longType,
+ builtIns.floatType,
+ builtIns.doubleType,
+ context.wasmSymbols.voidType ->
+ return null
+
+ }
+
+ if (isExternalType(this))
+ return null
+
+ if (isBuiltInWasmRefType(this))
+ return null
+
+ return SendKotlinObjectToJsAdapter(this)
+ }
+
+ private fun IrType.jsToKotlinAdapterIfNeeded(isReturn: Boolean): InteropTypeAdapter? {
+ if (isReturn && this == builtIns.unitType)
+ return null
+
+ when (this) {
+ builtIns.stringType -> return FunctionBasedAdapter(adapters.jsToKotlinStringAdapter.owner)
+ builtIns.anyType -> return FunctionBasedAdapter(adapters.jsToKotlinAnyAdapter.owner)
+ builtIns.byteType -> return FunctionBasedAdapter(adapters.jsToKotlinByteAdapter.owner)
+ builtIns.shortType -> return FunctionBasedAdapter(adapters.jsToKotlinShortAdapter.owner)
+ builtIns.charType -> return FunctionBasedAdapter(adapters.jsToKotlinCharAdapter.owner)
+
+ builtIns.booleanType,
+ builtIns.intType,
+ builtIns.longType,
+ builtIns.floatType,
+ builtIns.doubleType,
+ context.wasmSymbols.voidType ->
+ return null
+ }
+
+ if (isExternalType(this))
+ return null
+
+ if (isBuiltInWasmRefType(this))
+ return null
+
+ return ReceivingKotlinObjectFromJsAdapter(this)
+ }
+
+ interface InteropTypeAdapter {
+ val fromType: IrType
+ val toType: IrType
+ fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression
+ }
+
+ /**
+ * Adapter implemented as a single function call
+ */
+ class FunctionBasedAdapter(
+ private val function: IrSimpleFunction,
+ ) : InteropTypeAdapter {
+ override val fromType = function.valueParameters[0].type
+ override val toType = function.returnType
+ override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
+ val call = builder.irCall(function)
+ call.putValueArgument(0, expression)
+ return call
+ }
+ }
+
+ /**
+ * Current V8 Wasm GC mandates dataref type instead of structs and arrays
+ */
+ inner class SendKotlinObjectToJsAdapter(
+ override val fromType: IrType
+ ) : InteropTypeAdapter {
+ override val toType: IrType = context.wasmSymbols.wasmDataRefType
+ override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
+ return builder.irReinterpretCast(expression, toType)
+ }
+ }
+
+ /**
+ * Current V8 Wasm GC mandates dataref type instead of structs and arrays
+ */
+ inner class ReceivingKotlinObjectFromJsAdapter(
+ override val toType: IrType
+ ) : InteropTypeAdapter {
+ override val fromType: IrType = context.wasmSymbols.wasmDataRefType
+ override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
+ val call = builder.irCall(context.wasmSymbols.wasmRefCast)
+ call.putValueArgument(0, expression)
+ call.putTypeArgument(0, toType)
+ return call
+ }
+ }
+
+ /**
+ * Effectively `value?.let { adapter(it) }`
+ */
+ inner class NullOrAdapter(
+ val adapter: InteropTypeAdapter
+ ) : InteropTypeAdapter {
+ override val fromType: IrType = adapter.fromType.makeNullable()
+ override val toType: IrType = adapter.toType.makeNullable()
+ override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
+ return builder.irComposite {
+ val tmp = irTemporary(adapter.adapt(expression, builder))
+ +irIfNull(toType, irGet(tmp), irNull(toType), irImplicitCast(irGet(tmp), toType))
+ }
+ }
+ }
+}
+
+/**
+ * Redirect calls to external and @JsExport functions to created wrappers
+ */
+class JsInteropFunctionCallsLowering(val context: WasmBackendContext) : BodyLoweringPass {
+ override fun lower(irBody: IrBody, container: IrDeclaration) {
+ irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
+ override fun visitCall(expression: IrCall): IrExpression {
+ expression.transformChildrenVoid()
+ val newFun: IrSimpleFunction? = context.mapping.wasmJsInteropFunctionToWrapper[expression.symbol.owner]
+ return if (newFun != null && container != newFun) {
+ irCall(expression, newFun)
+ } else {
+ expression
+ }
+ }
+ })
+ }
+}
\ 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 a7b56793175..456ac17fec1 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
@@ -152,6 +152,8 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
private fun generateTypeCheckNonNull(argument: IrExpression, toType: IrType): IrExpression {
assert(!toType.isMarkedNullable())
return when {
+ // TODO: Use instanceof for classes later
+ toType.eraseToClassOrInterface.isExternal -> builder.irTrue()
toType.isNothing() -> builder.irFalse()
toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType)
toType.isInterface() -> generateIsInterface(argument, toType)
@@ -190,7 +192,28 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
}
}
- if (fromType.eraseToClassOrInterface.isSubclassOf(toType.eraseToClassOrInterface)) {
+ val fromClass = fromType.eraseToClassOrInterface
+ val toClass = toType.eraseToClassOrInterface
+
+ if (fromClass.isExternal && toClass.isExternal) {
+ return value
+ }
+
+ if (fromClass.isExternal && !toClass.isExternal) {
+ val narrowingToAny = builder.irCall(symbols.jsInteropAdapters.jsToKotlinAnyAdapter).also {
+ it.putValueArgument(0, value)
+ }
+ // Continue narrowing from Any to expected type
+ return narrowType(context.irBuiltIns.anyType, toType, narrowingToAny)
+ }
+
+ if (toClass.isExternal && !fromClass.isExternal) {
+ return builder.irCall(symbols.jsInteropAdapters.kotlinToJsAnyAdapter).also {
+ it.putValueArgument(0, value)
+ }
+ }
+
+ if (fromClass.isSubclassOf(toClass)) {
return value
}
if (toType.isNothing()) {
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/markAdditionalExportedDeclarations.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/markAdditionalExportedDeclarations.kt
new file mode 100644
index 00000000000..689123c95a3
--- /dev/null
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/markAdditionalExportedDeclarations.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.backend.wasm.lower
+
+import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
+import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
+import org.jetbrains.kotlin.ir.builders.irCallConstructor
+import org.jetbrains.kotlin.ir.declarations.IrFile
+import org.jetbrains.kotlin.ir.declarations.IrFunction
+import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
+import org.jetbrains.kotlin.name.FqName
+
+/**
+ * Mark declarations from [exportedFqNames] with @JsExport annotation
+ */
+fun markExportedDeclarations(context: WasmBackendContext, irFile: IrFile, exportedFqNames: Set) {
+ for (declaration in irFile.declarations) {
+ if (declaration is IrFunction && declaration.fqNameWhenAvailable in exportedFqNames) {
+ val builder = context.createIrBuilder(irFile.symbol)
+ declaration.annotations +=
+ builder.irCallConstructor(context.wasmSymbols.jsExportConstructor, typeArguments = emptyList())
+ }
+ }
+}
\ No newline at end of file
diff --git a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt
index a93360987b1..48db281457e 100644
--- a/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt
+++ b/compiler/ir/serialization.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt
@@ -40,6 +40,10 @@ class JsMapping(val state: JsMappingState) : DefaultMapping(state) {
state.newDeclarationToDeclarationMapping()
val suspendArityStore = state.newDeclarationToDeclarationCollectionMapping>()
+
+ // Wasm mappings
+ val wasmJsInteropFunctionToWrapper =
+ state.newDeclarationToDeclarationMapping()
}
diff --git a/compiler/testData/codegen/boxWasmJsInterop/externalTypeOperators.kt b/compiler/testData/codegen/boxWasmJsInterop/externalTypeOperators.kt
new file mode 100644
index 00000000000..ac85fe9f14b
--- /dev/null
+++ b/compiler/testData/codegen/boxWasmJsInterop/externalTypeOperators.kt
@@ -0,0 +1,231 @@
+// WITH_RUNTIME
+// FILE: externals.js
+
+const primitives1 = [3.14, "Test string 1", true, Symbol("symbol"), 131283889534859707199254740992n];
+const primitives2 = [3.15, "Test string 2", false, Symbol("symbol"), 231283889534859707199254740992n];
+
+function getPrimitive1(n) {
+ return primitives1[n];
+}
+
+function getPrimitive2(n) {
+ return primitives2[n];
+}
+
+function testPrimitive(obj, n) {
+ return obj === primitives1[n];
+}
+
+function getNewObject() {
+ return { value: 123 };
+}
+
+function testObject(obj) {
+ return obj.value === 123;
+}
+
+function getNewArray() {
+ return [1, 2, 3];
+}
+
+function testArray(obj) {
+ return Array.isArray(obj) && obj[0] === 1 && obj[1] === 2 && obj [2] === 3;
+}
+
+function roundTrip(x) { return x; }
+
+// FILE: externals.kt
+external interface Obj
+external interface SubObj : Obj
+
+external fun getPrimitive1(n: Int): Obj
+external fun getPrimitive2(n: Int): Obj
+external fun testPrimitive(obj: Obj, n: Int): Boolean
+
+external fun getNewObject(): Obj
+external fun testObject(obj: Obj): Boolean
+
+external fun getNewArray(): Obj
+external fun testArray(obj: Obj): Boolean
+
+external fun roundTrip(x: Obj): Obj
+
+fun assertTrue(x: Boolean) {
+ if (!x) error("assertTrue fail")
+}
+
+fun assertFalse(x: Boolean) {
+ if (x) error("assertFalse fail")
+}
+
+fun multiCast(obj: Obj): Obj =
+ obj
+ as Any as Any
+ as Any as? Any
+ as Any as Any?
+ as Any as? Any?
+ as Any as Obj
+ as Any as? Obj
+ as Any as Obj?
+ as Any as? Obj?
+
+ as Any? as Any
+ as Any? as? Any
+ as Any? as Any?
+ as Any? as? Any?
+ as Any? as Obj
+ as Any? as? Obj
+ as Any? as Obj?
+ as Any? as? Obj?
+
+ as Obj as Any
+ as Obj as? Any
+ as Obj as Any?
+ as Obj as? Any?
+ as Obj as Obj
+ as Obj as? Obj
+ as Obj as Obj?
+ as Obj as? Obj?
+
+ as Obj? as Any
+ as Obj? as? Any
+ as Obj? as Any?
+ as Obj? as? Any?
+ as Obj? as Obj
+ as Obj? as? Obj
+ as Obj? as Obj?
+ as Obj? as? Obj?
+ as Obj
+
+class CustomClass
+interface CustomInterface
+
+fun testWithTypeParameters(obj: Obj, checkObj: (Any?) -> Unit) {
+ checkObj(obj as T1)
+ checkObj(obj as T2)
+ checkObj(obj as T3)
+ checkObj(obj as? T1)
+ checkObj(obj as? T2)
+ checkObj(obj as? T3)
+ checkObj(obj as T1?)
+ checkObj(obj as T2?)
+ checkObj(obj as T3?)
+ checkObj(obj as? T1?)
+ checkObj(obj as? T2?)
+ checkObj(obj as? T3?)
+}
+
+fun test(
+ obj: Obj,
+ anotherObj: Obj,
+ checkObj: (Any?) -> Unit,
+ stableIdentity: Boolean
+) {
+ checkObj(obj)
+ checkObj(obj as Any)
+ checkObj(obj as? Any)
+ checkObj(obj as Any?)
+ checkObj(obj as? Any?)
+ testWithTypeParameters(obj, checkObj)
+ val subObj: SubObj = obj as SubObj
+ checkObj(subObj)
+ checkObj(subObj as Obj)
+ val roundTrippedObj = roundTrip(obj)
+ checkObj(roundTrippedObj)
+ assertTrue(obj == roundTrippedObj)
+ if (stableIdentity)
+ assertTrue(obj === roundTrippedObj)
+
+ val castedObj = multiCast(roundTrippedObj)
+ checkObj(castedObj)
+
+ assertTrue(obj is Any)
+ assertTrue(obj is Any?)
+ assertFalse(obj !is Any)
+ assertFalse(obj !is Any?)
+
+ assertFalse(obj as Any is CustomClass)
+ assertFalse(obj as Any is CustomInterface)
+
+ val objects = listOf(obj, roundTrippedObj, castedObj)
+ for (obj1 in objects) {
+ checkObj(obj1)
+ assertTrue(obj1.hashCode() == obj.hashCode())
+ assertTrue(obj1.toString() == obj.toString())
+ assertTrue(obj != anotherObj)
+ assertTrue(obj !== anotherObj)
+ for (obj2 in objects) {
+ assertTrue(obj1 == obj2)
+ if (stableIdentity)
+ assertTrue(obj1 === obj2)
+ assertTrue(obj1.hashCode() == obj2.hashCode())
+ assertTrue(obj1.toString() == obj2.toString())
+ }
+ }
+ assertTrue(objects.size == 3)
+ assertTrue(objects.toSet().size == 1)
+ assertTrue((objects + anotherObj).toSet().size == 2)
+}
+
+fun testPrimitive(n: Int) {
+ test(
+ getPrimitive1(n),
+ getPrimitive2(n),
+ { obj ->
+ if (!testPrimitive(obj as Obj, n)) {
+ error("Fail $n")
+ }
+ },
+ stableIdentity = false,
+ )
+}
+
+class C(val x: Int)
+value class IC(val x: Int)
+
+fun box(): String {
+ for (i in 0..2) { // TODO: Symbols and BigInts are not supprted in Wasm
+ testPrimitive(i)
+ }
+
+ test(
+ getNewObject(),
+ getNewObject(),
+ { obj ->
+ if (!testObject(obj as Obj)) {
+ error("Fail object")
+ }
+ },
+ stableIdentity = true,
+ )
+
+ test(
+ getNewArray(),
+ getNewArray(),
+ { obj ->
+ if (!testArray(obj as Obj)) {
+ error("Fail object")
+ }
+ },
+ stableIdentity = true,
+ )
+
+
+ val kotlinValues = listOf(10, "10", true, arrayOf(10), intArrayOf(20), C(10), IC(10))
+ val otherValues = listOf(11, "11", false, arrayOf(12), intArrayOf(22), C(11), IC(11))
+
+ for ((value, otherValue) in kotlinValues.zip(otherValues)) {
+ test(
+ value as Obj,
+ otherValues as Obj,
+ { obj ->
+ if (obj !== value) {
+ error("Fail custom class")
+ }
+ },
+ stableIdentity = true,
+ )
+ }
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/compiler/testData/codegen/boxWasmJsInterop/jsExport.kt b/compiler/testData/codegen/boxWasmJsInterop/jsExport.kt
index e381097831c..6b48f67016a 100644
--- a/compiler/testData/codegen/boxWasmJsInterop/jsExport.kt
+++ b/compiler/testData/codegen/boxWasmJsInterop/jsExport.kt
@@ -9,6 +9,20 @@ fun makeC(x: Int): C = C(x)
@JsExport
fun getX(c: C): Int = c.x
+@JsExport
+fun getString(s: String): String = "Test string $s";
+
+@JsExport
+fun isEven(x: Int): Boolean = x % 2 == 0
+
+external interface EI
+
+@JsExport
+fun eiAsAny(ei: EI): Any = ei
+
+@JsExport
+fun anyAsEI(any: Any): EI = any as EI
+
fun box(): String = "OK"
// FILE: jsExport__after.js
@@ -16,4 +30,16 @@ fun box(): String = "OK"
const c = main.makeC(300);
if (main.getX(c) !== 300) {
throw "Fail 1";
+}
+
+if (main.getString("2") !== "Test string 2") {
+ throw "Fail 2";
+}
+
+if (main.isEven(31) !== false || main.isEven(10) !== true) {
+ throw "Fail 3";
+}
+
+if (main.anyAsEI(main.eiAsAny({x:10})).x !== 10) {
+ throw "Fail 4";
}
\ No newline at end of file
diff --git a/compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt b/compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt
new file mode 100644
index 00000000000..cf50522e8b9
--- /dev/null
+++ b/compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt
@@ -0,0 +1,65 @@
+// TARGET_BACKEND: WASM
+// WITH_RUNTIME
+
+// FILE: externals.js
+
+function roundTrip(x) { return x; }
+
+// FILE: externals.kt
+
+external fun roundTrip(x: EI?): EI?
+
+fun assertTrue(x: Boolean) {
+ if (!x) error("assertTrue fail")
+}
+
+fun assertFalse(x: Boolean) {
+ if (x) error("assertFalse fail")
+}
+
+external interface EI
+
+@JsFun("() => null")
+external fun getNull(): EI?
+
+@JsFun("() => undefined")
+external fun getUndefined(): EI?
+
+@JsFun("(ref) => ref === null")
+external fun isJsNull(ref: EI?): Boolean
+
+@JsFun("(ref) => ref === undefined")
+external fun isJsUndefined(ref: EI?): Boolean
+
+@JsFun("() => null")
+external fun getJsNullAsNonNullable(): EI
+
+@JsFun("() => undefined")
+external fun getJsUndefinedAsNonNullable(): EI
+
+fun box(): String {
+ val jsNull = getNull()
+ val jsUndefined = getUndefined()
+
+ assertTrue(jsNull == null)
+ assertTrue((jsNull as Any?) == null)
+ assertTrue((jsNull as Any?) === null)
+ assertTrue(jsUndefined == null)
+
+ assertTrue(isJsNull(null))
+ assertTrue(isJsNull(null as EI?))
+ assertTrue(isJsNull(null as? EI?))
+ assertTrue(isJsNull(roundTrip(null)))
+ assertTrue(isJsNull(jsNull))
+
+ assertFalse(isJsUndefined(null))
+ assertTrue(isJsUndefined(jsUndefined))
+
+ // TODO: Should these fail?
+ val n2 = getJsNullAsNonNullable()
+ val ud2 = getJsUndefinedAsNonNullable()
+ assertTrue(isJsNull(n2))
+ assertTrue(isJsUndefined(ud2))
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/compiler/testData/codegen/boxWasmJsInterop/types.kt b/compiler/testData/codegen/boxWasmJsInterop/types.kt
new file mode 100644
index 00000000000..f49123b70a7
--- /dev/null
+++ b/compiler/testData/codegen/boxWasmJsInterop/types.kt
@@ -0,0 +1,112 @@
+// FILE: externals.js
+
+// -- Strings --
+
+function isTestString(x) {
+ return x === "Test string";
+}
+
+function getTestString() {
+ return "Test string";
+}
+
+function concatStrings(x, y) {
+ if (typeof x !== "string") return "Fail 1";
+ if (typeof y !== "string") return "Fail 2";
+ return x + y;
+}
+
+function concatStringsNullable(x, y) {
+ return concatStrings(x ?? "", y ?? "")
+}
+
+// -- Booleans --
+
+function isTrueBoolean(x) {
+ if (typeof x !== "boolean") return "Fail";
+ return x === true;
+}
+
+function isFalseBoolean(x) {
+ if (typeof x !== "boolean") return "Fail";
+ return x === false;
+}
+
+function getTrueBoolean(x) {
+ return true;
+}
+
+function getFalseBoolean(x) {
+ return false;
+}
+
+// -- Any --
+
+function createJsObjectAsAny() {
+ return { value: "object created by createJsObjectAsAny" };
+}
+
+function createJsObjectAsExternalInterface() {
+ return { value: "object created by createJsObjectAsExternalInterface" };
+}
+
+function getObjectValueEI(x) {
+ return x.value;
+}
+
+function getObjectValueAny(x) {
+ return x.value;
+}
+
+// FILE: externals.kt
+
+external fun isTestString(x: String): Boolean
+external fun getTestString(): String
+external fun concatStrings(x: String, y: String): String
+//external fun concatStringsNullable(x: String?, y: String?): String?
+
+external fun isTrueBoolean(x: Boolean): Boolean
+external fun isFalseBoolean(x: Boolean): Boolean
+external fun getTrueBoolean(): Boolean
+external fun getFalseBoolean(): Boolean
+
+external interface EI
+
+external fun createJsObjectAsAny(): Any
+external fun createJsObjectAsExternalInterface(): EI
+external fun getObjectValueEI(x: EI): String
+external fun getObjectValueAny(x: Any): String
+
+fun box(): String {
+ // Strings
+ if (!isTestString("Test string")) return "Fail !isTestString"
+ if (isTestString("Test string 2")) return "Fail isTestString"
+ if (getTestString() != "Test string") return "Fail getTestString"
+ if (concatStrings("A", "B") != "AB") return "Fail concatStrings 1"
+ if (concatStrings("Привет ", "😀\uD83D") != "Привет 😀\uD83D") return "Fail concatStrings 2"
+// if (concatStringsNullable("A", "B") != "AB") return "Fail concatStringsNullable 1"
+
+ // Boolean
+ if (!isTrueBoolean(true)) return "Fail !isTrueBoolean"
+ if (isTrueBoolean(false)) return "Fail isTrueBoolean"
+ if (!isFalseBoolean(false)) return "Fail !isFalseBoolean"
+ if (isFalseBoolean(true)) return "Fail isFalseBoolean"
+ if (getTrueBoolean() != true) return "Fail getTrueBoolean"
+ if (getFalseBoolean() != false) return "Fail getFalseBoolean"
+
+ // External interface
+ val objAsEI: EI = createJsObjectAsExternalInterface()
+ if (getObjectValueEI(objAsEI) != "object created by createJsObjectAsExternalInterface")
+ return "Fail createJsObjectAsExternalInterface + getObjectValueEI"
+ if (getObjectValueAny(objAsEI) != "object created by createJsObjectAsExternalInterface")
+ return "Fail createJsObjectAsExternalInterface + getObjectValueAny"
+
+ // Any
+ val objAsAny: Any = createJsObjectAsAny()
+ if (getObjectValueAny(objAsAny) != "object created by createJsObjectAsAny")
+ return "Fail createJsObjectAsAny + getObjectValueAny"
+ if (getObjectValueEI(objAsAny as EI) != "object created by createJsObjectAsAny")
+ return "Fail createJsObjectAsAny + getObjectValueEI"
+
+ return "OK"
+}
\ No newline at end of file
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt
index 7cf6f1ebfc2..c5177f72c93 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicWasmBoxTest.kt
@@ -195,7 +195,7 @@ abstract class BasicWasmBoxTest(
wasmInstance.exports.startUnitTests?.();
- const actualResult = importStringFromWasm(wasmInstance.exports.$testFunction());
+ const actualResult = wasmInstance.exports.$testFunction();
if (actualResult !== "OK")
throw `Wrong box result '${'$'}{actualResult}'; Expected "OK"`;
""".trimIndent()
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/abstractClassesForGeneratedIrTests.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/abstractClassesForGeneratedIrTests.kt
index 5d1d1f3cd54..a7bf227a984 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/abstractClassesForGeneratedIrTests.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/abstractClassesForGeneratedIrTests.kt
@@ -23,3 +23,9 @@ abstract class AbstractIrJsCodegenInlineTest : BasicIrBoxTest(
"compiler/testData/codegen/boxInline/",
"codegen/irBoxInline/"
)
+
+
+abstract class AbstractIrCodegenWasmJsInteropJsTest : BasicIrBoxTest(
+ "compiler/testData/codegen/wasmJsInterop",
+ "codegen/wasmJsInteropJs"
+)
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt
index 2ffb64a28eb..664635f6294 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/abstractClassesForGeneratedTests.kt
@@ -24,11 +24,6 @@ abstract class AbstractJsCodegenInlineTest : BasicBoxTest(
"codegen/boxInline"
)
-abstract class AbstractIrCodegenWasmJsInteropJsTest : BasicBoxTest(
- "compiler/testData/codegen/wasmJsInterop",
- "codegen/wasmJsInteropWasm"
-)
-
abstract class AbstractJsLegacyPrimitiveArraysBoxTest : BasicBoxTest(
"compiler/testData/codegen/box/arrays/",
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/IrCodegenWasmJsInteropJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrCodegenWasmJsInteropJsTestGenerated.java
similarity index 80%
rename from js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/IrCodegenWasmJsInteropJsTestGenerated.java
rename to js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrCodegenWasmJsInteropJsTestGenerated.java
index a5b348ed595..d92027949f9 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/IrCodegenWasmJsInteropJsTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrCodegenWasmJsInteropJsTestGenerated.java
@@ -3,7 +3,7 @@
* 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.js.test.semantics;
+package org.jetbrains.kotlin.js.test.ir.semantics;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
@@ -30,6 +30,11 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
+ @TestMetadata("externalTypeOperators.kt")
+ public void testExternalTypeOperators() throws Exception {
+ runTest("compiler/testData/codegen/boxWasmJsInterop/externalTypeOperators.kt");
+ }
+
@TestMetadata("externals.kt")
public void testExternals() throws Exception {
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
@@ -39,4 +44,9 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm
public void testJsExport() throws Exception {
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
}
+
+ @TestMetadata("types.kt")
+ public void testTypes() throws Exception {
+ runTest("compiler/testData/codegen/boxWasmJsInterop/types.kt");
+ }
}
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java
index 31f3817edd2..2370e26c62c 100644
--- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenWasmJsInteropWasmTestGenerated.java
@@ -30,6 +30,11 @@ public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWa
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true);
}
+ @TestMetadata("externalTypeOperators.kt")
+ public void testExternalTypeOperators() throws Exception {
+ runTest("compiler/testData/codegen/boxWasmJsInterop/externalTypeOperators.kt");
+ }
+
@TestMetadata("externals.kt")
public void testExternals() throws Exception {
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
@@ -39,4 +44,14 @@ public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWa
public void testJsExport() throws Exception {
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
}
+
+ @TestMetadata("nullableExternRefs.kt")
+ public void testNullableExternRefs() throws Exception {
+ runTest("compiler/testData/codegen/boxWasmJsInterop/nullableExternRefs.kt");
+ }
+
+ @TestMetadata("types.kt")
+ public void testTypes() throws Exception {
+ runTest("compiler/testData/codegen/boxWasmJsInterop/types.kt");
+ }
}
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt
new file mode 100644
index 00000000000..e77472a4b2b
--- /dev/null
+++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ExternalWrapper.kt
@@ -0,0 +1,191 @@
+/*
+ * 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 kotlin.wasm.internal
+
+import kotlin.wasm.internal.reftypes.anyref
+
+internal external interface ExternalInterfaceType
+
+internal class JsExternalBox(val ref: ExternalInterfaceType) {
+ override fun toString(): String =
+ externrefToString(ref)
+
+ override fun equals(other: Any?): Boolean =
+ if (other is JsExternalBox) {
+ externrefEquals(ref, other.ref)
+ } else {
+ false
+ }
+
+ override fun hashCode(): Int =
+ externrefHashCode(ref)
+}
+
+//language=js
+@JsFun("""
+(() => {
+const dataView = new DataView(new ArrayBuffer(8));
+function numberHashCode(obj) {
+ if ((obj | 0) === obj) {
+ return obj | 0;
+ } else {
+ dataView.setFloat64(0, obj, true);
+ return (dataView.getInt32(0, true) * 31 | 0) + dataView.getInt32(4, true) | 0;
+ }
+}
+
+const hashCodes = new WeakMap();
+function getObjectHashCode(obj) {
+ const res = hashCodes.get(obj);
+ if (res === undefined) {
+ const POW_2_32 = 4294967296;
+ const hash = (Math.random() * POW_2_32) | 0;
+ hashCodes.set(obj, hash);
+ return hash;
+ }
+ return res;
+}
+
+function getStringHashCode(str) {
+ var hash = 0;
+ for (var i = 0; i < str.length; i++) {
+ var code = str.charCodeAt(i);
+ hash = (hash * 31 + code) | 0;
+ }
+ return hash;
+}
+
+return (obj) => {
+ if (obj == null) {
+ return 0;
+ }
+ switch (typeof obj) {
+ case "object":
+ case "function":
+ return getObjectHashCode(obj);
+ case "number":
+ return numberHashCode(obj);
+ case "boolean":
+ return obj;
+ default:
+ return getStringHashCode(String(obj));
+ }
+}
+})()"""
+)
+private external fun externrefHashCode(ref: ExternalInterfaceType): Int
+
+@JsFun("ref => String(ref)")
+private external fun externrefToString(ref: ExternalInterfaceType): String
+
+@JsFun("(lhs, rhs) => lhs === rhs")
+private external fun externrefEquals(lhs: ExternalInterfaceType, rhs: ExternalInterfaceType): Boolean
+
+@JsFun("(ref) => (typeof ref !== 'object' || ref === null) ? null : (externrefBoxes.get(ref) ?? null)")
+private external fun getExternrefBoxOrNull(ref: ExternalInterfaceType): JsExternalBox?
+
+@JsFun("(ref, box) => { if (typeof ref === 'object' && ref !== null) { externrefBoxes.set(ref, box); } }")
+private external fun setExternrefBox(ref: ExternalInterfaceType, box: JsExternalBox)
+
+@WasmNoOpCast
+@Suppress("unused")
+internal fun Any?.asWasmAnyref(): anyref =
+ implementedAsIntrinsic
+
+@WasmNoOpCast
+@Suppress("unused")
+internal fun ExternalInterfaceType.externAsWasmAnyref(): anyref =
+ implementedAsIntrinsic
+
+@WasmNoOpCast
+@Suppress("unused")
+internal fun Any?.asWasmExternRef(): ExternalInterfaceType =
+ implementedAsIntrinsic
+
+
+@JsFun("(ref) => ref == null")
+internal external fun isNullish(ref: ExternalInterfaceType): Boolean
+
+internal fun externRefToAny(ref: ExternalInterfaceType): Any? {
+ // If ref is an instance of kotlin class -- return it cased to Any
+ val refAsAnyref = ref.externAsWasmAnyref()
+ if (wasm_ref_is_data(refAsAnyref)) {
+ val refAsDataRef = wasm_ref_as_data(refAsAnyref)
+ if (wasm_ref_test(refAsDataRef)) {
+ return wasm_ref_cast(refAsDataRef)
+ }
+ }
+
+ if (isNullish(ref))
+ return null
+
+ // If we already have a box -- return it,
+ // otherwise -- create a new box and remember it.
+ return getExternrefBoxOrNull(ref)
+ ?: JsExternalBox(ref).also {
+ setExternrefBox(ref, it)
+ }
+}
+
+
+internal fun anyToExternRef(x: Any): ExternalInterfaceType {
+ return if (x is JsExternalBox)
+ x.ref
+ else
+ x.asWasmExternRef()
+}
+
+internal external fun importStringFromWasm(addr: Int): ExternalInterfaceType
+
+@JsFun("x => x.length")
+internal external fun stringLength(x: ExternalInterfaceType): Int
+
+internal fun convertJsStringToKotlinString(x: ExternalInterfaceType): String {
+ val length = stringLength(x)
+ val addr = unsafeGetScratchRawMemory(INT_SIZE_BYTES + length * CHAR_SIZE_BYTES)
+ jsWriteStringIntoMemory(x, addr)
+ return kotlin.String(unsafeRawMemoryToCharArray(addr, length))
+}
+
+
+//language=js
+@JsFun(
+""" (str, addr) => {
+ const memory = new DataView(wasmInstance.exports.memory.buffer);
+ for (var i = 0; i < str.length; i++) {
+ memory.setInt16(addr + i * 2, str.charCodeAt(i), true);
+ }
+}
+"""
+)
+internal external fun jsWriteStringIntoMemory(str: ExternalInterfaceType, addr: Int)
+
+
+internal fun kotlinToJsStringAdapter(x: String): ExternalInterfaceType {
+ return importStringFromWasm(exportString(x))
+}
+
+@JsFun("() => true")
+internal external fun jsTrue(): ExternalInterfaceType
+
+@JsFun("() => false")
+internal external fun jsFalse(): ExternalInterfaceType
+
+internal fun kotlinToJsBooleanAdapter(x: Boolean): ExternalInterfaceType =
+ if (x) jsTrue() else jsFalse()
+
+internal fun kotlinToJsAnyAdapter(x: Any): ExternalInterfaceType =
+ anyToExternRef(x)
+
+internal fun jsToKotlinAnyAdapter(x: ExternalInterfaceType): Any? =
+ externRefToAny(x)
+
+internal fun jsToKotlinStringAdapter(x: ExternalInterfaceType): String =
+ convertJsStringToKotlinString(x)
+
+internal fun jsToKotlinByteAdapter(x: Int): Byte = x.toByte()
+internal fun jsToKotlinShortAdapter(x: Int): Short = x.toShort()
+internal fun jsToKotlinCharAdapter(x: Int): Char = x.toChar()
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
index 692474f9602..1ba074f4d8b 100644
--- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
+++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
@@ -9,6 +9,11 @@
package kotlin.wasm.internal
+import kotlin.wasm.internal.reftypes.anyref
+import kotlin.wasm.internal.reftypes.dataref
+import kotlin.wasm.internal.reftypes.funcref
+import kotlin.wasm.internal.reftypes.i31ref
+
@WasmOp(WasmOp.UNREACHABLE)
internal fun wasm_unreachable(): Nothing =
implementedAsIntrinsic
@@ -329,4 +334,25 @@ public external fun wasm_i64_clz(a: Long): Long
public external fun wasm_i64_popcnt(a: Long): Long
@WasmOp(WasmOp.I64_CTZ)
-public external fun wasm_i64_ctz(a: Long): Long
\ No newline at end of file
+public external fun wasm_i64_ctz(a: Long): Long
+
+
+// Reference type operators
+
+@WasmOp(WasmOp.REF_IS_FUNC)
+internal external fun wasm_ref_is_func(x: anyref): Boolean
+
+@WasmOp(WasmOp.REF_IS_DATA)
+internal external fun wasm_ref_is_data(x: anyref): Boolean
+
+@WasmOp(WasmOp.REF_IS_I31)
+internal external fun wasm_ref_is_i31(x: anyref): Boolean
+
+@WasmOp(WasmOp.REF_AS_FUNC)
+internal external fun wasm_ref_as_func(x: anyref): funcref
+
+@WasmOp(WasmOp.REF_AS_DATA)
+internal external fun wasm_ref_as_data(x: anyref): dataref
+
+@WasmOp(WasmOp.REF_AS_I31)
+internal external fun wasm_ref_as_i31(x: anyref): i31ref
\ No newline at end of file
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/reftypes/WasmReferenceTypes.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/reftypes/WasmReferenceTypes.kt
new file mode 100644
index 00000000000..bbf9d671d51
--- /dev/null
+++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/reftypes/WasmReferenceTypes.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+@file:ExcludedFromCodegen
+
+package kotlin.wasm.internal.reftypes
+
+import kotlin.wasm.internal.*
+
+// These interfaces correspond to Wasm GC reference types with the same name.
+// They are not proper Kotlin interfaces and should be used with care.
+//
+// WARNING! Do not upcast to Any, nullable types, type parameters, etc.
+// Do not use Any methods
+// Do not use type operators `is`, `as`, etc.
+// Do not use ==, ===
+//
+// Use dedicated intrinsics instead.
+
+internal interface anyref
+internal interface eqref : anyref
+internal interface dataref : eqref
+internal interface i31ref : eqref
+internal interface funcref : anyref
\ No newline at end of file
diff --git a/libraries/stdlib/wasm/src/generated/_WasmOp.kt b/libraries/stdlib/wasm/src/generated/_WasmOp.kt
index 87e2f12b01a..82d188a1894 100644
--- a/libraries/stdlib/wasm/src/generated/_WasmOp.kt
+++ b/libraries/stdlib/wasm/src/generated/_WasmOp.kt
@@ -216,6 +216,12 @@ internal annotation class WasmOp(val name: String) {
const val RETURN = "RETURN"
const val CALL = "CALL"
const val CALL_INDIRECT = "CALL_INDIRECT"
+ const val TRY = "TRY"
+ const val CATCH = "CATCH"
+ const val CATCH_ALL = "CATCH_ALL"
+ const val DELEGATE = "DELEGATE"
+ const val THROW = "THROW"
+ const val RETHROW = "RETHROW"
const val DROP = "DROP"
const val SELECT = "SELECT"
const val SELECT_TYPED = "SELECT_TYPED"
@@ -255,5 +261,19 @@ internal annotation class WasmOp(val name: String) {
const val REF_TEST = "REF_TEST"
const val REF_CAST = "REF_CAST"
const val BR_ON_CAST = "BR_ON_CAST"
+ const val BR_ON_CAST_FAIL = "BR_ON_CAST_FAIL"
+ const val REF_IS_FUNC = "REF_IS_FUNC"
+ const val REF_IS_DATA = "REF_IS_DATA"
+ const val REF_IS_I31 = "REF_IS_I31"
+ const val REF_AS_FUNC = "REF_AS_FUNC"
+ const val REF_AS_DATA = "REF_AS_DATA"
+ const val REF_AS_I31 = "REF_AS_I31"
+ const val BR_ON_FUNC = "BR_ON_FUNC"
+ const val BR_ON_DATA = "BR_ON_DATA"
+ const val BR_ON_I31 = "BR_ON_I31"
+ const val BR_ON_NON_FUNC = "BR_ON_NON_FUNC"
+ const val BR_ON_NON_DATA = "BR_ON_NON_DATA"
+ const val BR_ON_NON_I31 = "BR_ON_NON_I31"
+ const val GET_UNIT = "GET_UNIT"
}
}
diff --git a/libraries/stdlib/wasm/src/kotlin/js/annotations.kt b/libraries/stdlib/wasm/src/kotlin/js/annotations.kt
index bb890f25b40..e075105e945 100644
--- a/libraries/stdlib/wasm/src/kotlin/js/annotations.kt
+++ b/libraries/stdlib/wasm/src/kotlin/js/annotations.kt
@@ -13,4 +13,18 @@ package kotlin.js
@ExperimentalJsExport
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.FUNCTION)
-public actual annotation class JsExport
\ No newline at end of file
+public actual annotation class JsExport
+
+/**
+ * Specifies JavaScript name for external and imported declarations
+ */
+@Retention(AnnotationRetention.BINARY)
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY,
+ AnnotationTarget.CONSTRUCTOR,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+public actual annotation class JsName(actual val name: String)
\ No newline at end of file
diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt
index f63f43edbe9..18965959c54 100644
--- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt
+++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt
@@ -368,6 +368,23 @@ enum class WasmOp(
BR_ON_CAST("br_on_cast", 0xFB_42, listOf(LABEL_IDX)),
+ BR_ON_CAST_FAIL("br_on_cast_fail", 0xfb43, listOf(LABEL_IDX)),
+
+ REF_IS_FUNC("ref.is_func", 0xfb50),
+ REF_IS_DATA("ref.is_data", 0xfb51),
+ REF_IS_I31("ref.is_i31", 0xfb52),
+ REF_AS_FUNC("ref.as_func", 0xfb58),
+ REF_AS_DATA("ref.as_data", 0xfb59),
+ REF_AS_I31("ref.as_i31", 0xfb5a),
+
+ BR_ON_FUNC("br_on_func", 0xfb60, listOf(LABEL_IDX)),
+ BR_ON_DATA("br_on_data", 0xfb61, listOf(LABEL_IDX)),
+ BR_ON_I31("br_on_i31", 0xfb62, listOf(LABEL_IDX)),
+
+ BR_ON_NON_FUNC("br_on_non_func", 0xfb63, listOf(LABEL_IDX)),
+ BR_ON_NON_DATA("br_on_non_data", 0xfb64, listOf(LABEL_IDX)),
+ BR_ON_NON_I31("br_on_non_i31", 0xfb65, listOf(LABEL_IDX)),
+
// Pseudo-instruction, just alias for a normal call. It's used to easily spot get_unit on the wasm level.
GET_UNIT("call", 0x10, FUNC_IDX)
;