[K/Wasm] Add simple TypeScript definitions generating ^KT-65009 Fixed
This commit is contained in:
@@ -353,11 +353,15 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
}
|
||||
|
||||
if (arguments.wasm) {
|
||||
val (allModules, backendContext) = compileToLoweredIr(
|
||||
val generateDts = configuration.getBoolean(JSConfigurationKeys.GENERATE_DTS)
|
||||
val generateSourceMaps = configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)
|
||||
|
||||
val (allModules, backendContext, typeScriptFragment) = compileToLoweredIr(
|
||||
depsDescriptors = module,
|
||||
phaseConfig = createPhaseConfig(wasmPhases, arguments, messageCollector),
|
||||
irFactory = IrFactoryImpl,
|
||||
exportedDeclarations = setOf(FqName("main")),
|
||||
generateTypeScriptFragment = generateDts,
|
||||
propertyLazyInitialization = arguments.irPropertyLazyInitialization,
|
||||
)
|
||||
val dceDumpNameCache = DceDumpNameCache()
|
||||
@@ -367,16 +371,15 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
|
||||
dumpDeclarationIrSizesIfNeed(arguments.irDceDumpDeclarationIrSizesToFile, allModules, dceDumpNameCache)
|
||||
|
||||
val generateSourceMaps = configuration.getBoolean(JSConfigurationKeys.SOURCE_MAP)
|
||||
|
||||
val res = compileWasm(
|
||||
allModules = allModules,
|
||||
backendContext = backendContext,
|
||||
typeScriptFragment = typeScriptFragment,
|
||||
baseFileName = outputName,
|
||||
emitNameSection = arguments.wasmDebug,
|
||||
allowIncompleteImplementations = arguments.irDce,
|
||||
generateWat = configuration.get(JSConfigurationKeys.WASM_GENERATE_WAT, false),
|
||||
generateSourceMaps = generateSourceMaps
|
||||
generateSourceMaps = generateSourceMaps,
|
||||
)
|
||||
|
||||
writeCompilationResult(
|
||||
|
||||
@@ -27,6 +27,7 @@ data class ExportedModule(
|
||||
class ExportedNamespace(
|
||||
val name: String,
|
||||
val declarations: List<ExportedDeclaration>,
|
||||
val isPrivate: Boolean = false
|
||||
) : ExportedDeclaration()
|
||||
|
||||
data class ExportedFunction(
|
||||
@@ -100,7 +101,7 @@ data class ExportedObject(
|
||||
override val members: List<ExportedDeclaration>,
|
||||
override val nestedClasses: List<ExportedClass>,
|
||||
override val ir: IrClass,
|
||||
val irGetter: IrSimpleFunction
|
||||
val irGetter: IrSimpleFunction? = null
|
||||
) : ExportedClass()
|
||||
|
||||
class ExportedParameter(
|
||||
@@ -113,6 +114,7 @@ sealed class ExportedType {
|
||||
sealed class Primitive(val typescript: kotlin.String) : ExportedType() {
|
||||
object Boolean : Primitive("boolean")
|
||||
object Number : Primitive("number")
|
||||
object BigInt : Primitive("bigint")
|
||||
object ByteArray : Primitive("Int8Array")
|
||||
object ShortArray : Primitive("Int16Array")
|
||||
object IntArray : Primitive("Int32Array")
|
||||
@@ -121,11 +123,14 @@ sealed class ExportedType {
|
||||
object String : Primitive("string")
|
||||
object Throwable : Primitive("Error")
|
||||
object Any : Primitive("any")
|
||||
object Unknown : Primitive("unknown")
|
||||
object Undefined : Primitive("undefined")
|
||||
object Unit : Primitive("void")
|
||||
object Nothing : Primitive("never")
|
||||
object UniqueSymbol : Primitive("unique symbol")
|
||||
object Unknown : Primitive("unknown") {
|
||||
override fun withNullability(nullable: kotlin.Boolean) =
|
||||
if (nullable) this else NonNullable(this)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class LiteralType<T : Any>(val value: T) : ExportedType() {
|
||||
@@ -142,6 +147,7 @@ sealed class ExportedType {
|
||||
class ClassType(val name: String, val arguments: List<ExportedType>, val ir: IrClass) : ExportedType()
|
||||
class TypeParameter(val name: String, val constraint: ExportedType? = null) : ExportedType()
|
||||
class Nullable(val baseType: ExportedType) : ExportedType()
|
||||
class NonNullable(val baseType: ExportedType) : ExportedType()
|
||||
class ErrorType(val comment: String) : ExportedType()
|
||||
class TypeOf(val name: String) : ExportedType()
|
||||
|
||||
|
||||
+16
-15
@@ -524,7 +524,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
||||
return ExportedType.ErrorType("UnknownType ${type.render()}")
|
||||
}
|
||||
|
||||
private fun exportTypeParameter(typeParameter: IrTypeParameter): ExportedType.TypeParameter {
|
||||
fun exportTypeParameter(typeParameter: IrTypeParameter): ExportedType.TypeParameter {
|
||||
val constraint = typeParameter.superTypes.asSequence()
|
||||
.filter { it != context.irBuiltIns.anyNType }
|
||||
.map {
|
||||
@@ -550,12 +550,6 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
||||
)
|
||||
}
|
||||
|
||||
private fun ExportedDeclaration.withAttributesFor(declaration: IrDeclaration): ExportedDeclaration {
|
||||
declaration.getDeprecated()?.let { attributes.add(ExportedAttribute.DeprecatedAttribute(it)) }
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private val currentlyProcessedTypes = hashSetOf<IrType>()
|
||||
|
||||
private fun exportType(type: IrType, shouldCalculateExportedSupertypeForImplicit: Boolean = true): ExportedType {
|
||||
@@ -639,13 +633,6 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
||||
.also { currentlyProcessedTypes.remove(type) }
|
||||
}
|
||||
|
||||
private fun IrDeclarationWithName.getExportedIdentifier(): String =
|
||||
with(getJsNameOrKotlinName()) {
|
||||
if (isSpecial)
|
||||
error("Cannot export special name: ${name.asString()} for declaration $fqNameWhenAvailable")
|
||||
else identifier
|
||||
}
|
||||
|
||||
private fun functionExportability(function: IrSimpleFunction): Exportability {
|
||||
if (function.isInline && function.typeParameters.any { it.isReified })
|
||||
return Exportability.Prohibited("Inline reified function")
|
||||
@@ -811,7 +798,7 @@ fun IrDeclaration.isExportedImplicitlyOrExplicitly(context: JsIrBackendContext):
|
||||
return shouldDeclarationBeExportedImplicitlyOrExplicitly(candidate, context)
|
||||
}
|
||||
|
||||
private fun DescriptorVisibility.toExportedVisibility() =
|
||||
fun DescriptorVisibility.toExportedVisibility() =
|
||||
when (this) {
|
||||
DescriptorVisibilities.PROTECTED -> ExportedVisibility.PROTECTED
|
||||
else -> ExportedVisibility.DEFAULT
|
||||
@@ -870,3 +857,17 @@ val strictModeReservedWords = setOf(
|
||||
)
|
||||
|
||||
private val allReservedWords = reservedWords + strictModeReservedWords
|
||||
|
||||
fun ExportedDeclaration.withAttributesFor(declaration: IrDeclaration): ExportedDeclaration {
|
||||
declaration.getDeprecated()?.let { attributes.add(ExportedAttribute.DeprecatedAttribute(it)) }
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun IrDeclarationWithName.getExportedIdentifier(): String =
|
||||
with(getJsNameOrKotlinName()) {
|
||||
if (isSpecial)
|
||||
error("Cannot export special name: ${name.asString()} for declaration $fqNameWhenAvailable")
|
||||
else identifier
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.isObject
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.utils.filterIsInstanceAnd
|
||||
@@ -140,7 +141,10 @@ class ExportModelToJsStatements(
|
||||
defineProperty(
|
||||
namespace,
|
||||
declaration.name,
|
||||
staticContext.getNameForStaticDeclaration(declaration.irGetter).makeRef(),
|
||||
staticContext.getNameForStaticDeclaration(
|
||||
declaration.irGetter
|
||||
?: error("Expect to have an object getter in its export model, but ${declaration.ir.fqNameWhenAvailable ?: declaration.name} doesn't have it")
|
||||
).makeRef(),
|
||||
null,
|
||||
staticContext
|
||||
).makeStmt()
|
||||
|
||||
+3
-2
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.ir.backend.js.export
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.isEs6PrimaryConstructorReplacement
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.isSyntheticPrimaryConstructor
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsAnnotations
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getFqNameWithJsNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
|
||||
@@ -25,6 +24,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.findIsInstanceAnd
|
||||
|
||||
private const val NonNullable = "NonNullable"
|
||||
private const val Nullable = "Nullable"
|
||||
private const val objects = "_objects_"
|
||||
private const val declare = "declare "
|
||||
@@ -127,7 +127,7 @@ class ExportModelToTsDeclarations {
|
||||
}
|
||||
|
||||
private fun ExportedNamespace.generateTypeScriptString(indent: String, prefix: String): String {
|
||||
return "${prefix}namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}"
|
||||
return "${prefix.takeIf { !isPrivate } ?: "declare "}namespace $name {\n" + declarations.toTypeScript("$indent ") + "$indent}"
|
||||
}
|
||||
|
||||
private fun ExportedConstructor.generateTypeScriptString(indent: String): String {
|
||||
@@ -445,6 +445,7 @@ class ExportModelToTsDeclarations {
|
||||
|
||||
is ExportedType.ErrorType -> if (isInCommentContext) comment else "any /*$comment*/"
|
||||
is ExportedType.Nullable -> "$Nullable<" + baseType.toTypeScript(indent, isInCommentContext) + ">"
|
||||
is ExportedType.NonNullable -> "$NonNullable<" + baseType.toTypeScript(indent, isInCommentContext) + ">"
|
||||
is ExportedType.InlineInterfaceType -> {
|
||||
members.joinToString(prefix = "{\n", postfix = "$indent}", separator = "") { it.toTypeScript("$indent ") + "\n" }
|
||||
}
|
||||
|
||||
@@ -365,7 +365,13 @@ class WasmSymbols(
|
||||
|
||||
val jsCode = getFunction("js", kotlinJsPackage)
|
||||
|
||||
val jsAnyType: IrType by lazy { getIrClass(FqName("kotlin.js.JsAny")).defaultType }
|
||||
val jsReferenceClass by lazy { getIrClass(FqName("kotlin.js.JsReference")) }
|
||||
|
||||
val jsAnyType: IrType by lazy { getIrType("kotlin.js.JsAny") }
|
||||
val jsBooleanType: IrType by lazy { getIrType("kotlin.js.JsBoolean") }
|
||||
val jsStringType: IrType by lazy { getIrType("kotlin.js.JsString") }
|
||||
val jsNumberType: IrType by lazy { getIrType("kotlin.js.JsNumber") }
|
||||
val jsBigIntType: IrType by lazy { getIrType("kotlin.js.JsBigInt") }
|
||||
|
||||
val newJsArray = getInternalFunction("newJsArray")
|
||||
|
||||
@@ -424,6 +430,7 @@ class WasmSymbols(
|
||||
private fun getEnumsFunction(name: String) = getFunction(name, enumsInternalPackage)
|
||||
|
||||
private fun getIrClass(fqName: FqName): IrClassSymbol = symbolTable.descriptorExtension.referenceClass(getClass(fqName))
|
||||
private fun getIrType(fqName: String): IrType = getIrClass(FqName(fqName)).defaultType
|
||||
private fun getInternalClass(name: String): IrClassSymbol = getIrClass(FqName("kotlin.wasm.internal.$name"))
|
||||
fun getKFunctionType(type: IrType, list: List<IrType>): IrType {
|
||||
return irBuiltIns.functionN(list.size).typeWith(list + type)
|
||||
|
||||
@@ -14,10 +14,14 @@ import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmModuleFragmentGenerator
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.toJsStringLiteral
|
||||
import org.jetbrains.kotlin.backend.wasm.lower.markExportedDeclarations
|
||||
import org.jetbrains.kotlin.backend.wasm.utils.SourceMapGenerator
|
||||
import org.jetbrains.kotlin.backend.wasm.export.ExportModelGenerator
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.backend.js.MainModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
|
||||
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportModelToTsDeclarations
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.ExportedModule
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.TypeScriptFragment
|
||||
import org.jetbrains.kotlin.ir.backend.js.loadIr
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
@@ -28,6 +32,7 @@ import org.jetbrains.kotlin.js.config.WasmTarget
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver
|
||||
import org.jetbrains.kotlin.js.sourceMap.SourceMap3Builder
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToBinary
|
||||
import org.jetbrains.kotlin.wasm.ir.convertors.WasmIrToText
|
||||
@@ -35,13 +40,15 @@ import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocation
|
||||
import org.jetbrains.kotlin.wasm.ir.source.location.SourceLocationMapping
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import kotlin.math.exp
|
||||
|
||||
class WasmCompilerResult(
|
||||
val wat: String?,
|
||||
val jsUninstantiatedWrapper: String?,
|
||||
val jsWrapper: String,
|
||||
val wasm: ByteArray,
|
||||
val debugInformation: DebugInformation?
|
||||
val debugInformation: DebugInformation?,
|
||||
val dts: String?
|
||||
)
|
||||
|
||||
class DebugInformation(
|
||||
@@ -49,13 +56,20 @@ class DebugInformation(
|
||||
val sourceMapForText: String?,
|
||||
)
|
||||
|
||||
data class LoweredIrWithExtraArtifacts(
|
||||
val loweredIr: List<IrModuleFragment>,
|
||||
val backendContext: WasmBackendContext,
|
||||
val typeScriptFragment: TypeScriptFragment?
|
||||
)
|
||||
|
||||
fun compileToLoweredIr(
|
||||
depsDescriptors: ModulesStructure,
|
||||
phaseConfig: PhaseConfig,
|
||||
irFactory: IrFactory,
|
||||
exportedDeclarations: Set<FqName> = emptySet(),
|
||||
generateTypeScriptFragment: Boolean,
|
||||
propertyLazyInitialization: Boolean,
|
||||
): Pair<List<IrModuleFragment>, WasmBackendContext> {
|
||||
): LoweredIrWithExtraArtifacts {
|
||||
val mainModule = depsDescriptors.mainModule
|
||||
val configuration = depsDescriptors.compilerConfiguration
|
||||
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, irLinker) = loadIr(
|
||||
@@ -90,6 +104,13 @@ fun compileToLoweredIr(
|
||||
for (file in module.files)
|
||||
markExportedDeclarations(context, file, exportedDeclarations)
|
||||
|
||||
val typeScriptFragment = runIf(generateTypeScriptFragment) {
|
||||
val exportModel = ExportModelGenerator(context).generateExport(allModules)
|
||||
val exportModelToDtsTranslator = ExportModelToTsDeclarations()
|
||||
val fragment = exportModelToDtsTranslator.generateTypeScriptFragment(ModuleKind.ES, exportModel.declarations)
|
||||
TypeScriptFragment(exportModelToDtsTranslator.generateTypeScript("", ModuleKind.ES, listOf(fragment)))
|
||||
}
|
||||
|
||||
val phaserState = PhaserState<IrModuleFragment>()
|
||||
loweringList.forEachIndexed { _, lowering ->
|
||||
allModules.forEach { module ->
|
||||
@@ -97,12 +118,13 @@ fun compileToLoweredIr(
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(allModules, context)
|
||||
return LoweredIrWithExtraArtifacts(allModules, context, typeScriptFragment)
|
||||
}
|
||||
|
||||
fun compileWasm(
|
||||
allModules: List<IrModuleFragment>,
|
||||
backendContext: WasmBackendContext,
|
||||
typeScriptFragment: TypeScriptFragment?,
|
||||
baseFileName: String,
|
||||
emitNameSection: Boolean = false,
|
||||
allowIncompleteImplementations: Boolean = false,
|
||||
@@ -160,6 +182,7 @@ fun compileWasm(
|
||||
jsWrapper = compiledWasmModule.generateAsyncWasiWrapper("./$baseFileName.wasm")
|
||||
}
|
||||
|
||||
|
||||
return WasmCompilerResult(
|
||||
wat = wat,
|
||||
jsUninstantiatedWrapper = jsUninstantiatedWrapper,
|
||||
@@ -169,6 +192,7 @@ fun compileWasm(
|
||||
sourceMapGeneratorForBinary?.generate(),
|
||||
sourceMapGeneratorForText?.generate(),
|
||||
),
|
||||
dts = typeScriptFragment?.raw
|
||||
)
|
||||
}
|
||||
|
||||
@@ -355,4 +379,8 @@ fun writeCompilationResult(
|
||||
result.debugInformation?.sourceMapForText?.let {
|
||||
File(dir, "$fileNameBase.wat.map").writeText(it)
|
||||
}
|
||||
|
||||
if (result.dts != null) {
|
||||
File(dir, "$fileNameBase.d.ts").writeText(result.dts)
|
||||
}
|
||||
}
|
||||
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.export
|
||||
|
||||
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getFqNameWithJsNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isJsExport
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.realOverrideTarget
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||
import org.jetbrains.kotlin.serialization.js.ModuleKind
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedFilter
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedMapNotNull
|
||||
|
||||
private const val NOT_EXPORTED_NAMESPACE = "not.exported"
|
||||
|
||||
class ExportModelGenerator(val context: WasmBackendContext) {
|
||||
private val excludedFromExport = setOf<IrDeclaration>(
|
||||
context.wasmSymbols.jsRelatedSymbols.jsReferenceClass.owner,
|
||||
context.wasmSymbols.jsRelatedSymbols.jsAnyType.classOrFail.owner,
|
||||
context.wasmSymbols.jsRelatedSymbols.jsNumberType.classOrFail.owner,
|
||||
context.wasmSymbols.jsRelatedSymbols.jsStringType.classOrFail.owner,
|
||||
context.wasmSymbols.jsRelatedSymbols.jsBooleanType.classOrFail.owner,
|
||||
context.wasmSymbols.jsRelatedSymbols.jsBigIntType.classOrFail.owner
|
||||
)
|
||||
|
||||
private fun collectAllTheDeclarationsToExport(modules: Iterable<IrModuleFragment>): Iterable<IrDeclaration> {
|
||||
val declarationsToExport = mutableSetOf<IrDeclaration>()
|
||||
val queue = ArrayDeque<IrDeclaration>().apply {
|
||||
modules.asSequence()
|
||||
.flatMap { it.files }
|
||||
.flatMap { it.declarations }
|
||||
.filter { it.isJsExport() }
|
||||
.forEach {
|
||||
declarationsToExport.add(it)
|
||||
addLast(it)
|
||||
}
|
||||
}
|
||||
val declarationVisitor = object : IrElementVisitorVoid {
|
||||
override fun visitFunction(declaration: IrFunction) {
|
||||
visitType(declaration.returnType)
|
||||
declaration.typeParameters.forEach(::visitTypeParameter)
|
||||
declaration.valueParameters.forEach(::visitValueParameter)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
declaration.superTypes.forEach(::visitType)
|
||||
declaration.acceptChildrenVoid(this)
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField) {
|
||||
visitType(declaration.type)
|
||||
}
|
||||
|
||||
override fun visitValueParameter(declaration: IrValueParameter) {
|
||||
visitType(declaration.type)
|
||||
}
|
||||
|
||||
override fun visitTypeParameter(declaration: IrTypeParameter) {
|
||||
declaration.superTypes.forEach(::visitType)
|
||||
}
|
||||
|
||||
private fun visitType(type: IrType) {
|
||||
if (type !is IrSimpleType) return
|
||||
val classifier = type.classifier as? IrClassSymbol ?: return
|
||||
val klass = classifier.owner
|
||||
if (!klass.isExternal || klass in excludedFromExport || klass in declarationsToExport) return
|
||||
queue.add(klass)
|
||||
declarationsToExport.add(klass)
|
||||
type.arguments.forEach { it.typeOrNull?.let(::visitType) }
|
||||
}
|
||||
}
|
||||
|
||||
while (queue.isNotEmpty()) {
|
||||
val declaration = queue.removeFirst()
|
||||
declaration.acceptVoid(declarationVisitor)
|
||||
}
|
||||
|
||||
return declarationsToExport
|
||||
}
|
||||
|
||||
fun generateExport(modules: Iterable<IrModuleFragment>): ExportedModule =
|
||||
ExportedModule(
|
||||
context.configuration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
ModuleKind.ES,
|
||||
collectAllTheDeclarationsToExport(modules).mapNotNull(::exportDeclaration)
|
||||
)
|
||||
|
||||
private fun exportDeclaration(declaration: IrDeclaration): ExportedDeclaration? {
|
||||
return when (declaration) {
|
||||
is IrSimpleFunction -> exportFunction(declaration)
|
||||
is IrClass -> exportClass(declaration)
|
||||
else -> error("Can't export declaration $declaration")
|
||||
}?.withAttributesFor(declaration)
|
||||
}
|
||||
|
||||
private fun exportFunction(function: IrSimpleFunction): ExportedFunction? =
|
||||
runIf(function.correspondingPropertySymbol == null && function.realOverrideTarget.parentClassOrNull?.symbol != context.irBuiltIns.anyClass) {
|
||||
val parentClass = function.parentClassOrNull
|
||||
ExportedFunction(
|
||||
function.getExportedIdentifier(),
|
||||
returnType = exportType(function.returnType),
|
||||
typeParameters = function.typeParameters.memoryOptimizedMap(::exportTypeParameter),
|
||||
ir = function,
|
||||
isMember = parentClass != null,
|
||||
isStatic = function.isStaticMethodOfClass,
|
||||
isProtected = function.visibility == DescriptorVisibilities.PROTECTED,
|
||||
isAbstract = parentClass != null && !parentClass.isInterface && function.modality == Modality.ABSTRACT,
|
||||
parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters)
|
||||
.memoryOptimizedMap { exportParameter(it) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration {
|
||||
assert(constructor.isPrimary) { "Can't export not-primary constructor" }
|
||||
val allValueParameters = listOfNotNull(constructor.extensionReceiverParameter) + constructor.valueParameters
|
||||
return ExportedConstructor(
|
||||
parameters = allValueParameters.memoryOptimizedMap { exportParameter(it) },
|
||||
visibility = constructor.visibility.toExportedVisibility()
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportProperty(
|
||||
property: IrProperty,
|
||||
specializeType: ExportedType? = null
|
||||
): ExportedDeclaration {
|
||||
val parentClass = property.parent as? IrClass
|
||||
val isOptional = parentClass != null &&
|
||||
property.getter?.returnType?.isNullable() == true
|
||||
|
||||
return ExportedProperty(
|
||||
name = property.getExportedIdentifier(),
|
||||
type = specializeType ?: exportType(property.getter!!.returnType),
|
||||
mutable = property.isVar,
|
||||
isMember = parentClass != null,
|
||||
isAbstract = parentClass?.isInterface == false && property.modality == Modality.ABSTRACT,
|
||||
isProtected = property.visibility == DescriptorVisibilities.PROTECTED,
|
||||
isField = parentClass?.isInterface == true,
|
||||
irGetter = property.getter,
|
||||
irSetter = property.setter,
|
||||
isOptional = isOptional,
|
||||
isStatic = (property.getter ?: property.setter)?.isStaticMethodOfClass == true,
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportParameter(parameter: IrValueParameter): ExportedParameter =
|
||||
ExportedParameter(
|
||||
parameter.name.asString(),
|
||||
exportType(parameter.type),
|
||||
parameter.defaultValue != null
|
||||
)
|
||||
|
||||
private val currentlyProcessedTypes = hashSetOf<IrType>()
|
||||
|
||||
private fun exportType(type: IrType): ExportedType {
|
||||
if (type in currentlyProcessedTypes)
|
||||
return ExportedType.Primitive.Unknown
|
||||
|
||||
if (type !is IrSimpleType)
|
||||
return ExportedType.ErrorType("NonSimpleType ${type.render()}")
|
||||
|
||||
currentlyProcessedTypes.add(type)
|
||||
|
||||
val classifier = type.classifier
|
||||
val isMarkedNullable = type.isMarkedNullable()
|
||||
val nonNullType = type.makeNotNull() as IrSimpleType
|
||||
val jsRelatedSymbols = context.wasmSymbols.jsRelatedSymbols
|
||||
|
||||
val exportedType = when {
|
||||
nonNullType.isBoolean() || nonNullType == jsRelatedSymbols.jsBooleanType -> ExportedType.Primitive.Boolean
|
||||
nonNullType.isLong() || nonNullType.isULong() || nonNullType == jsRelatedSymbols.jsBigIntType -> ExportedType.Primitive.BigInt
|
||||
nonNullType.isPrimitiveType() || nonNullType.isUByte() || nonNullType.isUShort() || nonNullType.isUInt() || nonNullType == jsRelatedSymbols.jsNumberType ->
|
||||
ExportedType.Primitive.Number
|
||||
nonNullType.isString() || nonNullType == jsRelatedSymbols.jsStringType -> ExportedType.Primitive.String
|
||||
nonNullType == jsRelatedSymbols.jsAnyType -> ExportedType.Primitive.Unknown
|
||||
nonNullType.isUnit() || nonNullType == context.wasmSymbols.voidType -> ExportedType.Primitive.Unit
|
||||
nonNullType.isFunction() -> ExportedType.Function(
|
||||
parameterTypes = nonNullType.arguments.dropLast(1).memoryOptimizedMap { exportTypeArgument(it) },
|
||||
returnType = exportTypeArgument(nonNullType.arguments.last())
|
||||
)
|
||||
|
||||
classifier is IrTypeParameterSymbol -> ExportedType.TypeParameter(classifier.owner.name.identifier)
|
||||
|
||||
classifier is IrClassSymbol -> {
|
||||
val klass = classifier.owner
|
||||
if (klass.symbol == jsRelatedSymbols.jsReferenceClass) return ExportedType.Primitive.Unknown
|
||||
|
||||
require(klass.isExternal) { "Unexpected non-external class: ${klass.fqNameWhenAvailable}" }
|
||||
|
||||
val name = "$NOT_EXPORTED_NAMESPACE.${klass.getFqNameWithJsNameWhenAvailable(shouldIncludePackage = true).asString()}"
|
||||
|
||||
when (klass.kind) {
|
||||
ClassKind.OBJECT ->
|
||||
ExportedType.TypeOf(name)
|
||||
|
||||
ClassKind.CLASS,
|
||||
ClassKind.INTERFACE ->
|
||||
ExportedType.ClassType(
|
||||
name,
|
||||
type.arguments.memoryOptimizedMap { exportTypeArgument(it) },
|
||||
klass
|
||||
)
|
||||
else -> error("Unexpected class kind ${klass.kind}")
|
||||
}
|
||||
}
|
||||
|
||||
else -> error("Unexpected classifier $classifier")
|
||||
}
|
||||
|
||||
return exportedType.withNullability(isMarkedNullable)
|
||||
.also { currentlyProcessedTypes.remove(type) }
|
||||
}
|
||||
|
||||
private fun exportTypeArgument(type: IrTypeArgument): ExportedType {
|
||||
if (type is IrTypeProjection)
|
||||
return exportType(type.type)
|
||||
|
||||
if (type is IrType)
|
||||
return exportType(type)
|
||||
|
||||
return ExportedType.ErrorType("UnknownType ${type.render()}")
|
||||
}
|
||||
|
||||
private fun exportTypeParameter(typeParameter: IrTypeParameter): ExportedType.TypeParameter {
|
||||
val constraint = typeParameter.superTypes.asSequence()
|
||||
.filter { !it.isNullable() || it.makeNotNull() != context.wasmSymbols.jsRelatedSymbols.jsAnyType }
|
||||
.map { exportType(it) }
|
||||
.filter { it !is ExportedType.ErrorType }
|
||||
.toList()
|
||||
|
||||
return ExportedType.TypeParameter(
|
||||
typeParameter.name.identifier,
|
||||
constraint.run {
|
||||
when (size) {
|
||||
0 -> null
|
||||
1 -> single()
|
||||
else -> reduce(ExportedType::IntersectionType)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun exportMemberDeclaration(declaration: IrDeclaration): ExportedDeclaration? {
|
||||
if (declaration !is IrDeclarationWithVisibility || declaration.visibility == DescriptorVisibilities.PRIVATE) return null
|
||||
return when (declaration) {
|
||||
is IrSimpleFunction -> exportFunction(declaration)
|
||||
is IrConstructor -> exportConstructor(declaration)
|
||||
is IrProperty -> exportProperty(declaration)
|
||||
else -> null
|
||||
}?.withAttributesFor(declaration)
|
||||
}
|
||||
|
||||
private fun exportClass(declaration: IrClass): ExportedDeclaration {
|
||||
val typeParameters = declaration.typeParameters.memoryOptimizedMap(::exportTypeParameter)
|
||||
|
||||
val superClass = declaration.superTypes
|
||||
.find { it != context.irBuiltIns.anyType && !it.classifierOrFail.isInterface }
|
||||
?.let(::exportType)
|
||||
?.takeIf { it !is ExportedType.ErrorType }
|
||||
|
||||
val superInterfaces = declaration.superTypes
|
||||
.filter { it != context.wasmSymbols.jsRelatedSymbols.jsAnyType && it.classifierOrFail.isInterface }
|
||||
.map(::exportType)
|
||||
.memoryOptimizedFilter { it !is ExportedType.ErrorType }
|
||||
|
||||
val name = declaration.getExportedIdentifier()
|
||||
val members = declaration.declarations.memoryOptimizedMapNotNull(::exportMemberDeclaration)
|
||||
|
||||
val exportedDeclaration = if (declaration.kind == ClassKind.OBJECT) {
|
||||
ExportedObject(
|
||||
ir = declaration,
|
||||
name = name,
|
||||
members = members,
|
||||
superClasses = listOfNotNull(superClass),
|
||||
nestedClasses = emptyList(),
|
||||
superInterfaces = superInterfaces
|
||||
)
|
||||
} else {
|
||||
ExportedRegularClass(
|
||||
name = name,
|
||||
isInterface = declaration.isInterface,
|
||||
isAbstract = declaration.modality == Modality.ABSTRACT || declaration.modality == Modality.SEALED,
|
||||
superClasses = listOfNotNull(superClass),
|
||||
superInterfaces = superInterfaces,
|
||||
typeParameters = typeParameters,
|
||||
members = members,
|
||||
nestedClasses = emptyList(),
|
||||
ir = declaration
|
||||
)
|
||||
}
|
||||
|
||||
return ExportedNamespace(
|
||||
name = "$NOT_EXPORTED_NAMESPACE${declaration.packageFqName?.asString()?.takeIf { it.isNotEmpty() }?.let { ".$it" }.orEmpty()}",
|
||||
declarations = listOf(exportedDeclaration),
|
||||
isPrivate = true
|
||||
)
|
||||
}
|
||||
|
||||
private val IrClassifierSymbol.isInterface
|
||||
get() = (owner as? IrClass)?.isInterface == true
|
||||
}
|
||||
+3
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
val KOTLIN_TO_JS_CLOSURE_ORIGIN by IrDeclarationOriginImpl
|
||||
|
||||
/**
|
||||
* Create wrappers for external and @JsExport functions when type adaptation is needed
|
||||
*/
|
||||
@@ -466,6 +468,7 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
||||
val result = context.irFactory.buildFun {
|
||||
name = Name.identifier("__callFunction_${info.signatureString}")
|
||||
returnType = info.adaptedResultType
|
||||
origin = KOTLIN_TO_JS_CLOSURE_ORIGIN
|
||||
}
|
||||
result.parent = currentParent
|
||||
result.addValueParameter {
|
||||
|
||||
+7
@@ -9,8 +9,10 @@ import org.jetbrains.kotlin.backend.wasm.ir2wasm.WasmSignature
|
||||
import org.jetbrains.kotlin.backend.wasm.ir2wasm.wasmSignature
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.BridgesConstruction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
|
||||
class WasmBridgesConstruction(context: JsCommonBackendContext) : BridgesConstruction<JsCommonBackendContext>(context) {
|
||||
override fun getFunctionSignature(function: IrSimpleFunction): WasmSignature =
|
||||
@@ -20,4 +22,9 @@ class WasmBridgesConstruction(context: JsCommonBackendContext) : BridgesConstruc
|
||||
override val shouldCastDispatchReceiver: Boolean = true
|
||||
override fun getBridgeOrigin(bridge: IrSimpleFunction): IrDeclarationOrigin =
|
||||
IrDeclarationOrigin.BRIDGE
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration.isEffectivelyExternal()) return null
|
||||
return super.transformFlat(declaration)
|
||||
}
|
||||
}
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function getResult(): not.exported.org.second.Result<string>;
|
||||
declare namespace not.exported.org.second {
|
||||
class Result<T extends NonNullable<unknown>> extends not.exported.org.second.BaseResult<T> {
|
||||
constructor();
|
||||
}
|
||||
}
|
||||
declare namespace not.exported.org.second {
|
||||
abstract class BaseResult<T extends NonNullable<unknown>> {
|
||||
constructor(foo: typeof not.exported.org.second.Foo);
|
||||
}
|
||||
}
|
||||
declare namespace not.exported.org.second {
|
||||
const Foo: {
|
||||
get bar(): number;
|
||||
get baz(): string;
|
||||
} & not.exported.Baz<string>;
|
||||
}
|
||||
declare namespace not.exported {
|
||||
interface Baz<T> extends not.exported.Bar {
|
||||
readonly baz?: T;
|
||||
readonly bar: number;
|
||||
}
|
||||
}
|
||||
declare namespace not.exported {
|
||||
interface Bar {
|
||||
readonly bar: number;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
|
||||
// FILE: first.kt
|
||||
external interface Bar {
|
||||
val bar: Int
|
||||
}
|
||||
|
||||
external interface Baz<T: JsAny?> : Bar {
|
||||
val baz: T
|
||||
}
|
||||
|
||||
// FILE: second.kt
|
||||
package org.second
|
||||
|
||||
import Bar
|
||||
import Baz
|
||||
|
||||
external object Foo : Baz<JsString> {
|
||||
override val bar: Int
|
||||
override val baz: JsString
|
||||
}
|
||||
|
||||
external abstract class BaseResult<T: JsAny>(foo: Foo)
|
||||
|
||||
external class Result<T: JsAny> : BaseResult<T>
|
||||
|
||||
fun getResultInternal(): Result<JsString> = js("({})")
|
||||
|
||||
@JsExport
|
||||
fun getResult(): Result<JsString> = getResultInternal()
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs";
|
||||
|
||||
if (JSON.stringify(main.getResult()) != "{}") throw new Error("Unexpected result")
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function simple<T>(x: T): T;
|
||||
export declare function second<A, B>(a: A, b: B): B;
|
||||
export declare function simpleWithConstraint<T extends number>(x: T): T;
|
||||
export declare function complexConstraint<A extends not.exported.Foo<bigint> & not.exported.Bar, B extends typeof not.exported.Baz>(x: A): B;
|
||||
declare namespace not.exported {
|
||||
interface Foo<T extends NonNullable<unknown>> {
|
||||
readonly foo: T;
|
||||
}
|
||||
}
|
||||
declare namespace not.exported {
|
||||
interface Bar {
|
||||
readonly bar: string;
|
||||
}
|
||||
}
|
||||
declare namespace not.exported {
|
||||
const Baz: {
|
||||
get baz(): boolean;
|
||||
};
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
|
||||
// FILE: generics.kt
|
||||
@JsExport
|
||||
fun <T: JsAny?> simple(x: T): T = x
|
||||
|
||||
@JsExport
|
||||
fun <A: JsAny?, B: JsAny?> second(a: A, b: B): B = b
|
||||
|
||||
@JsExport
|
||||
fun <T: JsNumber> simpleWithConstraint(x: T): T = x
|
||||
|
||||
external interface Foo<T: JsAny> : JsAny {
|
||||
val foo: T
|
||||
}
|
||||
|
||||
external interface Bar : JsAny {
|
||||
val bar: JsString
|
||||
}
|
||||
|
||||
external object Baz : JsAny {
|
||||
val baz: JsBoolean
|
||||
}
|
||||
|
||||
fun getBaz(x: Foo<JsBigInt>): JsAny = js("({ baz: x.foo > 0n })")
|
||||
|
||||
@JsExport
|
||||
fun <A, B> complexConstraint(x: A): B where A: Foo<JsBigInt>, A: Bar, B: Baz = getBaz(x).unsafeCast<B>()
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs";
|
||||
|
||||
if (main.simple("OK") != "OK") throw new Error("Unexpected result from `simple` function")
|
||||
if (main.second(1, "OK") != "OK") throw new Error("Unexpected result from `second` function")
|
||||
if (main.simpleWithConstraint(42) != 42) throw new Error("Unexpected result from `simpleConstraint` function")
|
||||
if (JSON.stringify(main.complexConstraint({ foo: 1n, bar: "bar" })) != "{\"baz\":true}") throw new Error("Unexpected result from `complexConstraint` function")
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function produceBoolean(): boolean;
|
||||
export declare function produceNumber(): number;
|
||||
export declare function produceBigInt(): bigint;
|
||||
export declare function produceString(): string;
|
||||
export declare function produceAny(): NonNullable<unknown>;
|
||||
export declare function consumeBoolean(x: boolean): string;
|
||||
export declare function consumeNumber(x: number): string;
|
||||
export declare function consumeBigInt(x: bigint): string;
|
||||
export declare function consumeString(x: string): string;
|
||||
export declare function consumeAny(x: NonNullable<unknown>): string;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
// FILE: jsPrimitives.kt
|
||||
|
||||
@JsExport
|
||||
fun produceBoolean(): JsBoolean = true.toJsBoolean()
|
||||
|
||||
@JsExport
|
||||
fun produceNumber(): JsNumber = Int.MAX_VALUE.toJsNumber()
|
||||
|
||||
@JsExport
|
||||
fun produceBigInt(): JsBigInt = Long.MAX_VALUE.toJsBigInt()
|
||||
|
||||
@JsExport
|
||||
fun produceString(): JsString = "OK".toJsString()
|
||||
|
||||
@JsExport
|
||||
fun produceAny(): JsAny = 42.toJsNumber()
|
||||
|
||||
@JsExport
|
||||
fun consumeBoolean(x: JsBoolean): String = x.toBoolean().toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeNumber(x: JsNumber): String = x.toInt().toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeBigInt(x: JsBigInt): String = x.toLong().toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeString(x: JsString): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeAny(x: JsAny): String = x.toString()
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs"
|
||||
|
||||
// PRODUCING
|
||||
if (!main.produceBoolean()) throw new Error("Unexpected value was returned from the `produceBoolean` function")
|
||||
if (main.produceNumber() != 2147483647) throw new Error("Unexpected value was returned from the `produceNumber` function")
|
||||
if (main.produceBigInt() != 9223372036854775807n) throw new Error("Unexpected value was returned from the `produceBigInt` function")
|
||||
if (main.produceString() != "OK") throw new Error("Unexpected value was returned from the `produceString` function")
|
||||
if (main.produceAny() != 42) throw new Error("Unexpected value was returned from the `produceAny` function")
|
||||
|
||||
// CONSUMPTION
|
||||
if (main.consumeBoolean(false) != "false") throw new Error("Unexpected value was returned from the `consumeBoolean` function")
|
||||
if (main.consumeNumber(-2147483648) != "-2147483648") throw new Error("Unexpected value was returned from the `consumeNumber` function")
|
||||
if (main.consumeBigInt(-9223372036854775808n) != "-9223372036854775808") throw new Error("Unexpected value was returned from the `consumeBigInt` function")
|
||||
if (main.consumeAny(24) != 24) throw new Error("Unexpected value was returned from the `consumeAny` function")
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function produceBoolean(): Nullable<boolean>;
|
||||
export declare function produceNumber(): Nullable<number>;
|
||||
export declare function produceBigInt(): Nullable<bigint>;
|
||||
export declare function produceString(): Nullable<string>;
|
||||
export declare function produceAny(): unknown;
|
||||
export declare function consumeBoolean(x: Nullable<boolean>): Nullable<string>;
|
||||
export declare function consumeNumber(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeBigInt(x: Nullable<bigint>): Nullable<string>;
|
||||
export declare function consumeString(x: Nullable<string>): Nullable<string>;
|
||||
export declare function consumeAny(x: unknown): Nullable<string>;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
// FILE: nullableJsPrimitives.kt
|
||||
|
||||
@JsExport
|
||||
fun produceBoolean(): JsBoolean? = true.toJsBoolean()
|
||||
|
||||
@JsExport
|
||||
fun produceNumber(): JsNumber? = Int.MAX_VALUE.toJsNumber()
|
||||
|
||||
@JsExport
|
||||
fun produceBigInt(): JsBigInt? = Long.MAX_VALUE.toJsBigInt()
|
||||
|
||||
@JsExport
|
||||
fun produceString(): JsString? = "OK".toJsString()
|
||||
|
||||
@JsExport
|
||||
fun produceAny(): JsAny? = 42.toJsNumber()
|
||||
|
||||
@JsExport
|
||||
fun consumeBoolean(x: JsBoolean?): String? = x?.toBoolean()?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeNumber(x: JsNumber?): String? = x?.toInt()?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeBigInt(x: JsBigInt?): String? = x?.toLong()?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeString(x: JsString?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeAny(x: JsAny?): String? = x?.toString()
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs"
|
||||
|
||||
// PRODUCING
|
||||
if (!main.produceBoolean()) throw new Error("Unexpected value was returned from the `produceBoolean` function")
|
||||
if (main.produceNumber() != 2147483647) throw new Error("Unexpected value was returned from the `produceNumber` function")
|
||||
if (main.produceBigInt() != 9223372036854775807n) throw new Error("Unexpected value was returned from the `produceBigInt` function")
|
||||
if (main.produceString() != "OK") throw new Error("Unexpected value was returned from the `produceString` function")
|
||||
if (main.produceAny() != 42) throw new Error("Unexpected value was returned from the `produceAny` function")
|
||||
|
||||
// CONSUMPTION
|
||||
if (main.consumeBoolean(false) != "false") throw new Error("Unexpected value was returned from the `consumeBoolean` function")
|
||||
if (main.consumeNumber(-2147483648) != "-2147483648") throw new Error("Unexpected value was returned from the `consumeNumber` function")
|
||||
if (main.consumeBigInt(-9223372036854775808n) != "-9223372036854775808") throw new Error("Unexpected value was returned from the `consumeBigInt` function")
|
||||
if (main.consumeAny(24) != 24) throw new Error("Unexpected value was returned from the `consumeAny` function")
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function produceBoolean(): Nullable<boolean>;
|
||||
export declare function produceByte(): Nullable<number>;
|
||||
export declare function produceShort(): Nullable<number>;
|
||||
export declare function produceInt(): Nullable<number>;
|
||||
export declare function produceLong(): Nullable<bigint>;
|
||||
export declare function produceChar(): Nullable<number>;
|
||||
export declare function produceString(): Nullable<string>;
|
||||
export declare function produceFunction(): Nullable<() => number>;
|
||||
export declare function consumeBoolean(x: Nullable<boolean>): Nullable<string>;
|
||||
export declare function consumeByte(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeShort(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeInt(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeLong(x: Nullable<bigint>): Nullable<string>;
|
||||
export declare function consumeChar(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeString(x: Nullable<string>): Nullable<string>;
|
||||
export declare function consumeFunction(fn: Nullable<(p0: string) => number>): Nullable<number>;
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
// FILE: nullablePrimitives.kt
|
||||
|
||||
@JsExport
|
||||
fun produceBoolean(): Boolean? = true
|
||||
|
||||
@JsExport
|
||||
fun produceByte(): Byte? = Byte.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceShort(): Short? = Short.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceInt(): Int? = Int.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceLong(): Long? = Long.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceChar(): Char? = 'a'
|
||||
|
||||
@JsExport
|
||||
fun produceString(): String? = "OK"
|
||||
|
||||
@JsExport
|
||||
fun produceFunction(): (() -> Int)? = { 42 }
|
||||
|
||||
@JsExport
|
||||
fun consumeBoolean(x: Boolean?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeByte(x: Byte?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeShort(x: Short?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeInt(x: Int?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeLong(x: Long?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeChar(x: Char?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeString(x: String?): String? = x
|
||||
|
||||
@JsExport
|
||||
fun consumeFunction(fn: ((String) -> Int)?): Int? = fn?.invoke("42")
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs"
|
||||
|
||||
// PRODUCING
|
||||
if (!main.produceBoolean()) throw new Error("Unexpected value was returned from the `produceBoolean` function")
|
||||
if (main.produceByte() != 127) throw new Error("Unexpected value was returned from the `produceByte` function")
|
||||
if (main.produceShort() != 32767) throw new Error("Unexpected value was returned from the `produceShort` function")
|
||||
if (main.produceInt() != 2147483647) throw new Error("Unexpected value was returned from the `produceInt` function")
|
||||
if (main.produceLong() != 9223372036854775807n) throw new Error("Unexpected value was returned from the `produceLong` function")
|
||||
if (String.fromCharCode(main.produceChar()) != "a") throw new Error("Unexpected value was returned from the `produceChar` function")
|
||||
if (main.produceString() != "OK") throw new Error("Unexpected value was returned from the `produceString` function")
|
||||
if (main.produceFunction()() != 42) throw new Error("Unexpected value was returned from the `produceFunction` function")
|
||||
|
||||
// CONSUMPTION
|
||||
if (main.consumeBoolean(false) != "false") throw new Error("Unexpected value was returned from the `consumeBoolean` function")
|
||||
if (main.consumeByte(-128) != "-128") throw new Error("Unexpected value was returned from the `consumeByte` function")
|
||||
if (main.consumeShort(-32768) != "-32768") throw new Error("Unexpected value was returned from the `consumeShort` function")
|
||||
if (main.consumeInt(-2147483648) != "-2147483648") throw new Error("Unexpected value was returned from the `consumeInt` function")
|
||||
if (main.consumeLong(-9223372036854775808n) != "-9223372036854775808") throw new Error("Unexpected value was returned from the `consumeLong` function")
|
||||
if (main.consumeChar("b".charCodeAt()) != "b") throw new Error("Unexpected value was returned from the `consumeChar` function")
|
||||
if (main.consumeString("🙂") != "🙂") throw new Error("Unexpected value was returned from the `consumeString` function")
|
||||
if (main.consumeFunction(parseInt) != 42) throw new Error("Unexpected value was returned from the `consumeFunction` function")
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function produceUByte(): Nullable<number>;
|
||||
export declare function produceUShort(): Nullable<number>;
|
||||
export declare function produceUInt(): Nullable<number>;
|
||||
export declare function produceULong(): Nullable<bigint>;
|
||||
export declare function produceFunction(): () => Nullable<number>;
|
||||
export declare function consumeUByte(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeUShort(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeUInt(x: Nullable<number>): Nullable<string>;
|
||||
export declare function consumeULong(x: Nullable<bigint>): Nullable<string>;
|
||||
export declare function consumeFunction(fn: (p0: string) => Nullable<number>): Nullable<number>;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
// FILE: nullableUnsigned.kt
|
||||
|
||||
@JsExport
|
||||
fun produceUByte(): UByte? = UByte.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceUShort(): UShort? = UShort.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceUInt(): UInt? = UInt.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceULong(): ULong? = ULong.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceFunction(): () -> UInt? = ::produceUInt
|
||||
|
||||
@JsExport
|
||||
fun consumeUByte(x: UByte?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeUShort(x: UShort?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeUInt(x: UInt?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeULong(x: ULong?): String? = x?.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeFunction(fn: (String) -> UInt?): UInt? = fn("42")
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs"
|
||||
|
||||
// PRODUCING
|
||||
if (main.produceUByte() != 255) throw new Error("Unexpected value was returned from the `produceUByte` function")
|
||||
if (main.produceUShort() != 65535) throw new Error("Unexpected value was returned from the `produceUShort` function")
|
||||
if (main.produceUInt() != 4294967295) throw new Error("Unexpected value was returned from the `produceUInt` function")
|
||||
if (main.produceULong() != 18446744073709551615n) throw new Error("Unexpected value was returned from the `produceULong` function")
|
||||
if (main.produceFunction()() != 4294967295) throw new Error("Unexpected value was returned from the `produceFunction` function")
|
||||
|
||||
// CONSUMPTION
|
||||
if (main.consumeUByte(-128) != "128") throw new Error("Unexpected value was returned from the `consumeUByte` function")
|
||||
if (main.consumeUShort(-32768) != "32768") throw new Error("Unexpected value was returned from the `consumeUShort` function")
|
||||
if (main.consumeUInt(-2147483648) != "2147483648") throw new Error("Unexpected value was returned from the `consumeUInt` function")
|
||||
if (main.consumeULong(-9223372036854775808n) != "9223372036854775808") throw new Error("Unexpected value was returned from the `consumeULong` function")
|
||||
if (main.consumeFunction(parseInt) != 42) throw new Error("Unexpected value was returned from the `consumeFunction` function")
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function produceBoolean(): boolean;
|
||||
export declare function produceByte(): number;
|
||||
export declare function produceShort(): number;
|
||||
export declare function produceInt(): number;
|
||||
export declare function produceLong(): bigint;
|
||||
export declare function produceChar(): number;
|
||||
export declare function produceString(): string;
|
||||
export declare function getState(): string;
|
||||
export declare function mutateState(): void;
|
||||
export declare function produceFunction(): () => number;
|
||||
export declare function consumeBoolean(x: boolean): string;
|
||||
export declare function consumeByte(x: number): string;
|
||||
export declare function consumeShort(x: number): string;
|
||||
export declare function consumeInt(x: number): string;
|
||||
export declare function consumeLong(x: bigint): string;
|
||||
export declare function consumeChar(x: number): string;
|
||||
export declare function consumeString(x: string): string;
|
||||
export declare function consumeFunction(fn: (p0: string) => number): number;
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
// FILE: primitives.kt
|
||||
|
||||
@JsExport
|
||||
fun produceBoolean(): Boolean = true
|
||||
|
||||
@JsExport
|
||||
fun produceByte(): Byte = Byte.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceShort(): Short = Short.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceInt(): Int = Int.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceLong(): Long = Long.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceChar(): Char = 'a'
|
||||
|
||||
@JsExport
|
||||
fun produceString(): String = "OK"
|
||||
|
||||
private var state = "INITIAL"
|
||||
|
||||
@JsExport
|
||||
fun getState(): String = state
|
||||
|
||||
@JsExport
|
||||
fun mutateState() {
|
||||
state = "MUTATED"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun produceFunction(): () -> Int = ::produceInt
|
||||
|
||||
@JsExport
|
||||
fun consumeBoolean(x: Boolean): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeByte(x: Byte): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeShort(x: Short): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeInt(x: Int): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeLong(x: Long): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeChar(x: Char): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeString(x: String): String = x
|
||||
|
||||
@JsExport
|
||||
fun consumeFunction(fn: (String) -> Int): Int = fn("42")
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs"
|
||||
|
||||
// PRODUCING
|
||||
if (!main.produceBoolean()) throw new Error("Unexpected value was returned from the `produceBoolean` function")
|
||||
if (main.produceByte() != 127) throw new Error("Unexpected value was returned from the `produceByte` function")
|
||||
if (main.produceShort() != 32767) throw new Error("Unexpected value was returned from the `produceShort` function")
|
||||
if (main.produceInt() != 2147483647) throw new Error("Unexpected value was returned from the `produceInt` function")
|
||||
if (main.produceLong() != 9223372036854775807n) throw new Error("Unexpected value was returned from the `produceLong` function")
|
||||
if (String.fromCharCode(main.produceChar()) != "a") throw new Error("Unexpected value was returned from the `produceChar` function")
|
||||
if (main.produceString() != "OK") throw new Error("Unexpected value was returned from the `produceString` function")
|
||||
if (main.getState() != "INITIAL") throw new Error("Unexpected value was returned from the `getState` function before the mutation")
|
||||
main.mutateState()
|
||||
if (main.getState() != "MUTATED") throw new Error("Unexpected value was returned from the `getState` function after the mutation")
|
||||
if (main.produceFunction()() != 2147483647) throw new Error("Unexpected value was returned from the `produceFunction` function")
|
||||
|
||||
// CONSUMPTION
|
||||
if (main.consumeBoolean(false) != "false") throw new Error("Unexpected value was returned from the `consumeBoolean` function")
|
||||
if (main.consumeByte(-128) != "-128") throw new Error("Unexpected value was returned from the `consumeByte` function")
|
||||
if (main.consumeShort(-32768) != "-32768") throw new Error("Unexpected value was returned from the `consumeShort` function")
|
||||
if (main.consumeInt(-2147483648) != "-2147483648") throw new Error("Unexpected value was returned from the `consumeInt` function")
|
||||
if (main.consumeLong(-9223372036854775808n) != "-9223372036854775808") throw new Error("Unexpected value was returned from the `consumeLong` function")
|
||||
if (main.consumeChar("b".charCodeAt()) != "b") throw new Error("Unexpected value was returned from the `consumeChar` function")
|
||||
if (main.consumeString("🙂") != "🙂") throw new Error("Unexpected value was returned from the `consumeString` function")
|
||||
if (main.consumeFunction(parseInt) != 42) throw new Error("Unexpected value was returned from the `consumeFunction` function")
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
type Nullable<T> = T | null | undefined
|
||||
export declare function produceUByte(): number;
|
||||
export declare function produceUShort(): number;
|
||||
export declare function produceUInt(): number;
|
||||
export declare function produceULong(): bigint;
|
||||
export declare function produceFunction(): () => number;
|
||||
export declare function consumeUByte(x: number): string;
|
||||
export declare function consumeUShort(x: number): string;
|
||||
export declare function consumeUInt(x: number): string;
|
||||
export declare function consumeULong(x: bigint): string;
|
||||
export declare function consumeFunction(fn: (p0: string) => number): number;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// TARGET_BACKEND: WASM
|
||||
// MODULE: main
|
||||
// FILE: unsigned.kt
|
||||
|
||||
@JsExport
|
||||
fun produceUByte(): UByte = UByte.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceUShort(): UShort = UShort.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceUInt(): UInt = UInt.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceULong(): ULong = ULong.MAX_VALUE
|
||||
|
||||
@JsExport
|
||||
fun produceFunction(): () -> UInt = ::produceUInt
|
||||
|
||||
@JsExport
|
||||
fun consumeUByte(x: UByte): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeUShort(x: UShort): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeUInt(x: UInt): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeULong(x: ULong): String = x.toString()
|
||||
|
||||
@JsExport
|
||||
fun consumeFunction(fn: (String) -> UInt): UInt = fn("42")
|
||||
|
||||
// FILE: entry.mjs
|
||||
|
||||
import main from "./index.mjs"
|
||||
|
||||
// PRODUCING
|
||||
if (main.produceUByte() != 255) throw new Error("Unexpected value was returned from the `produceUByte` function")
|
||||
if (main.produceUShort() != 65535) throw new Error("Unexpected value was returned from the `produceUShort` function")
|
||||
if (main.produceUInt() != 4294967295) throw new Error("Unexpected value was returned from the `produceUInt` function")
|
||||
if (main.produceULong() != 18446744073709551615n) throw new Error("Unexpected value was returned from the `produceULong` function")
|
||||
if (main.produceFunction()() != 4294967295) throw new Error("Unexpected value was returned from the `produceFunction` function")
|
||||
|
||||
// CONSUMPTION
|
||||
if (main.consumeUByte(-128) != "128") throw new Error("Unexpected value was returned from the `consumeUByte` function")
|
||||
if (main.consumeUShort(-32768) != "32768") throw new Error("Unexpected value was returned from the `consumeUShort` function")
|
||||
if (main.consumeUInt(-2147483648) != "2147483648") throw new Error("Unexpected value was returned from the `consumeUInt` function")
|
||||
if (main.consumeULong(-9223372036854775808n) != "9223372036854775808") throw new Error("Unexpected value was returned from the `consumeULong` function")
|
||||
if (main.consumeFunction(parseInt) != 42) throw new Error("Unexpected value was returned from the `consumeFunction` function")
|
||||
+4
@@ -43,4 +43,8 @@ object WasmEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
|
||||
val RUN_THIRD_PARTY_OPTIMIZER by directive(
|
||||
description = "Also run third-party optimizer (for now, only binaryen is supported) after the main compilation",
|
||||
)
|
||||
|
||||
val CHECK_TYPESCRIPT_DECLARATIONS by directive(
|
||||
description = "Check typescript declarations generated by the compiler",
|
||||
)
|
||||
}
|
||||
|
||||
Generated
+10
@@ -108,4 +108,14 @@ public class FirJsCodegenWasmJsInteropTestGenerated extends AbstractFirJsCodegen
|
||||
public void testVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/vararg.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class TypeScriptDeclarations {
|
||||
@Test
|
||||
public void testAllFilesPresentInTypeScriptDeclarations() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -108,4 +108,14 @@ public class FirJsES6CodegenWasmJsInteropTestGenerated extends AbstractFirJsES6C
|
||||
public void testVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/vararg.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class TypeScriptDeclarations {
|
||||
@Test
|
||||
public void testAllFilesPresentInTypeScriptDeclarations() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+10
@@ -108,4 +108,14 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm
|
||||
public void testVararg() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/vararg.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class TypeScriptDeclarations {
|
||||
@Test
|
||||
public void testAllFilesPresentInTypeScriptDeclarations() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.test.services.AdditionalSourceProvider
|
||||
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
|
||||
import org.jetbrains.kotlin.test.services.LibraryProvider
|
||||
import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider
|
||||
import org.jetbrains.kotlin.wasm.test.handlers.WasmDtsHandler
|
||||
|
||||
abstract class AbstractWasmBlackBoxCodegenTestBase<R : ResultingArtifact.FrontendOutput<R>, I : ResultingArtifact.BackendInput<I>, A : ResultingArtifact.Binary<A>>(
|
||||
private val targetFrontend: FrontendKind<R>,
|
||||
@@ -94,6 +95,7 @@ abstract class AbstractWasmBlackBoxCodegenTestBase<R : ResultingArtifact.Fronten
|
||||
|
||||
wasmArtifactsHandlersStep {
|
||||
useHandlers(wasmBoxTestRunner)
|
||||
useHandlers(::WasmDtsHandler)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ class WasmBackendFacade(
|
||||
override fun transform(module: TestModule, inputArtifact: BinaryArtifacts.KLib): BinaryArtifacts.Wasm? {
|
||||
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
|
||||
val generateSourceMaps = WasmEnvironmentConfigurationDirectives.GENERATE_SOURCE_MAP in testServices.moduleStructure.allDirectives
|
||||
val generateDts = WasmEnvironmentConfigurationDirectives.CHECK_TYPESCRIPT_DECLARATIONS in testServices.moduleStructure.allDirectives
|
||||
|
||||
// Enforce PL with the ERROR log level to fail any tests where PL detected any incompatibilities.
|
||||
configuration.setupPartialLinkageConfig(PartialLinkageConfig(PartialLinkageMode.ENABLE, PartialLinkageLogLevel.ERROR))
|
||||
@@ -93,12 +94,13 @@ class WasmBackendFacade(
|
||||
)
|
||||
|
||||
val testPackage = extractTestPackage(testServices)
|
||||
val (allModules, backendContext) = compileToLoweredIr(
|
||||
val (allModules, backendContext, typeScriptFragment) = compileToLoweredIr(
|
||||
depsDescriptors = moduleStructure,
|
||||
phaseConfig = phaseConfig,
|
||||
irFactory = IrFactoryImpl,
|
||||
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, "box"))),
|
||||
propertyLazyInitialization = true,
|
||||
generateTypeScriptFragment = generateDts
|
||||
)
|
||||
val generateWat = debugMode >= DebugMode.DEBUG
|
||||
val baseFileName = "index"
|
||||
@@ -106,11 +108,12 @@ class WasmBackendFacade(
|
||||
val compilerResult = compileWasm(
|
||||
allModules = allModules,
|
||||
backendContext = backendContext,
|
||||
typeScriptFragment = typeScriptFragment,
|
||||
baseFileName = baseFileName,
|
||||
emitNameSection = true,
|
||||
allowIncompleteImplementations = false,
|
||||
generateWat = generateWat,
|
||||
generateSourceMaps = generateSourceMaps
|
||||
generateSourceMaps = generateSourceMaps,
|
||||
)
|
||||
|
||||
val dceDumpNameCache = DceDumpNameCache()
|
||||
@@ -121,11 +124,12 @@ class WasmBackendFacade(
|
||||
val compilerResultWithDCE = compileWasm(
|
||||
allModules = allModules,
|
||||
backendContext = backendContext,
|
||||
typeScriptFragment = typeScriptFragment,
|
||||
baseFileName = baseFileName,
|
||||
emitNameSection = true,
|
||||
allowIncompleteImplementations = true,
|
||||
generateWat = generateWat,
|
||||
generateSourceMaps = generateSourceMaps
|
||||
generateSourceMaps = generateSourceMaps,
|
||||
)
|
||||
|
||||
return BinaryArtifacts.Wasm(
|
||||
@@ -148,7 +152,8 @@ class WasmBackendFacade(
|
||||
jsUninstantiatedWrapper = jsUninstantiatedWrapper,
|
||||
jsWrapper = jsWrapper,
|
||||
wasm = newWasm,
|
||||
debugInformation = null
|
||||
debugInformation = null,
|
||||
dts = dts
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2010-2024 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.wasm.test.handlers
|
||||
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.model.TestModule
|
||||
import org.jetbrains.kotlin.test.backend.handlers.WasmBinaryArtifactHandler
|
||||
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives
|
||||
import org.jetbrains.kotlin.test.model.BinaryArtifacts
|
||||
import org.jetbrains.kotlin.test.services.TestServices
|
||||
import org.jetbrains.kotlin.test.services.moduleStructure
|
||||
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
|
||||
|
||||
class WasmDtsHandler(testServices: TestServices) : WasmBinaryArtifactHandler(testServices) {
|
||||
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
|
||||
|
||||
override fun processModule(module: TestModule, info: BinaryArtifacts.Wasm) {
|
||||
val globalDirectives = testServices.moduleStructure.allDirectives
|
||||
if (WasmEnvironmentConfigurationDirectives.CHECK_TYPESCRIPT_DECLARATIONS !in globalDirectives) return
|
||||
|
||||
val referenceDtsFile = module.files.first().originalFile.withReplacedExtensionOrNull(".kt", ".d.ts")
|
||||
?: error("Can't find reference .d.ts file")
|
||||
|
||||
val generatedDts = info.compilerResult.dts
|
||||
?: error("Can't find generated .d.ts file")
|
||||
|
||||
KotlinTestUtils.assertEqualsToFile(referenceDtsFile, generatedDts)
|
||||
}
|
||||
}
|
||||
+58
@@ -186,4 +186,62 @@ public class FirWasmCodegenWasmJsInteropTestGenerated extends AbstractFirWasmCod
|
||||
public void testWasmImport() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/wasmImport.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class TypeScriptDeclarations {
|
||||
@Test
|
||||
public void testAllFilesPresentInTypeScriptDeclarations() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("externalDeclarations.kt")
|
||||
public void testExternalDeclarations() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/externalDeclarations.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("generics.kt")
|
||||
public void testGenerics() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/generics.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jsPrimitives.kt")
|
||||
public void testJsPrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/jsPrimitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nullableJsPrimitives.kt")
|
||||
public void testNullableJsPrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/nullableJsPrimitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nullablePrimitives.kt")
|
||||
public void testNullablePrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/nullablePrimitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nullableUnisnged.kt")
|
||||
public void testNullableUnisnged() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/nullableUnisnged.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("primitives.kt")
|
||||
public void testPrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/primitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unisnged.kt")
|
||||
public void testUnisnged() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/unisnged.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+58
@@ -186,4 +186,62 @@ public class K1WasmCodegenWasmJsInteropTestGenerated extends AbstractK1WasmCodeg
|
||||
public void testWasmImport() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/wasmImport.kt");
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class TypeScriptDeclarations {
|
||||
@Test
|
||||
public void testAllFilesPresentInTypeScriptDeclarations() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.WASM, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("externalDeclarations.kt")
|
||||
public void testExternalDeclarations() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/externalDeclarations.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("generics.kt")
|
||||
public void testGenerics() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/generics.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("jsPrimitives.kt")
|
||||
public void testJsPrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/jsPrimitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nullableJsPrimitives.kt")
|
||||
public void testNullableJsPrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/nullableJsPrimitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nullablePrimitives.kt")
|
||||
public void testNullablePrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/nullablePrimitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("nullableUnisnged.kt")
|
||||
public void testNullableUnisnged() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/nullableUnisnged.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("primitives.kt")
|
||||
public void testPrimitives() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/primitives.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("unisnged.kt")
|
||||
public void testUnisnged() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxWasmJsInterop/typeScriptDeclarations/unisnged.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user