[Wasm] Imporove external interface support
* Support boxing/unboxing when casting to Any * Support ===, equals, hashCode, toString * Support adapting String in interop boundary
This commit is contained in:
+3
-1
@@ -1,6 +1,8 @@
|
|||||||
<component name="ArtifactManager">
|
<component name="ArtifactManager">
|
||||||
<artifact type="jar" name="kotlin-test-wasm-js-1.6.255-SNAPSHOT">
|
<artifact type="jar" name="kotlin-test-wasm-js-1.6.255-SNAPSHOT">
|
||||||
<output-path>$PROJECT_DIR$/libraries/kotlin.test/wasm/build/libs</output-path>
|
<output-path>$PROJECT_DIR$/libraries/kotlin.test/wasm/build/libs</output-path>
|
||||||
<root id="archive" name="kotlin-test-wasm-js-1.6.255-SNAPSHOT.jar" />
|
<root id="archive" name="kotlin-test-wasm-js-1.6.255-SNAPSHOT.jar">
|
||||||
|
<element id="module-output" name="kotlin.kotlin-test.kotlin-test-wasm.jsMain" />
|
||||||
|
</root>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
@@ -35,7 +35,6 @@ class WasmBackendContext(
|
|||||||
override val irBuiltIns: IrBuiltIns,
|
override val irBuiltIns: IrBuiltIns,
|
||||||
symbolTable: SymbolTable,
|
symbolTable: SymbolTable,
|
||||||
irModuleFragment: IrModuleFragment,
|
irModuleFragment: IrModuleFragment,
|
||||||
val additionalExportedDeclarations: Set<FqName>,
|
|
||||||
override val configuration: CompilerConfiguration,
|
override val configuration: CompilerConfiguration,
|
||||||
) : JsCommonBackendContext {
|
) : JsCommonBackendContext {
|
||||||
override val builtIns = module.builtIns
|
override val builtIns = module.builtIns
|
||||||
|
|||||||
+24
-3
@@ -66,7 +66,7 @@ private val expectDeclarationsRemovingPhase = makeWasmModulePhase(
|
|||||||
description = "Remove expect declaration from module fragment"
|
description = "Remove expect declaration from module fragment"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val stringConstructorLowering = makeWasmModulePhase(
|
private val stringConcatenationLowering = makeWasmModulePhase(
|
||||||
::StringConcatenationLowering,
|
::StringConcatenationLowering,
|
||||||
name = "StringConcatenation",
|
name = "StringConcatenation",
|
||||||
description = "String concatenation lowering"
|
description = "String concatenation lowering"
|
||||||
@@ -122,6 +122,18 @@ private val tailrecLoweringPhase = makeWasmModulePhase(
|
|||||||
description = "Replace `tailrec` call sites with equivalent loop"
|
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(
|
private val enumClassConstructorLoweringPhase = makeWasmModulePhase(
|
||||||
::EnumClassConstructorLowering,
|
::EnumClassConstructorLowering,
|
||||||
name = "EnumClassConstructorLowering",
|
name = "EnumClassConstructorLowering",
|
||||||
@@ -134,7 +146,6 @@ private val enumClassConstructorBodyLoweringPhase = makeWasmModulePhase(
|
|||||||
description = "Transform Enum Class into regular Class"
|
description = "Transform Enum Class into regular Class"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
private val enumEntryInstancesLoweringPhase = makeWasmModulePhase(
|
private val enumEntryInstancesLoweringPhase = makeWasmModulePhase(
|
||||||
::EnumEntryInstancesLowering,
|
::EnumEntryInstancesLowering,
|
||||||
name = "EnumEntryInstancesLowering",
|
name = "EnumEntryInstancesLowering",
|
||||||
@@ -431,6 +442,12 @@ private val objectUsageLoweringPhase = makeWasmModulePhase(
|
|||||||
description = "Transform IrGetObjectValue into instance generator call"
|
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(
|
private val typeOperatorLoweringPhase = makeWasmModulePhase(
|
||||||
::WasmTypeOperatorLowering,
|
::WasmTypeOperatorLowering,
|
||||||
name = "TypeOperatorLowering",
|
name = "TypeOperatorLowering",
|
||||||
@@ -515,6 +532,9 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
delegateToPrimaryConstructorLoweringPhase then
|
delegateToPrimaryConstructorLoweringPhase then
|
||||||
// Common prefix ends
|
// Common prefix ends
|
||||||
|
|
||||||
|
jsInteropFunctionsLowering then
|
||||||
|
jsInteropFunctionCallsLowering then
|
||||||
|
|
||||||
enumEntryInstancesLoweringPhase then
|
enumEntryInstancesLoweringPhase then
|
||||||
enumEntryInstancesBodyLoweringPhase then
|
enumEntryInstancesBodyLoweringPhase then
|
||||||
enumClassCreateInitializerLoweringPhase then
|
enumClassCreateInitializerLoweringPhase then
|
||||||
@@ -536,7 +556,7 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
|
|
||||||
forLoopsLoweringPhase then
|
forLoopsLoweringPhase then
|
||||||
propertyAccessorInlinerLoweringPhase then
|
propertyAccessorInlinerLoweringPhase then
|
||||||
stringConstructorLowering then
|
stringConcatenationLowering then
|
||||||
|
|
||||||
defaultArgumentStubGeneratorPhase then
|
defaultArgumentStubGeneratorPhase then
|
||||||
defaultArgumentPatchOverridesPhase then
|
defaultArgumentPatchOverridesPhase then
|
||||||
@@ -564,6 +584,7 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
builtInsLoweringPhase0 then
|
builtInsLoweringPhase0 then
|
||||||
|
|
||||||
autoboxingTransformerPhase then
|
autoboxingTransformerPhase then
|
||||||
|
explicitlyCastExternalTypesPhase then
|
||||||
objectUsageLoweringPhase then
|
objectUsageLoweringPhase then
|
||||||
typeOperatorLoweringPhase then
|
typeOperatorLoweringPhase then
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.types.classifierOrFail
|
|||||||
import org.jetbrains.kotlin.ir.types.defaultType
|
import org.jetbrains.kotlin.ir.types.defaultType
|
||||||
import org.jetbrains.kotlin.ir.types.typeWith
|
import org.jetbrains.kotlin.ir.types.typeWith
|
||||||
import org.jetbrains.kotlin.ir.util.SymbolTable
|
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.ir.util.render
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
@@ -157,9 +158,7 @@ class WasmSymbols(
|
|||||||
val nullableFloatIeee754Equals = getInternalFunction("nullableFloatIeee754Equals")
|
val nullableFloatIeee754Equals = getInternalFunction("nullableFloatIeee754Equals")
|
||||||
val nullableDoubleIeee754Equals = getInternalFunction("nullableDoubleIeee754Equals")
|
val nullableDoubleIeee754Equals = getInternalFunction("nullableDoubleIeee754Equals")
|
||||||
|
|
||||||
val exportString = getInternalFunction("exportString")
|
|
||||||
val unsafeGetScratchRawMemory = getInternalFunction("unsafeGetScratchRawMemory")
|
val unsafeGetScratchRawMemory = getInternalFunction("unsafeGetScratchRawMemory")
|
||||||
|
|
||||||
val startCoroutineUninterceptedOrReturnIntrinsics =
|
val startCoroutineUninterceptedOrReturnIntrinsics =
|
||||||
(0..2).map { getInternalFunction("startCoroutineUninterceptedOrReturnIntrinsic$it") }
|
(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 =
|
private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
|
||||||
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||||
|
|
||||||
|
|||||||
@@ -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.common.phaser.invokeToplevel
|
||||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCompiledModuleFragment
|
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmCompiledModuleFragment
|
||||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleFragmentGenerator
|
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.MainModule
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
|
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
|
||||||
import org.jetbrains.kotlin.ir.backend.js.loadIr
|
import org.jetbrains.kotlin.ir.backend.js.loadIr
|
||||||
@@ -45,7 +46,7 @@ fun compileWasm(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val moduleDescriptor = moduleFragment.descriptor
|
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
|
// Load declarations referenced during `context` initialization
|
||||||
allModules.forEach {
|
allModules.forEach {
|
||||||
@@ -63,6 +64,8 @@ fun compileWasm(
|
|||||||
deserializer.postProcess()
|
deserializer.postProcess()
|
||||||
symbolTable.noUnboundLeft("Unbound symbols at the end of linker")
|
symbolTable.noUnboundLeft("Unbound symbols at the end of linker")
|
||||||
|
|
||||||
|
moduleFragment.files.forEach { irFile -> markExportedDeclarations(context, irFile, exportedDeclarations) }
|
||||||
|
|
||||||
wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment)
|
wasmPhases.invokeToplevel(phaseConfig, context, moduleFragment)
|
||||||
|
|
||||||
val compiledWasmModule = WasmCompiledModuleFragment(context.irBuiltIns)
|
val compiledWasmModule = WasmCompiledModuleFragment(context.irBuiltIns)
|
||||||
@@ -89,9 +92,12 @@ fun compileWasm(
|
|||||||
|
|
||||||
|
|
||||||
fun WasmCompiledModuleFragment.generateJs(): String {
|
fun WasmCompiledModuleFragment.generateJs(): String {
|
||||||
|
//language=js
|
||||||
val runtime = """
|
val runtime = """
|
||||||
var wasmInstance = null;
|
var wasmInstance = null;
|
||||||
|
|
||||||
|
const externrefBoxes = new WeakMap();
|
||||||
|
|
||||||
const runtime = {
|
const runtime = {
|
||||||
identity(x) {
|
identity(x) {
|
||||||
return x;
|
return x;
|
||||||
|
|||||||
-30
@@ -185,18 +185,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
|||||||
else
|
else
|
||||||
context.referenceLocal(valueSymbol)
|
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) {
|
override fun visitSetValue(expression: IrSetValue) {
|
||||||
@@ -324,16 +312,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
|||||||
body.buildCall(context.referenceFunction(function.symbol))
|
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
|
// Unit types don't cross function boundaries
|
||||||
if (function.returnType == irBuiltIns.unitType)
|
if (function.returnType == irBuiltIns.unitType)
|
||||||
body.buildGetUnit()
|
body.buildGetUnit()
|
||||||
@@ -460,14 +438,6 @@ class BodyGenerator(val context: WasmFunctionCodegenContext) : IrElementVisitorV
|
|||||||
body.buildGetLocal(context.referenceLocal(0))
|
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)
|
body.buildInstr(WasmOp.RETURN)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-23
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
|
|||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
|
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.findUnitGetInstanceFunction
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||||
@@ -69,33 +70,18 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
|
|||||||
// Generate function type
|
// Generate function type
|
||||||
val watName = declaration.fqNameWhenAvailable.toString()
|
val watName = declaration.fqNameWhenAvailable.toString()
|
||||||
val irParameters = declaration.getEffectiveValueParameters()
|
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 =
|
val resultType =
|
||||||
when {
|
when {
|
||||||
isExported -> context.transformExportedResultType(declaration.returnType)
|
|
||||||
// Unit_getInstance returns true Unit reference instead of "void"
|
// Unit_getInstance returns true Unit reference instead of "void"
|
||||||
declaration == unitGetInstanceFunction -> context.transformType(declaration.returnType)
|
declaration == unitGetInstanceFunction -> context.transformType(declaration.returnType)
|
||||||
else -> context.transformResultType(declaration.returnType)
|
else -> context.transformResultType(declaration.returnType)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isExportedOrImported = isExported || importedName != null
|
|
||||||
val wasmFunctionType =
|
val wasmFunctionType =
|
||||||
WasmFunctionType(
|
WasmFunctionType(
|
||||||
name = watName,
|
name = watName,
|
||||||
parameterTypes = irParameters.map {
|
parameterTypes = irParameters.map { context.transformValueParameterType(it) },
|
||||||
val t = context.transformValueParameterType(it)
|
resultTypes = listOfNotNull(resultType)
|
||||||
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
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
context.defineFunctionType(declaration.symbol, wasmFunctionType)
|
context.defineFunctionType(declaration.symbol, wasmFunctionType)
|
||||||
|
|
||||||
@@ -164,12 +150,11 @@ class DeclarationGenerator(val context: WasmModuleCodegenContext) : IrElementVis
|
|||||||
if (initPriority != null)
|
if (initPriority != null)
|
||||||
context.registerInitFunction(function, initPriority)
|
context.registerInitFunction(function, initPriority)
|
||||||
|
|
||||||
if (declaration.isExported(backendContext)) {
|
if (declaration.isExported()) {
|
||||||
context.addExport(
|
context.addExport(
|
||||||
WasmExport.Function(
|
WasmExport.Function(
|
||||||
field = function,
|
field = function,
|
||||||
// TODO: Add ability to specify exported name.
|
name = declaration.getJsNameOrKotlinName().identifier
|
||||||
name = declaration.name.identifier
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -371,7 +356,7 @@ fun generateDefaultInitializerForType(type: WasmType, g: WasmExpressionBuilder)
|
|||||||
WasmF32 -> g.buildConstF32(0f)
|
WasmF32 -> g.buildConstF32(0f)
|
||||||
WasmF64 -> g.buildConstF64(0.0)
|
WasmF64 -> g.buildConstF64(0.0)
|
||||||
is WasmRefNullType -> g.buildRefNull(type.heapType)
|
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")
|
WasmUnreachableType -> error("Unreachable type can't be initialized")
|
||||||
else -> error("Unknown value type ${type.name}")
|
else -> error("Unknown value type ${type.name}")
|
||||||
}
|
}
|
||||||
@@ -381,5 +366,5 @@ fun IrFunction.getEffectiveValueParameters(): List<IrValueParameter> {
|
|||||||
return listOfNotNull(implicitThis, dispatchReceiverParameter, extensionReceiverParameter) + valueParameters
|
return listOfNotNull(implicitThis, dispatchReceiverParameter, extensionReceiverParameter) + valueParameters
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrFunction.isExported(context: WasmBackendContext): Boolean =
|
fun IrFunction.isExported(): Boolean =
|
||||||
fqNameWhenAvailable in context.additionalExportedDeclarations || isJsExport()
|
isJsExport()
|
||||||
+19
-2
@@ -12,9 +12,12 @@ import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
|
|||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
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.classifierOrNull
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
import org.jetbrains.kotlin.ir.util.isInterface
|
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.*
|
import org.jetbrains.kotlin.wasm.ir.*
|
||||||
|
|
||||||
class WasmTypeTransformer(
|
class WasmTypeTransformer(
|
||||||
@@ -93,13 +96,21 @@ class WasmTypeTransformer(
|
|||||||
symbols.voidType ->
|
symbols.voidType ->
|
||||||
error("Void type can't be used as a value")
|
error("Void type can't be used as a value")
|
||||||
|
|
||||||
// this also handles builtIns.stringType
|
|
||||||
else -> {
|
else -> {
|
||||||
val klass = this.getClass()
|
val klass = this.getClass()
|
||||||
val ic = context.backendContext.inlineClassesUtils.getInlinedClass(this)
|
val ic = context.backendContext.inlineClassesUtils.getInlinedClass(this)
|
||||||
|
|
||||||
if (klass != null && (klass.hasWasmForeignAnnotation() || klass.isExternal)) {
|
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) {
|
} else if (ic != null) {
|
||||||
context.backendContext.inlineClassesUtils.getInlineClassUnderlyingType(ic).toWasmValueType()
|
context.backendContext.inlineClassesUtils.getInlineClassUnderlyingType(ic).toWasmValueType()
|
||||||
} else {
|
} 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
|
// Return null if upper bound is Any
|
||||||
private val IrTypeParameter.erasedUpperBound: IrClass?
|
private val IrTypeParameter.erasedUpperBound: IrClass?
|
||||||
|
|||||||
-1
@@ -43,7 +43,6 @@ interface WasmBaseCodegenContext {
|
|||||||
fun transformBoxedType(irType: IrType): WasmType
|
fun transformBoxedType(irType: IrType): WasmType
|
||||||
fun transformValueParameterType(irValueParameter: IrValueParameter): WasmType
|
fun transformValueParameterType(irValueParameter: IrValueParameter): WasmType
|
||||||
fun transformResultType(irType: IrType): WasmType?
|
fun transformResultType(irType: IrType): WasmType?
|
||||||
fun transformExportedResultType(irType: IrType): WasmType?
|
|
||||||
fun transformBlockResultType(irType: IrType): WasmType?
|
fun transformBlockResultType(irType: IrType): WasmType?
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
-7
@@ -58,13 +58,6 @@ class WasmModuleCodegenContextImpl(
|
|||||||
return with(typeTransformer) { irType.toWasmResultType() }
|
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? {
|
override fun transformBlockResultType(irType: IrType): WasmType? {
|
||||||
return with(typeTransformer) { irType.toWasmBlockResultType() }
|
return with(typeTransformer) { irType.toWasmBlockResultType() }
|
||||||
}
|
}
|
||||||
|
|||||||
+29
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+308
@@ -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<IrDeclaration>? {
|
||||||
|
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<IrDeclaration>? {
|
||||||
|
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 { <original-body> }
|
||||||
|
*
|
||||||
|
* ->
|
||||||
|
*
|
||||||
|
* @JsExport
|
||||||
|
* @JsName("foo")
|
||||||
|
* fun foo__JsExportAdapter(x: JsType): JsType =
|
||||||
|
* adaptResult(foo(adaptParameter(x)));
|
||||||
|
*
|
||||||
|
* fun foo(x: KotlinType): KotlinType { <original-body> }
|
||||||
|
*/
|
||||||
|
fun transformExportFunction(function: IrSimpleFunction): List<IrDeclaration>? {
|
||||||
|
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<InteropTypeAdapter?>,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
-1
@@ -152,6 +152,8 @@ class WasmBaseTypeOperatorTransformer(val context: WasmBackendContext) : IrEleme
|
|||||||
private fun generateTypeCheckNonNull(argument: IrExpression, toType: IrType): IrExpression {
|
private fun generateTypeCheckNonNull(argument: IrExpression, toType: IrType): IrExpression {
|
||||||
assert(!toType.isMarkedNullable())
|
assert(!toType.isMarkedNullable())
|
||||||
return when {
|
return when {
|
||||||
|
// TODO: Use instanceof for classes later
|
||||||
|
toType.eraseToClassOrInterface.isExternal -> builder.irTrue()
|
||||||
toType.isNothing() -> builder.irFalse()
|
toType.isNothing() -> builder.irFalse()
|
||||||
toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType)
|
toType.isTypeParameter() -> generateTypeCheckWithTypeParameter(argument, toType)
|
||||||
toType.isInterface() -> generateIsInterface(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
|
return value
|
||||||
}
|
}
|
||||||
if (toType.isNothing()) {
|
if (toType.isNothing()) {
|
||||||
|
|||||||
+27
@@ -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<FqName>) {
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,10 @@ class JsMapping(val state: JsMappingState) : DefaultMapping(state) {
|
|||||||
state.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
|
state.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
|
||||||
|
|
||||||
val suspendArityStore = state.newDeclarationToDeclarationCollectionMapping<IrClass, Collection<IrSimpleFunction>>()
|
val suspendArityStore = state.newDeclarationToDeclarationCollectionMapping<IrClass, Collection<IrSimpleFunction>>()
|
||||||
|
|
||||||
|
// Wasm mappings
|
||||||
|
val wasmJsInteropFunctionToWrapper =
|
||||||
|
state.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 <T1 : Any, T2 : Any?, T3 : Obj> 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<Any, Any?, Obj>(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>(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"
|
||||||
|
}
|
||||||
@@ -9,6 +9,20 @@ fun makeC(x: Int): C = C(x)
|
|||||||
@JsExport
|
@JsExport
|
||||||
fun getX(c: C): Int = c.x
|
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"
|
fun box(): String = "OK"
|
||||||
|
|
||||||
// FILE: jsExport__after.js
|
// FILE: jsExport__after.js
|
||||||
@@ -16,4 +30,16 @@ fun box(): String = "OK"
|
|||||||
const c = main.makeC(300);
|
const c = main.makeC(300);
|
||||||
if (main.getX(c) !== 300) {
|
if (main.getX(c) !== 300) {
|
||||||
throw "Fail 1";
|
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";
|
||||||
}
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
+112
@@ -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 ?? "<null>", y ?? "<null>")
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- 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"
|
||||||
|
}
|
||||||
@@ -195,7 +195,7 @@ abstract class BasicWasmBoxTest(
|
|||||||
|
|
||||||
wasmInstance.exports.startUnitTests?.();
|
wasmInstance.exports.startUnitTests?.();
|
||||||
|
|
||||||
const actualResult = importStringFromWasm(wasmInstance.exports.$testFunction());
|
const actualResult = wasmInstance.exports.$testFunction();
|
||||||
if (actualResult !== "OK")
|
if (actualResult !== "OK")
|
||||||
throw `Wrong box result '${'$'}{actualResult}'; Expected "OK"`;
|
throw `Wrong box result '${'$'}{actualResult}'; Expected "OK"`;
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|||||||
+6
@@ -23,3 +23,9 @@ abstract class AbstractIrJsCodegenInlineTest : BasicIrBoxTest(
|
|||||||
"compiler/testData/codegen/boxInline/",
|
"compiler/testData/codegen/boxInline/",
|
||||||
"codegen/irBoxInline/"
|
"codegen/irBoxInline/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
abstract class AbstractIrCodegenWasmJsInteropJsTest : BasicIrBoxTest(
|
||||||
|
"compiler/testData/codegen/wasmJsInterop",
|
||||||
|
"codegen/wasmJsInteropJs"
|
||||||
|
)
|
||||||
|
|||||||
-5
@@ -24,11 +24,6 @@ abstract class AbstractJsCodegenInlineTest : BasicBoxTest(
|
|||||||
"codegen/boxInline"
|
"codegen/boxInline"
|
||||||
)
|
)
|
||||||
|
|
||||||
abstract class AbstractIrCodegenWasmJsInteropJsTest : BasicBoxTest(
|
|
||||||
"compiler/testData/codegen/wasmJsInterop",
|
|
||||||
"codegen/wasmJsInteropWasm"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
abstract class AbstractJsLegacyPrimitiveArraysBoxTest : BasicBoxTest(
|
abstract class AbstractJsLegacyPrimitiveArraysBoxTest : BasicBoxTest(
|
||||||
"compiler/testData/codegen/box/arrays/",
|
"compiler/testData/codegen/box/arrays/",
|
||||||
|
|||||||
+11
-1
@@ -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.
|
* 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 com.intellij.testFramework.TestDataPath;
|
||||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
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);
|
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")
|
@TestMetadata("externals.kt")
|
||||||
public void testExternals() throws Exception {
|
public void testExternals() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
|
||||||
@@ -39,4 +44,9 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm
|
|||||||
public void testJsExport() throws Exception {
|
public void testJsExport() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("types.kt")
|
||||||
|
public void testTypes() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxWasmJsInterop/types.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+15
@@ -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);
|
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")
|
@TestMetadata("externals.kt")
|
||||||
public void testExternals() throws Exception {
|
public void testExternals() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
|
||||||
@@ -39,4 +44,14 @@ public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWa
|
|||||||
public void testJsExport() throws Exception {
|
public void testJsExport() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Any>(refAsDataRef)) {
|
||||||
|
return wasm_ref_cast<Any>(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()
|
||||||
@@ -9,6 +9,11 @@
|
|||||||
|
|
||||||
package kotlin.wasm.internal
|
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)
|
@WasmOp(WasmOp.UNREACHABLE)
|
||||||
internal fun wasm_unreachable(): Nothing =
|
internal fun wasm_unreachable(): Nothing =
|
||||||
implementedAsIntrinsic
|
implementedAsIntrinsic
|
||||||
@@ -329,4 +334,25 @@ public external fun wasm_i64_clz(a: Long): Long
|
|||||||
public external fun wasm_i64_popcnt(a: Long): Long
|
public external fun wasm_i64_popcnt(a: Long): Long
|
||||||
|
|
||||||
@WasmOp(WasmOp.I64_CTZ)
|
@WasmOp(WasmOp.I64_CTZ)
|
||||||
public external fun wasm_i64_ctz(a: Long): Long
|
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
|
||||||
@@ -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
|
||||||
@@ -216,6 +216,12 @@ internal annotation class WasmOp(val name: String) {
|
|||||||
const val RETURN = "RETURN"
|
const val RETURN = "RETURN"
|
||||||
const val CALL = "CALL"
|
const val CALL = "CALL"
|
||||||
const val CALL_INDIRECT = "CALL_INDIRECT"
|
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 DROP = "DROP"
|
||||||
const val SELECT = "SELECT"
|
const val SELECT = "SELECT"
|
||||||
const val SELECT_TYPED = "SELECT_TYPED"
|
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_TEST = "REF_TEST"
|
||||||
const val REF_CAST = "REF_CAST"
|
const val REF_CAST = "REF_CAST"
|
||||||
const val BR_ON_CAST = "BR_ON_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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,18 @@ package kotlin.js
|
|||||||
@ExperimentalJsExport
|
@ExperimentalJsExport
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION)
|
||||||
public actual annotation class JsExport
|
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)
|
||||||
@@ -368,6 +368,23 @@ enum class WasmOp(
|
|||||||
|
|
||||||
BR_ON_CAST("br_on_cast", 0xFB_42, listOf(LABEL_IDX)),
|
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.
|
// 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)
|
GET_UNIT("call", 0x10, FUNC_IDX)
|
||||||
;
|
;
|
||||||
|
|||||||
Reference in New Issue
Block a user