[K/JS] Support essential Kotlin collections (List, MutableList, Set, MutableSet, Map, MutableMap) for exporting into JS
^KT-34995 Fixed ^KT-44871 Fixed
This commit is contained in:
+6
@@ -224,6 +224,12 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker(MppCheckerKind
|
||||
|| isNothingOrNullableNothing
|
||||
|| isPrimitiveArray
|
||||
|| isNonPrimitiveArray
|
||||
|| isList
|
||||
|| isMutableList
|
||||
|| isSet
|
||||
|| isMutableSet
|
||||
|| isMap
|
||||
|| isMutableMap
|
||||
|
||||
private fun validateDeclarationOnConsumableName(
|
||||
declaration: FirMemberDeclaration,
|
||||
|
||||
@@ -35,6 +35,13 @@ val ConeKotlinType.isNullableString: Boolean get() = isBuiltinType(StandardClass
|
||||
|
||||
val ConeKotlinType.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false)
|
||||
|
||||
val ConeKotlinType.isList: Boolean get() = isBuiltinType(StandardClassIds.List, false)
|
||||
val ConeKotlinType.isMutableList: Boolean get() = isBuiltinType(StandardClassIds.MutableList, false)
|
||||
val ConeKotlinType.isSet: Boolean get() = isBuiltinType(StandardClassIds.Set, false)
|
||||
val ConeKotlinType.isMutableSet: Boolean get() = isBuiltinType(StandardClassIds.MutableSet, false)
|
||||
val ConeKotlinType.isMap: Boolean get() = isBuiltinType(StandardClassIds.Map, false)
|
||||
val ConeKotlinType.isMutableMap: Boolean get() = isBuiltinType(StandardClassIds.MutableMap, false)
|
||||
|
||||
val ConeKotlinType.isUByte: Boolean get() = isBuiltinType(StandardClassIds.UByte, false)
|
||||
val ConeKotlinType.isUShort: Boolean get() = isBuiltinType(StandardClassIds.UShort, false)
|
||||
val ConeKotlinType.isUInt: Boolean get() = isBuiltinType(StandardClassIds.UInt, false)
|
||||
|
||||
@@ -349,13 +349,10 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
||||
context.symbolTable.descriptorExtension.referenceClass(context.getJsInternalClass("DoNotIntrinsify"))
|
||||
val jsFunAnnotationSymbol = context.symbolTable.descriptorExtension.referenceClass(context.getJsInternalClass("JsFun"))
|
||||
val jsNameAnnotationSymbol = context.symbolTable.descriptorExtension.referenceClass(context.getJsInternalClass("JsName"))
|
||||
val jsExportAnnotationSymbol = context.symbolTable.descriptorExtension.referenceClass(context.getJsInternalClass("JsExport"))
|
||||
val jsGeneratorAnnotationSymbol = context.symbolTable.descriptorExtension.referenceClass(context.getJsInternalClass("JsGenerator"))
|
||||
|
||||
val jsExportAnnotationSymbol by lazy(LazyThreadSafetyMode.NONE) {
|
||||
context.symbolTable.descriptorExtension.referenceClass(context.getJsInternalClass("JsExport"))
|
||||
}
|
||||
|
||||
val jsExportIgnoreAnnotationSymbol by lazy(LazyThreadSafetyMode.NONE) {
|
||||
val jsExportIgnoreAnnotationSymbol by context.lazy2 {
|
||||
jsExportAnnotationSymbol.owner
|
||||
.findDeclaration<IrClass> { it.fqNameWhenAvailable == FqName("kotlin.js.JsExport.Ignore") }
|
||||
?.symbol ?: error("can't find kotlin.js.JsExport.Ignore annotation")
|
||||
|
||||
@@ -46,6 +46,12 @@ private val collectClassDefaultConstructorsPhase = makeIrModulePhase(
|
||||
description = "Collect classes default constructors to add it to metadata on code generating phase"
|
||||
)
|
||||
|
||||
private val prepareCollectionsToExportLowering = makeIrModulePhase(
|
||||
::PrepareCollectionsToExportLowering,
|
||||
name = "PrepareCollectionsToExportLowering",
|
||||
description = "Add @JsImplicitExport to exportable collections all the declarations which we don't want to export such as `Enum.entries` or `DataClass::componentN`",
|
||||
)
|
||||
|
||||
private val preventExportOfSyntheticDeclarationsLowering = makeIrModulePhase(
|
||||
::ExcludeSyntheticDeclarationsFromExportLowering,
|
||||
name = "ExcludeSyntheticDeclarationsFromExportLowering",
|
||||
@@ -746,7 +752,7 @@ private val escapedIdentifiersLowering = makeIrModulePhase(
|
||||
private val implicitlyExportedDeclarationsMarkingLowering = makeIrModulePhase(
|
||||
::ImplicitlyExportedDeclarationsMarkingLowering,
|
||||
name = "ImplicitlyExportedDeclarationsMarkingLowering",
|
||||
description = "Add @JsImplicitExport annotation to declarations which are not exported but are used inside other exported declarations as a type"
|
||||
description = "Add @JsImplicitExport annotation to declarations which are not exported but are used inside other exported declarations as a type",
|
||||
)
|
||||
|
||||
private val cleanupLoweringPhase = makeIrModulePhase<JsIrBackendContext>(
|
||||
@@ -782,6 +788,7 @@ val mainFunctionCallWrapperLowering = makeIrModulePhase<JsIrBackendContext>(
|
||||
val loweringList = listOf<SimpleNamedCompilerPhase<JsIrBackendContext, IrModuleFragment, IrModuleFragment>>(
|
||||
scriptRemoveReceiverLowering,
|
||||
validateIrBeforeLowering,
|
||||
prepareCollectionsToExportLowering,
|
||||
preventExportOfSyntheticDeclarationsLowering,
|
||||
inventNamesForLocalClassesPhase,
|
||||
collectClassIdentifiersLowering,
|
||||
|
||||
@@ -10,9 +10,7 @@ import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
|
||||
import org.jetbrains.kotlin.backend.common.phaser.PhaserState
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.collectNativeImplementations
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
|
||||
|
||||
+9
-5
@@ -34,7 +34,10 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
||||
|
||||
fun generateExport(file: IrPackageFragment): List<ExportedDeclaration> {
|
||||
val namespaceFqName = file.packageFqName
|
||||
val exports = file.declarations.memoryOptimizedFlatMap { declaration -> listOfNotNull(exportDeclaration(declaration)) }
|
||||
val exports = file.declarations.memoryOptimizedMapNotNull { declaration ->
|
||||
declaration.takeIf { it.couldBeConvertedToExplicitExport() != true }?.let(::exportDeclaration)
|
||||
}
|
||||
|
||||
return when {
|
||||
exports.isEmpty() -> emptyList()
|
||||
!generateNamespacesForPackages || namespaceFqName.isRoot -> exports
|
||||
@@ -306,6 +309,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac
|
||||
val candidate = getExportCandidate(declaration) ?: continue
|
||||
if (isImplicitlyExportedClass && candidate !is IrClass) continue
|
||||
if (!shouldDeclarationBeExportedImplicitlyOrExplicitly(candidate, context)) continue
|
||||
if (candidate.isFakeOverride && klass.isInterface) continue
|
||||
|
||||
val processingResult = specialProcessing(candidate)
|
||||
if (processingResult != null) {
|
||||
@@ -734,15 +738,15 @@ private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, cont
|
||||
if (declaration is IrClass && declaration.kind == ClassKind.ENUM_ENTRY)
|
||||
return false
|
||||
|
||||
if (declaration.isJsExportIgnore())
|
||||
return false
|
||||
|
||||
if (context.additionalExportedDeclarationNames.contains(declaration.fqNameWhenAvailable))
|
||||
return true
|
||||
|
||||
if (context.additionalExportedDeclarations.contains(declaration))
|
||||
return true
|
||||
|
||||
if (declaration.isJsExportIgnore())
|
||||
return false
|
||||
|
||||
if (declaration is IrOverridableDeclaration<*>) {
|
||||
val overriddenNonEmpty = declaration
|
||||
.overriddenSymbols
|
||||
@@ -770,7 +774,7 @@ fun IrOverridableDeclaration<*>.isAllowedFakeOverriddenDeclaration(context: JsIr
|
||||
resolveFakeOverrideMaybeAbstract { it === this || it.parentClassOrNull?.isExported(context) != true }
|
||||
}
|
||||
|
||||
if (firstExportedRealOverride?.parentClassOrNull.isExportedInterface(context)) {
|
||||
if (firstExportedRealOverride?.parentClassOrNull.isExportedInterface(context) && firstExportedRealOverride?.isJsExportIgnore() != true) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+25
-13
@@ -9,7 +9,9 @@ import org.jetbrains.kotlin.backend.common.DeclarationTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.isJsImplicitExport
|
||||
import org.jetbrains.kotlin.ir.backend.js.lazy2
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsAnnotations
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.couldBeConvertedToExplicitExport
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
@@ -17,17 +19,18 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.isPrimitiveArray
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
||||
|
||||
class ImplicitlyExportedDeclarationsMarkingLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
|
||||
private val strictImplicitExport = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_STRICT_IMPLICIT_EXPORT)
|
||||
private val jsExportCtor by context.lazy2 { context.intrinsics.jsExportAnnotationSymbol.constructors.single() }
|
||||
private val jsImplicitExportCtor by context.lazy2 { context.intrinsics.jsImplicitExportAnnotationSymbol.constructors.single() }
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (!strictImplicitExport || !declaration.isExported(context)) return null
|
||||
if (!declaration.isExported(context)) return null
|
||||
|
||||
val implicitlyExportedDeclarations = when (declaration) {
|
||||
is IrFunction -> declaration.collectImplicitlyExportedDeclarations()
|
||||
@@ -36,7 +39,7 @@ class ImplicitlyExportedDeclarationsMarkingLowering(private val context: JsIrBac
|
||||
else -> emptySet()
|
||||
}
|
||||
|
||||
implicitlyExportedDeclarations.forEach { it.markWithJsImplicitExport() }
|
||||
implicitlyExportedDeclarations.forEach { it.markWithJsImplicitExportOrUpgrade() }
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -80,19 +83,28 @@ class ImplicitlyExportedDeclarationsMarkingLowering(private val context: JsIrBac
|
||||
classifier is IrTypeParameterSymbol -> classifier.owner.superTypes.flatMap { it.collectImplicitlyExportedDeclarations() }
|
||||
.toSet()
|
||||
|
||||
classifier is IrClassSymbol -> setOfNotNull(classifier.owner.takeIf { it.shouldBeMarkedWithImplicitExport() })
|
||||
classifier is IrClassSymbol -> setOfNotNull(classifier.owner.takeIf { it.shouldBeMarkedWithImplicitExportOrUpgraded() })
|
||||
else -> emptySet()
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrDeclaration.shouldBeMarkedWithImplicitExport(): Boolean {
|
||||
return this is IrClass && !isExternal && !isExported(context) && !isJsImplicitExport()
|
||||
private fun IrDeclaration.shouldBeMarkedWithImplicitExportOrUpgraded(): Boolean {
|
||||
return this is IrClass && !isExternal && !isExported(context)
|
||||
}
|
||||
|
||||
private fun IrDeclaration.markWithJsImplicitExport() {
|
||||
val jsImplicitExportCtor = context.intrinsics.jsImplicitExportAnnotationSymbol.constructors.single()
|
||||
annotations = annotations memoryOptimizedPlus JsIrBuilder.buildConstructorCall(jsImplicitExportCtor)
|
||||
private fun IrDeclaration.markWithJsImplicitExportOrUpgrade() {
|
||||
if (couldBeConvertedToExplicitExport() == true) {
|
||||
annotations = annotations.memoryOptimizedMap {
|
||||
if (it.isAnnotation(JsAnnotations.jsImplicitExportFqn)) {
|
||||
JsIrBuilder.buildConstructorCall(jsExportCtor)
|
||||
} else it
|
||||
}
|
||||
} else if (strictImplicitExport) {
|
||||
annotations = annotations memoryOptimizedPlus JsIrBuilder.buildConstructorCall(jsImplicitExportCtor).apply {
|
||||
putValueArgument(0, false.toIrConst(context.irBuiltIns.booleanType))
|
||||
}
|
||||
|
||||
parentClassOrNull?.takeIf { it.shouldBeMarkedWithImplicitExport() }?.markWithJsImplicitExport()
|
||||
parentClassOrNull?.takeIf { it.shouldBeMarkedWithImplicitExportOrUpgraded() }?.markWithJsImplicitExportOrUpgrade()
|
||||
}
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
import org.jetbrains.kotlin.ir.util.primaryConstructor
|
||||
import org.jetbrains.kotlin.ir.util.toIrConst
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedPlus
|
||||
|
||||
// TODO: Remove the lowering and move annotations into stdlib after solving problem with tests on KLIB
|
||||
class PrepareCollectionsToExportLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
|
||||
private val jsNameCtor by lazy(LazyThreadSafetyMode.NONE) {
|
||||
context.intrinsics.jsNameAnnotationSymbol.primaryConstructorSymbol
|
||||
}
|
||||
private val jsExportIgnoreCtor by lazy(LazyThreadSafetyMode.NONE) {
|
||||
context.intrinsics.jsExportIgnoreAnnotationSymbol.primaryConstructorSymbol
|
||||
}
|
||||
private val jsImplicitExportCtor by lazy(LazyThreadSafetyMode.NONE) {
|
||||
context.intrinsics.jsImplicitExportAnnotationSymbol.primaryConstructorSymbol
|
||||
}
|
||||
|
||||
private val IrClassSymbol.primaryConstructorSymbol: IrConstructorSymbol get() = owner.primaryConstructor!!.symbol
|
||||
|
||||
private val exportedMethodNames = setOf(
|
||||
"asJsReadonlyArrayView",
|
||||
"asJsArrayView",
|
||||
"asJsReadonlySetView",
|
||||
"asJsSetView",
|
||||
"asJsReadonlyMapView",
|
||||
"asJsMapView"
|
||||
)
|
||||
|
||||
private val exportableSymbols = setOf(
|
||||
context.ir.symbols.list,
|
||||
context.ir.symbols.mutableList,
|
||||
context.ir.symbols.set,
|
||||
context.ir.symbols.mutableSet,
|
||||
context.ir.symbols.map,
|
||||
context.ir.symbols.mutableMap,
|
||||
)
|
||||
|
||||
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||
if (declaration is IrClass && declaration.symbol in exportableSymbols) {
|
||||
declaration.addJsName()
|
||||
declaration.markWithJsImplicitExport()
|
||||
|
||||
declaration.declarations.forEach {
|
||||
if (it !is IrDeclarationWithName || it.name.toString() !in exportedMethodNames) {
|
||||
it.excludeFromJsExport()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun IrDeclaration.excludeFromJsExport() {
|
||||
if (this is IrSimpleFunction) {
|
||||
correspondingPropertySymbol?.owner?.excludeFromJsExport()
|
||||
}
|
||||
annotations = annotations memoryOptimizedPlus JsIrBuilder.buildConstructorCall(jsExportIgnoreCtor)
|
||||
}
|
||||
|
||||
private fun IrDeclarationWithName.addJsName() {
|
||||
annotations = annotations memoryOptimizedPlus JsIrBuilder.buildConstructorCall(jsNameCtor).apply {
|
||||
putValueArgument(0, "Kt${name.asString()}".toIrConst(context.irBuiltIns.stringType))
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrDeclaration.markWithJsImplicitExport() {
|
||||
annotations = annotations memoryOptimizedPlus JsIrBuilder.buildConstructorCall(jsImplicitExportCtor).apply {
|
||||
putValueArgument(0, true.toIrConst(context.irBuiltIns.booleanType))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,10 @@ object JsAnnotations {
|
||||
fun IrConstructorCall.getSingleConstStringArgument() =
|
||||
(getValueArgument(0) as IrConst<String>).value
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun IrConstructorCall.getSingleConstBooleanArgument() =
|
||||
(getValueArgument(0) as IrConst<Boolean>).value
|
||||
|
||||
fun IrAnnotationContainer.getJsModule(): String? =
|
||||
getAnnotation(JsAnnotations.jsModuleFqn)?.getSingleConstStringArgument()
|
||||
|
||||
@@ -63,6 +67,9 @@ fun IrAnnotationContainer.isJsExport(): Boolean =
|
||||
fun IrAnnotationContainer.isJsImplicitExport(): Boolean =
|
||||
hasAnnotation(JsAnnotations.jsImplicitExportFqn)
|
||||
|
||||
fun IrAnnotationContainer.couldBeConvertedToExplicitExport(): Boolean? =
|
||||
getAnnotation(JsAnnotations.jsImplicitExportFqn)?.getSingleConstBooleanArgument()
|
||||
|
||||
fun IrAnnotationContainer.isJsExportIgnore(): Boolean =
|
||||
hasAnnotation(JsAnnotations.jsExportIgnoreFqn)
|
||||
|
||||
|
||||
+24
@@ -54,3 +54,27 @@ fun foo5(<!NON_EXPORTABLE_TYPE("parameter; kotlin.Function1<kotlin.Unit, kotlin.
|
||||
@JsExport
|
||||
fun foo6(x: (A) -> A) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo7(x: List<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo8(x: MutableList<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo9(x: Set<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo10(x: MutableSet<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo11(x: Map<String, Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo12(x: MutableMap<String, Int>) {
|
||||
}
|
||||
|
||||
+24
@@ -54,3 +54,27 @@ fun foo5(<!NON_EXPORTABLE_TYPE("parameter; (Unit) -> Unit")!>x: (Unit) -> Unit<!
|
||||
@JsExport
|
||||
fun foo6(x: (A) -> A) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo7(x: List<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo8(x: MutableList<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo9(x: Set<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo10(x: MutableSet<Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo11(x: Map<String, Int>) {
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun foo12(x: MutableMap<String, Int>) {
|
||||
}
|
||||
|
||||
+6
@@ -5,11 +5,17 @@ package foo {
|
||||
@kotlin.js.JsExport public var x2: foo.C
|
||||
@kotlin.js.JsExport public fun bar(): foo.C
|
||||
@kotlin.js.JsExport public fun foo(/*0*/ x: foo.C): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo10(/*0*/ x: kotlin.collections.MutableSet<kotlin.Int>): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo11(/*0*/ x: kotlin.collections.Map<kotlin.String, kotlin.Int>): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo12(/*0*/ x: kotlin.collections.MutableMap<kotlin.String, kotlin.Int>): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo2(): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo3(/*0*/ x: kotlin.Unit): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo4(/*0*/ x: () -> kotlin.Unit): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo5(/*0*/ x: (kotlin.Unit) -> kotlin.Unit): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo6(/*0*/ x: (foo.A) -> foo.A): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo7(/*0*/ x: kotlin.collections.List<kotlin.Int>): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo8(/*0*/ x: kotlin.collections.MutableList<kotlin.Int>): kotlin.Unit
|
||||
@kotlin.js.JsExport public fun foo9(/*0*/ x: kotlin.collections.Set<kotlin.Int>): kotlin.Unit
|
||||
|
||||
@kotlin.js.JsExport public final class A {
|
||||
public constructor A(/*0*/ x: foo.C, /*1*/ y: foo.C)
|
||||
|
||||
Vendored
+1
@@ -1,3 +1,4 @@
|
||||
// MUTE_LL_FIR: KT-65218
|
||||
// WITH_REFLECT
|
||||
// FIR_DUMP
|
||||
import kotlin.reflect.*
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// MUTE_LL_FIR: KT-65218
|
||||
// WITH_REFLECT
|
||||
// FIR_DUMP
|
||||
import kotlin.reflect.*
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// FIR_IDENTICAL
|
||||
// KT-64271
|
||||
// IGNORE_BACKEND_K2: JVM_IR
|
||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
||||
// IGNORE_BACKEND_K2: JVM_IR, JS_IR, JS_IR_ES6
|
||||
// ^ Set has js specific methods
|
||||
|
||||
inline class IT(val x: Int)
|
||||
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
// WITH_STDLIB
|
||||
// IGNORE_BACKEND_K2: JS_IR NATIVE
|
||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
||||
// ^ MutableList has js specific methods
|
||||
// IGNORE_BACKEND_K2: JS_IR, JS_IR_ES6, NATIVE
|
||||
// ^ the order of fake overrides is different on K2
|
||||
|
||||
interface X {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
// IGNORE_BACKEND_K1: JS_IR, JS_IR_ES6
|
||||
// ^ Map has js specific methods
|
||||
// IGNORE_BACKEND_K2: JS_IR
|
||||
// IGNORE_BACKEND_K2: NATIVE
|
||||
|
||||
|
||||
+11
-3
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.js.resolve.diagnostics
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind.*
|
||||
@@ -14,6 +15,7 @@ import org.jetbrains.kotlin.js.common.RESERVED_KEYWORDS
|
||||
import org.jetbrains.kotlin.js.common.SPECIAL_KEYWORDS
|
||||
import org.jetbrains.kotlin.js.naming.NameSuggestion
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
@@ -190,9 +192,15 @@ class JsExportDeclarationChecker(private val includeUnsignedNumbers: Boolean) :
|
||||
KotlinBuiltIns.isString(nonNullable) ||
|
||||
(nonNullable.isPrimitiveNumberOrNullableType() && !nonNullable.isLong()) ||
|
||||
nonNullable.isNothingOrNullableNothing() ||
|
||||
KotlinBuiltIns.isArray(nonNullable) ||
|
||||
KotlinBuiltIns.isPrimitiveArray(nonNullable) ||
|
||||
(includeUnsignedNumbers && KotlinBuiltIns.isUnsignedNumber(nonNullable))
|
||||
(includeUnsignedNumbers && KotlinBuiltIns.isUnsignedNumber(nonNullable)) ||
|
||||
KotlinBuiltIns.isArray(this) ||
|
||||
KotlinBuiltIns.isPrimitiveArray(this) ||
|
||||
KotlinBuiltIns.isConstructedFromGivenClass(this, StandardNames.FqNames.list) ||
|
||||
KotlinBuiltIns.isConstructedFromGivenClass(this, StandardNames.FqNames.mutableList) ||
|
||||
KotlinBuiltIns.isConstructedFromGivenClass(this, StandardNames.FqNames.set) ||
|
||||
KotlinBuiltIns.isConstructedFromGivenClass(this, StandardNames.FqNames.mutableSet) ||
|
||||
KotlinBuiltIns.isConstructedFromGivenClass(this, StandardNames.FqNames.map) ||
|
||||
KotlinBuiltIns.isConstructedFromGivenClass(this, StandardNames.FqNames.mutableMap)
|
||||
|
||||
if (isPrimitiveExportableType) return true
|
||||
|
||||
|
||||
Generated
+32
@@ -57,6 +57,38 @@ public class IrJsES6TypeScriptExportTestGenerated extends AbstractIrJsES6TypeScr
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/collections")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class Collections {
|
||||
@Test
|
||||
public void testAllFilesPresentInCollections() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/collections"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("collections.kt")
|
||||
public void testCollections() throws Exception {
|
||||
runTest("js/js.translator/testData/typescript-export/collections/collections.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/collections-in-exported-file")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class Collections_in_exported_file {
|
||||
@Test
|
||||
public void testAllFilesPresentInCollections_in_exported_file() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/collections-in-exported-file"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("collections.kt")
|
||||
public void testCollections() throws Exception {
|
||||
runTest("js/js.translator/testData/typescript-export/collections-in-exported-file/collections.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/constructors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
Generated
+32
@@ -57,6 +57,38 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/collections")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class Collections {
|
||||
@Test
|
||||
public void testAllFilesPresentInCollections() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/collections"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("collections.kt")
|
||||
public void testCollections() throws Exception {
|
||||
runTest("js/js.translator/testData/typescript-export/collections/collections.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/collections-in-exported-file")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class Collections_in_exported_file {
|
||||
@Test
|
||||
public void testAllFilesPresentInCollections_in_exported_file() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/collections-in-exported-file"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("collections.kt")
|
||||
public void testCollections() throws Exception {
|
||||
runTest("js/js.translator/testData/typescript-export/collections-in-exported-file/collections.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/constructors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
namespace kotlin.collections {
|
||||
interface KtList<E> /* extends kotlin.collections.Collection<E> */ {
|
||||
asJsReadonlyArrayView(): ReadonlyArray<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtList": unique symbol;
|
||||
};
|
||||
}
|
||||
interface KtMap<K, V> {
|
||||
asJsReadonlyMapView(): ReadonlyMap<K, V>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMap": unique symbol;
|
||||
};
|
||||
}
|
||||
interface KtMutableList<E> extends kotlin.collections.KtList<E>/*, kotlin.collections.MutableCollection<E> */ {
|
||||
asJsArrayView(): Array<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMutableList": unique symbol;
|
||||
} & kotlin.collections.KtList<E>["__doNotUseOrImplementIt"];
|
||||
}
|
||||
interface KtSet<E> /* extends kotlin.collections.Collection<E> */ {
|
||||
asJsReadonlySetView(): ReadonlySet<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtSet": unique symbol;
|
||||
};
|
||||
}
|
||||
interface KtMutableSet<E> extends kotlin.collections.KtSet<E>/*, kotlin.collections.MutableCollection<E> */ {
|
||||
asJsSetView(): Set<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMutableSet": unique symbol;
|
||||
} & kotlin.collections.KtSet<E>["__doNotUseOrImplementIt"];
|
||||
}
|
||||
interface KtMutableMap<K, V> extends kotlin.collections.KtMap<K, V> {
|
||||
asJsMapView(): Map<K, V>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMutableMap": unique symbol;
|
||||
} & kotlin.collections.KtMap<K, V>["__doNotUseOrImplementIt"];
|
||||
}
|
||||
}
|
||||
function provideList(): kotlin.collections.KtList<number>;
|
||||
function provideMutableList(): kotlin.collections.KtMutableList<number>;
|
||||
function provideSet(): kotlin.collections.KtSet<number>;
|
||||
function provideMutableSet(): kotlin.collections.KtMutableSet<number>;
|
||||
function provideMap(): kotlin.collections.KtMap<string, number>;
|
||||
function provideMutableMap(): kotlin.collections.KtMutableMap<string, number>;
|
||||
function consumeList(list: kotlin.collections.KtList<number>): boolean;
|
||||
function consumeMutableList(list: kotlin.collections.KtMutableList<number>): boolean;
|
||||
function consumeSet(list: kotlin.collections.KtSet<number>): boolean;
|
||||
function consumeMutableSet(list: kotlin.collections.KtMutableSet<number>): boolean;
|
||||
function consumeMap(map: kotlin.collections.KtMap<string, number>): boolean;
|
||||
function consumeMutableMap(map: kotlin.collections.KtMutableMap<string, number>): boolean;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/** This file is generated by {@link :js:js.test:generateTypeScriptJsExportOnFileTests} task. DO NOT MODIFY MANUALLY */
|
||||
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// WITH_STDLIB
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
// INFER_MAIN_MODULE
|
||||
|
||||
// TODO fix statics export in DCE-driven mode
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
// MODULE: JS_TESTS
|
||||
// FILE: f1.kt
|
||||
|
||||
@file:JsExport
|
||||
|
||||
|
||||
fun provideList(): List<Int> = listOf(1, 2, 3)
|
||||
|
||||
|
||||
fun provideMutableList(): MutableList<Int> = mutableListOf(4, 5, 6)
|
||||
|
||||
|
||||
fun provideSet(): Set<Int> = setOf(1, 2, 3)
|
||||
|
||||
|
||||
fun provideMutableSet(): MutableSet<Int> = mutableSetOf(4, 5, 6)
|
||||
|
||||
|
||||
fun provideMap(): Map<String, Int> = mapOf("a" to 1, "b" to 2, "c" to 3)
|
||||
|
||||
|
||||
fun provideMutableMap(): MutableMap<String, Int> = mutableMapOf("d" to 4, "e" to 5, "f" to 6)
|
||||
|
||||
|
||||
fun consumeList(list: List<Int>) = list.toString() == "[1, 2, 3]"
|
||||
|
||||
|
||||
fun consumeMutableList(list: MutableList<Int>): Boolean {
|
||||
list.add(7)
|
||||
return list.toString() == "[4, 5, 6, 7]"
|
||||
}
|
||||
|
||||
|
||||
fun consumeSet(list: Set<Int>) = list.toString() == "[1, 2, 3]"
|
||||
|
||||
|
||||
fun consumeMutableSet(list: MutableSet<Int>): Boolean {
|
||||
list.add(7)
|
||||
return list.toString() == "[4, 5, 6, 7]"
|
||||
}
|
||||
|
||||
|
||||
fun consumeMap(map: Map<String, Int>) = map.toString() == "{a=1, b=2, c=3}"
|
||||
|
||||
|
||||
fun consumeMutableMap(map: MutableMap<String, Int>): Boolean {
|
||||
map["g"] = 7
|
||||
return map.toString() == "{d=4, e=5, f=6, g=7}"
|
||||
}
|
||||
Vendored
+213
@@ -0,0 +1,213 @@
|
||||
import provideList = JS_TESTS.provideList;
|
||||
import consumeList = JS_TESTS.consumeList;
|
||||
import provideMutableList = JS_TESTS.provideMutableList;
|
||||
import consumeMutableList = JS_TESTS.consumeMutableList;
|
||||
import provideMutableSet = JS_TESTS.provideMutableSet;
|
||||
import provideSet = JS_TESTS.provideSet;
|
||||
import consumeSet = JS_TESTS.consumeSet;
|
||||
import consumeMutableSet = JS_TESTS.consumeMutableSet;
|
||||
import provideMap = JS_TESTS.provideMap;
|
||||
import consumeMap = JS_TESTS.consumeMap;
|
||||
import provideMutableMap = JS_TESTS.provideMutableMap;
|
||||
import consumeMutableMap = JS_TESTS.consumeMutableMap;
|
||||
|
||||
function assert(condition: boolean, message: string) {
|
||||
if (!condition) {
|
||||
throw message
|
||||
}
|
||||
}
|
||||
|
||||
function assertThrow(fn: () => void, message: string) {
|
||||
try {
|
||||
fn();
|
||||
throw message;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function box(): string {
|
||||
testImmutableList()
|
||||
testImmutableSet()
|
||||
testImmutableMap()
|
||||
|
||||
testMutableList()
|
||||
testMutableSet()
|
||||
testMutableMap()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
function testImmutableList() {
|
||||
const list = provideList()
|
||||
const listReadonlyArrayView = list.asJsReadonlyArrayView()
|
||||
|
||||
assert(listReadonlyArrayView[0] == 1, "Problem with accessing of element in immutable list readonly array view")
|
||||
assert(listReadonlyArrayView["0"] == 1, "Problem with accessing of element in immutable list readonly array view by string")
|
||||
assert(listReadonlyArrayView.map(x => x + 1).join("") == "234", "Problem with immutable list readonly array view")
|
||||
assert(consumeList(list), "Problem with consumption of a Kotlin list")
|
||||
assertThrow(() => { (listReadonlyArrayView as Array<number>)[1] = 4 }, "Immutable list readonly array view have ability to mutate the list by direct set")
|
||||
assertThrow(() => { (listReadonlyArrayView as Array<number>).push(4) }, "Immutable list readonly array view have ability to mutate the list by 'push'")
|
||||
assertThrow(() => { (listReadonlyArrayView as Array<number>).pop() }, "Immutable list readonly array view have ability to mutate the list by 'pop'")
|
||||
// @ts-expect-error
|
||||
assertThrow(() => { listReadonlyArrayView["foo"] }, "Immutable list getting a random index from its readonly array view")
|
||||
}
|
||||
|
||||
function testMutableList() {
|
||||
const mutableList = provideMutableList()
|
||||
const mutableListReadonlyArrayView = mutableList.asJsReadonlyArrayView()
|
||||
|
||||
assert(mutableListReadonlyArrayView[0] == 4, "Problem with accessing of element in mutable list readonly array view")
|
||||
assert(mutableListReadonlyArrayView["0"] == 4, "Problem with accessing of element in immutable list readonly array view by string")
|
||||
assert(mutableListReadonlyArrayView.map(x => x + 1).join("") == "567", "Problem with mutable list readonly array view")
|
||||
assert(!consumeList(mutableList), "Problem with consumption of a Kotlin mutable list as a list")
|
||||
assert(consumeMutableList(mutableList), "Problem with consumption of a Kotlin mutable list as a mutable list")
|
||||
assert(mutableListReadonlyArrayView.map(x => x + 1).join("") == "5678", "Problem with mutable list readonly array view after original list is mutated")
|
||||
assertThrow(() => { (mutableListReadonlyArrayView as Array<number>)[1] = 4 }, "Mutable list readonly array view have ability to mutate the list by direct set")
|
||||
assertThrow(() => { (mutableListReadonlyArrayView as Array<number>).push(4) }, "Mutable list readonly array view have ability to mutate the list by 'push'")
|
||||
assertThrow(() => { (mutableListReadonlyArrayView as Array<number>).pop() }, "Mutable list readonly array view have ability to mutate the list by 'pop'")
|
||||
// @ts-expect-error
|
||||
assertThrow(() => { mutableListReadonlyArrayView["foo"] }, "Immutable list getting a random index from its readonly array view")
|
||||
|
||||
const mutableListArrayView = mutableList.asJsArrayView()
|
||||
mutableListArrayView.pop()
|
||||
|
||||
assert(mutableListArrayView[0] == 4, "Problem with accessing of element in mutable list array view")
|
||||
assert(mutableListArrayView["0"] == 4, "Problem with accessing of element in mutable list array view by string")
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "567", "Problem with mutable list array view")
|
||||
assert(consumeMutableList(mutableList), "Problem with consumption of a Kotlin mutable list as a mutable list")
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "5678", "Problem with mutable list array view after original list is mutated")
|
||||
// @ts-expect-error
|
||||
assertThrow(() => { mutableListArrayView["foo"] = 4 }, "Mutable list setting a random index in its array view")
|
||||
|
||||
mutableListArrayView.shift()
|
||||
mutableListArrayView.unshift(9)
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "10678", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView.sort()
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "67810", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView.push(3)
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "678104", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView[3] = 4
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "67854", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView["3"] = 6
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "67874", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView.length = 3
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "678", "Problem with mutable list array view after the view is mutated after size decreased")
|
||||
assert(mutableListReadonlyArrayView.map(x => x + 1).join("") == "678", "Problem with mutable list readonly array view after size decreased")
|
||||
|
||||
assertThrow(() => { mutableListArrayView.length = 5 }, "Mutable list view size increasing works, but should not")
|
||||
}
|
||||
|
||||
function testImmutableSet() {
|
||||
const set = provideSet()
|
||||
const setReadonlyView = set.asJsReadonlySetView();
|
||||
|
||||
assert(setReadonlyView.has(1), "Problem with accessing element of readonly view")
|
||||
assert(joinSetOrMap(setReadonlyView) == "123", "Problem with readonly view iterator")
|
||||
assert(consumeSet(set), "Problem with consumption of a Kotlin set")
|
||||
assertThrow(() => { (setReadonlyView as Set<number>).add(4) }, "Set readonly view have ability to mutate the set by 'add'")
|
||||
assertThrow(() => { (setReadonlyView as Set<number>).delete(4) }, "Set readonly view have ability to mutate the set by 'delete'")
|
||||
assertThrow(() => { (setReadonlyView as Set<number>).clear() }, "Set readonly view have ability to mutate the set by 'clear'")
|
||||
}
|
||||
|
||||
function testMutableSet() {
|
||||
const mutableSet = provideMutableSet()
|
||||
const mutableSetReadonlyView = mutableSet.asJsReadonlySetView()
|
||||
|
||||
assert(mutableSetReadonlyView.has(4), "Problem with accessing element of mutable set readonly view")
|
||||
assert(joinSetOrMap(mutableSetReadonlyView) == "456", "Problem with mutable set readonly view iterator")
|
||||
assert(!consumeSet(mutableSet), "Problem with consumption of a Kotlin mutable set as a set")
|
||||
assert(consumeMutableSet(mutableSet), "Problem with consumption of a Kotlin mutable set as a mutable set")
|
||||
assert(joinSetOrMap(mutableSetReadonlyView) == "4567", "Problem with mutable set readonly view after original set is mutated")
|
||||
assertThrow(() => { (mutableSetReadonlyView as Set<number>).add(4) }, "Mutable set readonly view have ability to mutate the set by 'add'")
|
||||
assertThrow(() => { (mutableSetReadonlyView as Set<number>).delete(4) }, "Mutable set readonly view have ability to mutate the set by 'delete'")
|
||||
assertThrow(() => { (mutableSetReadonlyView as Set<number>).clear() }, "Mutable set readonly view have ability to mutate the set by 'clear'")
|
||||
|
||||
const mutableSetView = mutableSet.asJsSetView()
|
||||
|
||||
mutableSetView.delete(7)
|
||||
|
||||
assert(mutableSetView.has(4), "Problem with accessing element of mutable set view")
|
||||
assert(joinSetOrMap(mutableSetView) == "456", "Problem with mutable set view")
|
||||
assert(consumeMutableSet(mutableSet), "Problem with consumption of a Kotlin mutable set as a mutable set")
|
||||
assert(joinSetOrMap(mutableSetView) == "4567", "Problem with mutable set view after original set is mutated")
|
||||
|
||||
mutableSetView.add(8)
|
||||
|
||||
assert(joinSetOrMap(mutableSetView) == "45678", "Problem with mutable set view after the view is mutated")
|
||||
|
||||
mutableSetView.clear()
|
||||
|
||||
assert(joinSetOrMap(mutableSetView) == "", "Problem with mutable set view after the view is mutated")
|
||||
}
|
||||
|
||||
function testImmutableMap() {
|
||||
const map = provideMap()
|
||||
const mapReadonlyView = map.asJsReadonlyMapView()
|
||||
|
||||
assert(mapReadonlyView.has("a"), "Problem with accessing element in map readonly view")
|
||||
assert(mapReadonlyView.get("a") == 1, "Problem with accessing element in map readonly view")
|
||||
assert(joinSetOrMap(mapReadonlyView) == "[a:1][b:2][c:3]", "Problem with map readonly view iterator")
|
||||
assert(consumeMap(map), "Problem with consumption of a Kotlin map")
|
||||
assertThrow(() => { (mapReadonlyView as Map<string, number>).set("d", 4) }, "Map readonly view have ability to mutate the map by 'set'")
|
||||
assertThrow(() => { (mapReadonlyView as Map<string, number>).delete("a") }, "Map readonly view have ability to mutate the map by 'delete'")
|
||||
assertThrow(() => { (mapReadonlyView as Map<string, number>).clear() }, "Map readonly view have ability to mutate the map by 'clear'")
|
||||
}
|
||||
|
||||
function testMutableMap() {
|
||||
const mutableMap = provideMutableMap()
|
||||
const mutableMapReadonlyView = mutableMap.asJsReadonlyMapView()
|
||||
|
||||
assert(mutableMapReadonlyView.has("d"), "Problem with accessing element in mutable map readonly view")
|
||||
assert(mutableMapReadonlyView.get("d") == 4, "Problem with accessing element in mutable map readonly view")
|
||||
assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6]", "Problem with mutable map readonly view")
|
||||
assert(!consumeMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a map")
|
||||
assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
|
||||
assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map readonly view after original map is mutated")
|
||||
assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).set("d", 4) }, "Mutable map readonly view have ability to mutate the map by 'set'")
|
||||
assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).delete("a") }, "Mutable map readonly view have ability to mutate the map by 'delete'")
|
||||
assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).clear() }, "Mutable map readonly view have ability to mutate the map by 'clear'")
|
||||
|
||||
const mutableMapView = mutableMap.asJsMapView()
|
||||
|
||||
mutableMapView.delete("g")
|
||||
|
||||
assert(mutableMapView.has("d"), "Problem with accessing element in mutable map view")
|
||||
assert(mutableMapView.get("d") == 4, "Problem with accessing element in mutable map view")
|
||||
assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6]", "Problem with mutable map view")
|
||||
assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
|
||||
assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map view after original map is mutated")
|
||||
|
||||
mutableMapView.set("h", 8)
|
||||
|
||||
assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7][h:8]", "Problem with mutable map view after the view is mutated")
|
||||
|
||||
mutableMapView.clear()
|
||||
|
||||
assert(joinSetOrMap(mutableMapView) == "", "Problem with mutable map view after the view is mutated")
|
||||
}
|
||||
|
||||
function joinSetOrMap(setOrMap: ReadonlySet<number> | ReadonlyMap<string, number>): string {
|
||||
let result = ""
|
||||
|
||||
if (setOrMap instanceof Set) {
|
||||
setOrMap.forEach((a: any) => {
|
||||
result += a
|
||||
});
|
||||
} else {
|
||||
setOrMap.forEach((key: any, value: any) => {
|
||||
result += `[${key}:${value}]`
|
||||
});
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"include": [ "./*" ],
|
||||
"extends": "../common.tsconfig.json"
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
namespace kotlin.collections {
|
||||
interface KtList<E> /* extends kotlin.collections.Collection<E> */ {
|
||||
asJsReadonlyArrayView(): ReadonlyArray<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtList": unique symbol;
|
||||
};
|
||||
}
|
||||
interface KtMap<K, V> {
|
||||
asJsReadonlyMapView(): ReadonlyMap<K, V>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMap": unique symbol;
|
||||
};
|
||||
}
|
||||
interface KtMutableList<E> extends kotlin.collections.KtList<E>/*, kotlin.collections.MutableCollection<E> */ {
|
||||
asJsArrayView(): Array<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMutableList": unique symbol;
|
||||
} & kotlin.collections.KtList<E>["__doNotUseOrImplementIt"];
|
||||
}
|
||||
interface KtSet<E> /* extends kotlin.collections.Collection<E> */ {
|
||||
asJsReadonlySetView(): ReadonlySet<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtSet": unique symbol;
|
||||
};
|
||||
}
|
||||
interface KtMutableSet<E> extends kotlin.collections.KtSet<E>/*, kotlin.collections.MutableCollection<E> */ {
|
||||
asJsSetView(): Set<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMutableSet": unique symbol;
|
||||
} & kotlin.collections.KtSet<E>["__doNotUseOrImplementIt"];
|
||||
}
|
||||
interface KtMutableMap<K, V> extends kotlin.collections.KtMap<K, V> {
|
||||
asJsMapView(): Map<K, V>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtMutableMap": unique symbol;
|
||||
} & kotlin.collections.KtMap<K, V>["__doNotUseOrImplementIt"];
|
||||
}
|
||||
}
|
||||
function provideList(): kotlin.collections.KtList<number>;
|
||||
function provideMutableList(): kotlin.collections.KtMutableList<number>;
|
||||
function provideSet(): kotlin.collections.KtSet<number>;
|
||||
function provideMutableSet(): kotlin.collections.KtMutableSet<number>;
|
||||
function provideMap(): kotlin.collections.KtMap<string, number>;
|
||||
function provideMutableMap(): kotlin.collections.KtMutableMap<string, number>;
|
||||
function consumeList(list: kotlin.collections.KtList<number>): boolean;
|
||||
function consumeMutableList(list: kotlin.collections.KtMutableList<number>): boolean;
|
||||
function consumeSet(list: kotlin.collections.KtSet<number>): boolean;
|
||||
function consumeMutableSet(list: kotlin.collections.KtMutableSet<number>): boolean;
|
||||
function consumeMap(map: kotlin.collections.KtMap<string, number>): boolean;
|
||||
function consumeMutableMap(map: kotlin.collections.KtMutableMap<string, number>): boolean;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// WITH_STDLIB
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
// INFER_MAIN_MODULE
|
||||
|
||||
// TODO fix statics export in DCE-driven mode
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
// MODULE: JS_TESTS
|
||||
// FILE: f1.kt
|
||||
|
||||
@JsExport
|
||||
fun provideList(): List<Int> = listOf(1, 2, 3)
|
||||
|
||||
@JsExport
|
||||
fun provideMutableList(): MutableList<Int> = mutableListOf(4, 5, 6)
|
||||
|
||||
@JsExport
|
||||
fun provideSet(): Set<Int> = setOf(1, 2, 3)
|
||||
|
||||
@JsExport
|
||||
fun provideMutableSet(): MutableSet<Int> = mutableSetOf(4, 5, 6)
|
||||
|
||||
@JsExport
|
||||
fun provideMap(): Map<String, Int> = mapOf("a" to 1, "b" to 2, "c" to 3)
|
||||
|
||||
@JsExport
|
||||
fun provideMutableMap(): MutableMap<String, Int> = mutableMapOf("d" to 4, "e" to 5, "f" to 6)
|
||||
|
||||
@JsExport
|
||||
fun consumeList(list: List<Int>) = list.toString() == "[1, 2, 3]"
|
||||
|
||||
@JsExport
|
||||
fun consumeMutableList(list: MutableList<Int>): Boolean {
|
||||
list.add(7)
|
||||
return list.toString() == "[4, 5, 6, 7]"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun consumeSet(list: Set<Int>) = list.toString() == "[1, 2, 3]"
|
||||
|
||||
@JsExport
|
||||
fun consumeMutableSet(list: MutableSet<Int>): Boolean {
|
||||
list.add(7)
|
||||
return list.toString() == "[4, 5, 6, 7]"
|
||||
}
|
||||
|
||||
@JsExport
|
||||
fun consumeMap(map: Map<String, Int>) = map.toString() == "{a=1, b=2, c=3}"
|
||||
|
||||
@JsExport
|
||||
fun consumeMutableMap(map: MutableMap<String, Int>): Boolean {
|
||||
map["g"] = 7
|
||||
return map.toString() == "{d=4, e=5, f=6, g=7}"
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
import provideList = JS_TESTS.provideList;
|
||||
import consumeList = JS_TESTS.consumeList;
|
||||
import provideMutableList = JS_TESTS.provideMutableList;
|
||||
import consumeMutableList = JS_TESTS.consumeMutableList;
|
||||
import provideMutableSet = JS_TESTS.provideMutableSet;
|
||||
import provideSet = JS_TESTS.provideSet;
|
||||
import consumeSet = JS_TESTS.consumeSet;
|
||||
import consumeMutableSet = JS_TESTS.consumeMutableSet;
|
||||
import provideMap = JS_TESTS.provideMap;
|
||||
import consumeMap = JS_TESTS.consumeMap;
|
||||
import provideMutableMap = JS_TESTS.provideMutableMap;
|
||||
import consumeMutableMap = JS_TESTS.consumeMutableMap;
|
||||
|
||||
function assert(condition: boolean, message: string) {
|
||||
if (!condition) {
|
||||
throw message
|
||||
}
|
||||
}
|
||||
|
||||
function assertThrow(fn: () => void, message: string) {
|
||||
try {
|
||||
fn();
|
||||
throw message;
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function box(): string {
|
||||
testImmutableList()
|
||||
testImmutableSet()
|
||||
testImmutableMap()
|
||||
|
||||
testMutableList()
|
||||
testMutableSet()
|
||||
testMutableMap()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
function testImmutableList() {
|
||||
const list = provideList()
|
||||
const listReadonlyArrayView = list.asJsReadonlyArrayView()
|
||||
|
||||
assert(listReadonlyArrayView[0] == 1, "Problem with accessing of element in immutable list readonly array view")
|
||||
assert(listReadonlyArrayView["0"] == 1, "Problem with accessing of element in immutable list readonly array view by string")
|
||||
assert(listReadonlyArrayView.map(x => x + 1).join("") == "234", "Problem with immutable list readonly array view")
|
||||
assert(consumeList(list), "Problem with consumption of a Kotlin list")
|
||||
assertThrow(() => { (listReadonlyArrayView as Array<number>)[1] = 4 }, "Immutable list readonly array view have ability to mutate the list by direct set")
|
||||
assertThrow(() => { (listReadonlyArrayView as Array<number>).push(4) }, "Immutable list readonly array view have ability to mutate the list by 'push'")
|
||||
assertThrow(() => { (listReadonlyArrayView as Array<number>).pop() }, "Immutable list readonly array view have ability to mutate the list by 'pop'")
|
||||
// @ts-expect-error
|
||||
assertThrow(() => { listReadonlyArrayView["foo"] }, "Immutable list getting a random index from its readonly array view")
|
||||
}
|
||||
|
||||
function testMutableList() {
|
||||
const mutableList = provideMutableList()
|
||||
const mutableListReadonlyArrayView = mutableList.asJsReadonlyArrayView()
|
||||
|
||||
assert(mutableListReadonlyArrayView[0] == 4, "Problem with accessing of element in mutable list readonly array view")
|
||||
assert(mutableListReadonlyArrayView["0"] == 4, "Problem with accessing of element in immutable list readonly array view by string")
|
||||
assert(mutableListReadonlyArrayView.map(x => x + 1).join("") == "567", "Problem with mutable list readonly array view")
|
||||
assert(!consumeList(mutableList), "Problem with consumption of a Kotlin mutable list as a list")
|
||||
assert(consumeMutableList(mutableList), "Problem with consumption of a Kotlin mutable list as a mutable list")
|
||||
assert(mutableListReadonlyArrayView.map(x => x + 1).join("") == "5678", "Problem with mutable list readonly array view after original list is mutated")
|
||||
assertThrow(() => { (mutableListReadonlyArrayView as Array<number>)[1] = 4 }, "Mutable list readonly array view have ability to mutate the list by direct set")
|
||||
assertThrow(() => { (mutableListReadonlyArrayView as Array<number>).push(4) }, "Mutable list readonly array view have ability to mutate the list by 'push'")
|
||||
assertThrow(() => { (mutableListReadonlyArrayView as Array<number>).pop() }, "Mutable list readonly array view have ability to mutate the list by 'pop'")
|
||||
// @ts-expect-error
|
||||
assertThrow(() => { mutableListReadonlyArrayView["foo"] }, "Immutable list getting a random index from its readonly array view")
|
||||
|
||||
const mutableListArrayView = mutableList.asJsArrayView()
|
||||
mutableListArrayView.pop()
|
||||
|
||||
assert(mutableListArrayView[0] == 4, "Problem with accessing of element in mutable list array view")
|
||||
assert(mutableListArrayView["0"] == 4, "Problem with accessing of element in mutable list array view by string")
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "567", "Problem with mutable list array view")
|
||||
assert(consumeMutableList(mutableList), "Problem with consumption of a Kotlin mutable list as a mutable list")
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "5678", "Problem with mutable list array view after original list is mutated")
|
||||
// @ts-expect-error
|
||||
assertThrow(() => { mutableListArrayView["foo"] = 4 }, "Mutable list setting a random index in its array view")
|
||||
|
||||
mutableListArrayView.shift()
|
||||
mutableListArrayView.unshift(9)
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "10678", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView.sort()
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "67810", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView.push(3)
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "678104", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView[3] = 4
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "67854", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView["3"] = 6
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "67874", "Problem with mutable list array view after the view is mutated")
|
||||
|
||||
mutableListArrayView.length = 3
|
||||
|
||||
assert(mutableListArrayView.map(x => x + 1).join("") == "678", "Problem with mutable list array view after the view is mutated after size decreased")
|
||||
assert(mutableListReadonlyArrayView.map(x => x + 1).join("") == "678", "Problem with mutable list readonly array view after size decreased")
|
||||
|
||||
assertThrow(() => { mutableListArrayView.length = 5 }, "Mutable list view size increasing works, but should not")
|
||||
}
|
||||
|
||||
function testImmutableSet() {
|
||||
const set = provideSet()
|
||||
const setReadonlyView = set.asJsReadonlySetView();
|
||||
|
||||
assert(setReadonlyView.has(1), "Problem with accessing element of readonly view")
|
||||
assert(joinSetOrMap(setReadonlyView) == "123", "Problem with readonly view iterator")
|
||||
assert(consumeSet(set), "Problem with consumption of a Kotlin set")
|
||||
assertThrow(() => { (setReadonlyView as Set<number>).add(4) }, "Set readonly view have ability to mutate the set by 'add'")
|
||||
assertThrow(() => { (setReadonlyView as Set<number>).delete(4) }, "Set readonly view have ability to mutate the set by 'delete'")
|
||||
assertThrow(() => { (setReadonlyView as Set<number>).clear() }, "Set readonly view have ability to mutate the set by 'clear'")
|
||||
}
|
||||
|
||||
function testMutableSet() {
|
||||
const mutableSet = provideMutableSet()
|
||||
const mutableSetReadonlyView = mutableSet.asJsReadonlySetView()
|
||||
|
||||
assert(mutableSetReadonlyView.has(4), "Problem with accessing element of mutable set readonly view")
|
||||
assert(joinSetOrMap(mutableSetReadonlyView) == "456", "Problem with mutable set readonly view iterator")
|
||||
assert(!consumeSet(mutableSet), "Problem with consumption of a Kotlin mutable set as a set")
|
||||
assert(consumeMutableSet(mutableSet), "Problem with consumption of a Kotlin mutable set as a mutable set")
|
||||
assert(joinSetOrMap(mutableSetReadonlyView) == "4567", "Problem with mutable set readonly view after original set is mutated")
|
||||
assertThrow(() => { (mutableSetReadonlyView as Set<number>).add(4) }, "Mutable set readonly view have ability to mutate the set by 'add'")
|
||||
assertThrow(() => { (mutableSetReadonlyView as Set<number>).delete(4) }, "Mutable set readonly view have ability to mutate the set by 'delete'")
|
||||
assertThrow(() => { (mutableSetReadonlyView as Set<number>).clear() }, "Mutable set readonly view have ability to mutate the set by 'clear'")
|
||||
|
||||
const mutableSetView = mutableSet.asJsSetView()
|
||||
|
||||
mutableSetView.delete(7)
|
||||
|
||||
assert(mutableSetView.has(4), "Problem with accessing element of mutable set view")
|
||||
assert(joinSetOrMap(mutableSetView) == "456", "Problem with mutable set view")
|
||||
assert(consumeMutableSet(mutableSet), "Problem with consumption of a Kotlin mutable set as a mutable set")
|
||||
assert(joinSetOrMap(mutableSetView) == "4567", "Problem with mutable set view after original set is mutated")
|
||||
|
||||
mutableSetView.add(8)
|
||||
|
||||
assert(joinSetOrMap(mutableSetView) == "45678", "Problem with mutable set view after the view is mutated")
|
||||
|
||||
mutableSetView.clear()
|
||||
|
||||
assert(joinSetOrMap(mutableSetView) == "", "Problem with mutable set view after the view is mutated")
|
||||
}
|
||||
|
||||
function testImmutableMap() {
|
||||
const map = provideMap()
|
||||
const mapReadonlyView = map.asJsReadonlyMapView()
|
||||
|
||||
assert(mapReadonlyView.has("a"), "Problem with accessing element in map readonly view")
|
||||
assert(mapReadonlyView.get("a") == 1, "Problem with accessing element in map readonly view")
|
||||
assert(joinSetOrMap(mapReadonlyView) == "[a:1][b:2][c:3]", "Problem with map readonly view iterator")
|
||||
assert(consumeMap(map), "Problem with consumption of a Kotlin map")
|
||||
assertThrow(() => { (mapReadonlyView as Map<string, number>).set("d", 4) }, "Map readonly view have ability to mutate the map by 'set'")
|
||||
assertThrow(() => { (mapReadonlyView as Map<string, number>).delete("a") }, "Map readonly view have ability to mutate the map by 'delete'")
|
||||
assertThrow(() => { (mapReadonlyView as Map<string, number>).clear() }, "Map readonly view have ability to mutate the map by 'clear'")
|
||||
}
|
||||
|
||||
function testMutableMap() {
|
||||
const mutableMap = provideMutableMap()
|
||||
const mutableMapReadonlyView = mutableMap.asJsReadonlyMapView()
|
||||
|
||||
assert(mutableMapReadonlyView.has("d"), "Problem with accessing element in mutable map readonly view")
|
||||
assert(mutableMapReadonlyView.get("d") == 4, "Problem with accessing element in mutable map readonly view")
|
||||
assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6]", "Problem with mutable map readonly view")
|
||||
assert(!consumeMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a map")
|
||||
assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
|
||||
assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map readonly view after original map is mutated")
|
||||
assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).set("d", 4) }, "Mutable map readonly view have ability to mutate the map by 'set'")
|
||||
assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).delete("a") }, "Mutable map readonly view have ability to mutate the map by 'delete'")
|
||||
assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).clear() }, "Mutable map readonly view have ability to mutate the map by 'clear'")
|
||||
|
||||
const mutableMapView = mutableMap.asJsMapView()
|
||||
|
||||
mutableMapView.delete("g")
|
||||
|
||||
assert(mutableMapView.has("d"), "Problem with accessing element in mutable map view")
|
||||
assert(mutableMapView.get("d") == 4, "Problem with accessing element in mutable map view")
|
||||
assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6]", "Problem with mutable map view")
|
||||
assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
|
||||
assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map view after original map is mutated")
|
||||
|
||||
mutableMapView.set("h", 8)
|
||||
|
||||
assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7][h:8]", "Problem with mutable map view after the view is mutated")
|
||||
|
||||
mutableMapView.clear()
|
||||
|
||||
assert(joinSetOrMap(mutableMapView) == "", "Problem with mutable map view after the view is mutated")
|
||||
}
|
||||
|
||||
function joinSetOrMap(setOrMap: ReadonlySet<number> | ReadonlyMap<string, number>): string {
|
||||
let result = ""
|
||||
|
||||
if (setOrMap instanceof Set) {
|
||||
setOrMap.forEach((a: any) => {
|
||||
result += a
|
||||
});
|
||||
} else {
|
||||
setOrMap.forEach((key: any, value: any) => {
|
||||
result += `[${key}:${value}]`
|
||||
});
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"include": [ "./*" ],
|
||||
"extends": "../common.tsconfig.json"
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"strict": true,
|
||||
"newLine": "lf"
|
||||
"newLine": "lf",
|
||||
"lib": ["es2015", "dom"]
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -1,5 +1,13 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
namespace kotlin.collections {
|
||||
interface KtList<E> /* extends kotlin.collections.Collection<E> */ {
|
||||
asJsReadonlyArrayView(): ReadonlyArray<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtList": unique symbol;
|
||||
};
|
||||
}
|
||||
}
|
||||
namespace foo {
|
||||
interface SomeExternalInterface {
|
||||
}
|
||||
@@ -18,8 +26,8 @@ declare namespace JS_TESTS {
|
||||
function genericWithMultipleConstraints<T extends unknown/* kotlin.Comparable<T> */ & foo.SomeExternalInterface & Error>(x: T): T;
|
||||
function generic3<A, B, C, D, E>(a: A, b: B, c: C, d: D): Nullable<E>;
|
||||
function inlineFun(x: number, callback: (p0: number) => void): void;
|
||||
function formatList(value: any/* kotlin.collections.List<UnknownType *> */): string;
|
||||
function createList(): any/* kotlin.collections.List<UnknownType *> */;
|
||||
function formatList(value: kotlin.collections.KtList<any /*UnknownType **/>): string;
|
||||
function createList(): kotlin.collections.KtList<any /*UnknownType **/>;
|
||||
function defaultParametersAtTheBegining(a: string | undefined, b: string): string;
|
||||
function nonDefaultParameterInBetween(a: string | undefined, b: string, c?: string): string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
namespace kotlin.collections {
|
||||
interface KtList<E> /* extends kotlin.collections.Collection<E> */ {
|
||||
asJsReadonlyArrayView(): ReadonlyArray<E>;
|
||||
readonly __doNotUseOrImplementIt: {
|
||||
readonly "kotlin.collections.KtList": unique symbol;
|
||||
};
|
||||
}
|
||||
}
|
||||
namespace foo {
|
||||
interface SomeExternalInterface {
|
||||
}
|
||||
@@ -18,8 +26,8 @@ declare namespace JS_TESTS {
|
||||
function genericWithMultipleConstraints<T extends unknown/* kotlin.Comparable<T> */ & foo.SomeExternalInterface & Error>(x: T): T;
|
||||
function generic3<A, B, C, D, E>(a: A, b: B, c: C, d: D): Nullable<E>;
|
||||
function inlineFun(x: number, callback: (p0: number) => void): void;
|
||||
function formatList(value: any/* kotlin.collections.List<UnknownType *> */): string;
|
||||
function createList(): any/* kotlin.collections.List<UnknownType *> */;
|
||||
function formatList(value: kotlin.collections.KtList<any /*UnknownType **/>): string;
|
||||
function createList(): kotlin.collections.KtList<any /*UnknownType **/>;
|
||||
function defaultParametersAtTheBegining(a: string | undefined, b: string): string;
|
||||
function nonDefaultParameterInBetween(a: string | undefined, b: string, c?: string): string;
|
||||
}
|
||||
|
||||
@@ -10111,6 +10111,11 @@ public open class ArrayList<E> : kotlin.collections.AbstractMutableList<E>, kotl
|
||||
|
||||
public open override fun addAll(elements: kotlin.collections.Collection<E>): kotlin.Boolean
|
||||
|
||||
@kotlin.js.ExperimentalJsExport
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public open override fun asJsArrayView(): kotlin.js.collections.JsArray<E>
|
||||
|
||||
public open override fun clear(): kotlin.Unit
|
||||
|
||||
public final fun ensureCapacity(minCapacity: kotlin.Int): kotlin.Unit
|
||||
@@ -10310,6 +10315,11 @@ public open class LinkedHashSet<E> : kotlin.collections.HashSet<E>, kotlin.colle
|
||||
public interface List<out E> : kotlin.collections.Collection<E> {
|
||||
public abstract override val size: kotlin.Int { get; }
|
||||
|
||||
@kotlin.js.ExperimentalJsExport
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public open fun asJsReadonlyArrayView(): kotlin.js.collections.JsReadonlyArray<E>
|
||||
|
||||
public abstract override operator fun contains(element: E): kotlin.Boolean
|
||||
|
||||
public abstract override fun containsAll(elements: kotlin.collections.Collection<E>): kotlin.Boolean
|
||||
@@ -10362,6 +10372,11 @@ public interface Map<K, out V> {
|
||||
|
||||
public abstract val values: kotlin.collections.Collection<V> { get; }
|
||||
|
||||
@kotlin.js.ExperimentalJsExport
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public open fun asJsReadonlyMapView(): kotlin.js.collections.JsReadonlyMap<K, V>
|
||||
|
||||
public abstract fun containsKey(key: K): kotlin.Boolean
|
||||
|
||||
public abstract fun containsValue(value: V): kotlin.Boolean
|
||||
@@ -10410,6 +10425,11 @@ public interface MutableList<E> : kotlin.collections.List<E>, kotlin.collections
|
||||
|
||||
public abstract override fun addAll(elements: kotlin.collections.Collection<E>): kotlin.Boolean
|
||||
|
||||
@kotlin.js.ExperimentalJsExport
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public open fun asJsArrayView(): kotlin.js.collections.JsArray<E>
|
||||
|
||||
public abstract override fun clear(): kotlin.Unit
|
||||
|
||||
public abstract override fun listIterator(): kotlin.collections.MutableListIterator<E>
|
||||
@@ -10448,6 +10468,11 @@ public interface MutableMap<K, V> : kotlin.collections.Map<K, V> {
|
||||
|
||||
public abstract override val values: kotlin.collections.MutableCollection<V> { get; }
|
||||
|
||||
@kotlin.js.ExperimentalJsExport
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public open fun asJsMapView(): kotlin.js.collections.JsMap<K, V>
|
||||
|
||||
public abstract fun clear(): kotlin.Unit
|
||||
|
||||
public abstract fun put(key: K, value: V): V?
|
||||
@@ -10466,6 +10491,11 @@ public interface MutableSet<E> : kotlin.collections.Set<E>, kotlin.collections.M
|
||||
|
||||
public abstract override fun addAll(elements: kotlin.collections.Collection<E>): kotlin.Boolean
|
||||
|
||||
@kotlin.js.ExperimentalJsExport
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public open fun asJsSetView(): kotlin.js.collections.JsSet<E>
|
||||
|
||||
public abstract override fun clear(): kotlin.Unit
|
||||
|
||||
public abstract override operator fun iterator(): kotlin.collections.MutableIterator<E>
|
||||
@@ -10483,6 +10513,11 @@ public interface RandomAccess {
|
||||
public interface Set<out E> : kotlin.collections.Collection<E> {
|
||||
public abstract override val size: kotlin.Int { get; }
|
||||
|
||||
@kotlin.js.ExperimentalJsExport
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public open fun asJsReadonlySetView(): kotlin.js.collections.JsReadonlySet<E>
|
||||
|
||||
public abstract override operator fun contains(element: E): kotlin.Boolean
|
||||
|
||||
public abstract override fun containsAll(elements: kotlin.collections.Collection<E>): kotlin.Boolean
|
||||
@@ -10498,4 +10533,4 @@ public abstract class ShortIterator : kotlin.collections.Iterator<kotlin.Short>
|
||||
public final override operator fun next(): kotlin.Short
|
||||
|
||||
public abstract fun nextShort(): kotlin.Short
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
@kotlin.js.JsName(name = "Array")
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
public open external class JsArray<E> : kotlin.js.collections.JsReadonlyArray<E> {
|
||||
public constructor JsArray<E>()
|
||||
}
|
||||
|
||||
@kotlin.js.JsName(name = "Map")
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
public open external class JsMap<K, V> : kotlin.js.collections.JsReadonlyMap<K, V> {
|
||||
public constructor JsMap<K, V>()
|
||||
}
|
||||
|
||||
@kotlin.js.JsName(name = "ReadonlyArray")
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
public external interface JsReadonlyArray<out E> {
|
||||
}
|
||||
|
||||
@kotlin.js.JsName(name = "ReadonlyMap")
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
public external interface JsReadonlyMap<K, out V> {
|
||||
}
|
||||
|
||||
@kotlin.js.JsName(name = "ReadonlySet")
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
public external interface JsReadonlySet<out E> {
|
||||
}
|
||||
|
||||
@kotlin.js.JsName(name = "Set")
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.js.ExperimentalJsCollectionsApi
|
||||
public open external class JsSet<E> : kotlin.js.collections.JsReadonlySet<E> {
|
||||
public constructor JsSet<E>()
|
||||
}
|
||||
@@ -201,6 +201,15 @@ public final annotation class EagerInitialization : kotlin.Annotation {
|
||||
public constructor EagerInitialization()
|
||||
}
|
||||
|
||||
@kotlin.RequiresOptIn(level = Level.WARNING)
|
||||
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.FUNCTION})
|
||||
@kotlin.annotation.MustBeDocumented
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public final annotation class ExperimentalJsCollectionsApi : kotlin.Annotation {
|
||||
public constructor ExperimentalJsCollectionsApi()
|
||||
}
|
||||
|
||||
@kotlin.RequiresOptIn(level = Level.WARNING)
|
||||
@kotlin.annotation.MustBeDocumented
|
||||
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||
|
||||
@@ -130,4 +130,19 @@ public expect annotation class JsExport() {
|
||||
)
|
||||
@MustBeDocumented
|
||||
@SinceKotlin("1.9")
|
||||
public annotation class ExperimentalJsReflectionCreateInstance
|
||||
public annotation class ExperimentalJsReflectionCreateInstance
|
||||
|
||||
/**
|
||||
* This annotation marks the experimental JS-collections API that allows to manipulate with native JS-collections
|
||||
* The API can be removed completely in any further release.
|
||||
*
|
||||
* Any usage of a declaration annotated with `@ExperimentalJsCollectionsApi` should be accepted either by
|
||||
* annotating that usage with the [OptIn] annotation, e.g. `@OptIn(ExperimentalJsCollectionsApi::class)`,
|
||||
* or by using the compiler argument `-opt-in=kotlin.js.ExperimentalJsCollectionsApi`.
|
||||
*/
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@MustBeDocumented
|
||||
@SinceKotlin("1.9")
|
||||
public annotation class ExperimentalJsCollectionsApi
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
import kotlin.js.collections.*
|
||||
|
||||
/**
|
||||
* Classes that inherit from this interface can be represented as a sequence of elements that can
|
||||
* be iterated over.
|
||||
@@ -146,8 +148,11 @@ public interface List<out E> : Collection<E> {
|
||||
// Query Operations
|
||||
|
||||
override val size: Int
|
||||
|
||||
override fun isEmpty(): Boolean
|
||||
|
||||
override fun contains(element: @UnsafeVariance E): Boolean
|
||||
|
||||
override fun iterator(): Iterator<E>
|
||||
|
||||
// Bulk Operations
|
||||
@@ -191,6 +196,15 @@ public interface List<out E> : Collection<E> {
|
||||
* Structural changes in the base list make the behavior of the view undefined.
|
||||
*/
|
||||
public fun subList(fromIndex: Int, toIndex: Int): List<E>
|
||||
|
||||
/**
|
||||
* Returns a view with the [JsReadonlyArray] methods to consume it in JavaScript as a regular readonly array.
|
||||
* Structural changes in the base list are synchronized with the view.
|
||||
*/
|
||||
@ExperimentalJsExport
|
||||
@ExperimentalJsCollectionsApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun asJsReadonlyArrayView(): JsReadonlyArray<E> = createJsReadonlyArrayViewFrom(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +240,9 @@ public interface MutableList<E> : List<E>, MutableCollection<E> {
|
||||
public fun addAll(index: Int, elements: Collection<E>): Boolean
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean
|
||||
|
||||
override fun clear(): Unit
|
||||
|
||||
// Positional Access Operations
|
||||
@@ -256,6 +272,15 @@ public interface MutableList<E> : List<E>, MutableCollection<E> {
|
||||
|
||||
// View
|
||||
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
|
||||
|
||||
/**
|
||||
* Returns a view with the [JsArray] methods to consume it in JavaScript as a regular array.
|
||||
* Structural changes in the base list are synchronized with the view, and vice verse.
|
||||
*/
|
||||
@ExperimentalJsExport
|
||||
@ExperimentalJsCollectionsApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun asJsArrayView(): JsArray<E> = createJsArrayViewFrom(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,12 +293,24 @@ public interface Set<out E> : Collection<E> {
|
||||
// Query Operations
|
||||
|
||||
override val size: Int
|
||||
|
||||
override fun isEmpty(): Boolean
|
||||
|
||||
override fun contains(element: @UnsafeVariance E): Boolean
|
||||
|
||||
override fun iterator(): Iterator<E>
|
||||
|
||||
// Bulk Operations
|
||||
override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
|
||||
|
||||
/**
|
||||
* Returns a view with the [JsReadonlySet] methods to consume it in JavaScript as a regular readonly Set.
|
||||
* Structural changes in the base set are synchronized with the view.
|
||||
*/
|
||||
@ExperimentalJsExport
|
||||
@ExperimentalJsCollectionsApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun asJsReadonlySetView(): JsReadonlySet<E> = createJsReadonlySetViewFrom(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,9 +336,21 @@ public interface MutableSet<E> : Set<E>, MutableCollection<E> {
|
||||
// Bulk Modification Operations
|
||||
|
||||
override fun addAll(elements: Collection<E>): Boolean
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean
|
||||
|
||||
override fun clear(): Unit
|
||||
|
||||
/**
|
||||
* Returns a view with the [JsSet] methods to consume it in JavaScript as a regular Set.
|
||||
* Structural changes in the base set are synchronized with the view, and vice verse.
|
||||
*/
|
||||
@ExperimentalJsExport
|
||||
@ExperimentalJsCollectionsApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun asJsSetView(): JsSet<E> = createJsSetViewFrom(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,6 +419,15 @@ public interface Map<K, out V> {
|
||||
*/
|
||||
public val value: V
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view with the [JsReadonlyMap] methods to consume it in JavaScript as a regular readonly Map.
|
||||
* Structural changes in the base map are synchronized with the view.
|
||||
*/
|
||||
@ExperimentalJsExport
|
||||
@ExperimentalJsCollectionsApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun asJsReadonlyMapView(): JsReadonlyMap<K, V> = createJsReadonlyMapViewFrom(this)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,4 +490,13 @@ public interface MutableMap<K, V> : Map<K, V> {
|
||||
*/
|
||||
public fun setValue(newValue: V): V
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a view with the [JsMap] methods to consume it in JavaScript as a regular Map.
|
||||
* Structural changes in the base map are synchronized with the view, and vice verse.
|
||||
*/
|
||||
@ExperimentalJsExport
|
||||
@ExperimentalJsCollectionsApi
|
||||
@SinceKotlin("1.9")
|
||||
public fun asJsMapView(): JsMap<K, V> = createJsMapViewFrom(this)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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:OptIn(ExperimentalJsCollectionsApi::class)
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
import kotlin.js.collections.*
|
||||
|
||||
private class JsArrayView<E> : JsArray<E>()
|
||||
|
||||
private fun UNSUPPORTED_OPERATION() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
internal fun <E> createJsReadonlyArrayViewFrom(list: List<E>): JsReadonlyArray<E> =
|
||||
createJsArrayViewWith(
|
||||
listSize = { list.size },
|
||||
listGet = { i -> list[i] },
|
||||
listSet = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
listDecreaseSize = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
listIncreaseSize = ::UNSUPPORTED_OPERATION.asDynamic()
|
||||
)
|
||||
|
||||
internal fun <E> createJsArrayViewFrom(list: MutableList<E>): JsArray<E> =
|
||||
createJsArrayViewWith(
|
||||
listSize = { list.size },
|
||||
listGet = { i -> list[i] },
|
||||
listSet = { i, v -> list[i] = v },
|
||||
listDecreaseSize = { size -> list.subList(list.size - size, list.size).clear() },
|
||||
listIncreaseSize = ::UNSUPPORTED_OPERATION.asDynamic()
|
||||
)
|
||||
|
||||
@Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER")
|
||||
private fun <E> createJsArrayViewWith(
|
||||
listSize: () -> Int,
|
||||
listGet: (Int) -> E,
|
||||
listSet: (Int, E) -> Unit,
|
||||
listDecreaseSize: (Int) -> Unit,
|
||||
listIncreaseSize: (Int) -> Unit,
|
||||
): dynamic {
|
||||
val arrayView = objectCreate<JsArrayView<*>>()
|
||||
|
||||
return js("""
|
||||
new Proxy(arrayView, {
|
||||
get: function(target, prop, receiver) {
|
||||
if (prop === "length") return listSize();
|
||||
var type = typeof prop
|
||||
var index = type === "string" || type === "number" ? +prop : undefined
|
||||
if (!isNaN(index)) return listGet(index);
|
||||
return target[prop]
|
||||
},
|
||||
has: function(target, key) { return !isNaN(key) && key < listSize() },
|
||||
set: function(obj, prop, value) {
|
||||
if (prop === "length") {
|
||||
var size = listSize();
|
||||
var newSize = type === "string" || type === "number" ? +prop : undefined
|
||||
if (isNaN(newSize)) throw new RangeError("invalid array length")
|
||||
if (newSize < size) listDecreaseSize(size - newSize)
|
||||
else listIncreaseSize(newSize - size)
|
||||
return true
|
||||
}
|
||||
|
||||
var type = typeof prop;
|
||||
var index = type === "string" || type === "number" ? +prop : undefined;
|
||||
|
||||
if (isNaN(index)) return false;
|
||||
|
||||
listSet(index, value)
|
||||
|
||||
return true
|
||||
},
|
||||
})
|
||||
""")
|
||||
}
|
||||
|
||||
private class JsSetView<E> : JsSet<E>()
|
||||
|
||||
internal fun <E> createJsReadonlySetViewFrom(set: Set<E>): JsReadonlySet<E> =
|
||||
createJsSetViewWith<E>(
|
||||
setSize = { set.size },
|
||||
setAdd = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
setRemove = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
setClear = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
setContains = { v -> set.contains(v) },
|
||||
valuesIterator = { createJsIteratorFrom(set.iterator()) },
|
||||
entriesIterator = { createJsIteratorFrom(set.iterator()) { arrayOf(it, it) } },
|
||||
forEach = { cb, t -> forEach(cb, t) }
|
||||
)
|
||||
|
||||
internal fun <E> createJsSetViewFrom(set: MutableSet<E>): JsSet<E> =
|
||||
createJsSetViewWith<E>(
|
||||
setSize = { set.size },
|
||||
setAdd = { v -> set.add(v) },
|
||||
setRemove = { v -> set.remove(v) },
|
||||
setClear = { set.clear() },
|
||||
setContains = { v -> set.contains(v) },
|
||||
valuesIterator = { createJsIteratorFrom(set.iterator()) },
|
||||
entriesIterator = { createJsIteratorFrom(set.iterator()) { arrayOf(it, it) } },
|
||||
forEach = { cb, t -> forEach(cb, t) }
|
||||
)
|
||||
|
||||
@Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER")
|
||||
private fun <E> createJsSetViewWith(
|
||||
setSize: () -> Int,
|
||||
setAdd: (E) -> Unit,
|
||||
setRemove: (E) -> Boolean,
|
||||
setClear: () -> Unit,
|
||||
setContains: (E) -> Boolean,
|
||||
valuesIterator: () -> dynamic,
|
||||
entriesIterator: () -> dynamic,
|
||||
forEach: (dynamic, dynamic) -> Unit,
|
||||
): dynamic {
|
||||
val setView = objectCreate<JsSetView<E>>().also {
|
||||
js("it[Symbol.iterator] = valuesIterator")
|
||||
defineProp(it, "size", setSize, VOID)
|
||||
}
|
||||
|
||||
return js("""
|
||||
Object.assign(setView, {
|
||||
add: function(value) { setAdd(value); return this },
|
||||
'delete': setRemove,
|
||||
clear: setClear,
|
||||
has: setContains,
|
||||
keys: valuesIterator,
|
||||
values: valuesIterator,
|
||||
entries: entriesIterator,
|
||||
forEach: function (cb, thisArg) { forEach(cb, thisArg || setView) }
|
||||
})
|
||||
""")
|
||||
}
|
||||
|
||||
|
||||
private class JsMapView<K, V> : JsMap<K, V>()
|
||||
|
||||
internal fun <K, V> createJsReadonlyMapViewFrom(map: Map<K, V>): JsReadonlyMap<K, V> =
|
||||
createJsMapViewWith<K, V>(
|
||||
mapSize = { map.size },
|
||||
mapGet = { k -> map[k] },
|
||||
mapContains = { k -> map.containsKey(k) },
|
||||
mapPut = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
mapRemove = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
mapClear = ::UNSUPPORTED_OPERATION.asDynamic(),
|
||||
keysIterator = { createJsIteratorFrom(map.keys.iterator()) },
|
||||
valuesIterator = { createJsIteratorFrom(map.values.iterator()) },
|
||||
entriesIterator = { createJsIteratorFrom(map.entries.iterator()) { arrayOf(it.key, it.value) } },
|
||||
forEach = { cb, t -> forEach(cb, t) }
|
||||
)
|
||||
|
||||
internal fun <K, V> createJsMapViewFrom(map: MutableMap<K, V>): JsMap<K, V> =
|
||||
createJsMapViewWith<K, V>(
|
||||
mapSize = { map.size },
|
||||
mapGet = { k -> map[k] },
|
||||
mapContains = { k -> map.containsKey(k) },
|
||||
mapPut = { k, v -> map.put(k, v) },
|
||||
mapRemove = { k -> map.remove(k) },
|
||||
mapClear = { map.clear() },
|
||||
keysIterator = { createJsIteratorFrom(map.keys.iterator()) },
|
||||
valuesIterator = { createJsIteratorFrom(map.values.iterator()) },
|
||||
entriesIterator = { createJsIteratorFrom(map.entries.iterator()) { arrayOf(it.key, it.value) } },
|
||||
forEach = { cb, t -> forEach(cb, t) }
|
||||
)
|
||||
|
||||
@Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER")
|
||||
private fun <K, V> createJsMapViewWith(
|
||||
mapSize: () -> Int,
|
||||
mapGet: (K) -> V?,
|
||||
mapContains: (K) -> Boolean,
|
||||
mapPut: (K, V) -> Unit,
|
||||
mapRemove: (K) -> Unit,
|
||||
mapClear: () -> Unit,
|
||||
keysIterator: () -> dynamic,
|
||||
valuesIterator: () -> dynamic,
|
||||
entriesIterator: () -> dynamic,
|
||||
forEach: (dynamic, dynamic) -> Unit,
|
||||
): dynamic {
|
||||
val mapView = objectCreate<JsMapView<K, V>>().also {
|
||||
js("it[Symbol.iterator] = entriesIterator")
|
||||
defineProp(it, "size", mapSize, VOID)
|
||||
}
|
||||
|
||||
return js("""
|
||||
Object.assign(mapView, {
|
||||
get: mapGet,
|
||||
set: function(key, value) { mapPut(key, value); return this },
|
||||
'delete': mapRemove,
|
||||
clear: mapClear,
|
||||
has: mapContains,
|
||||
keys: valuesIterator,
|
||||
values: valuesIterator,
|
||||
entries: entriesIterator,
|
||||
forEach: function (cb, thisArg) { forEach(cb, thisArg || mapView) }
|
||||
})
|
||||
""")
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER")
|
||||
private fun <T> createJsIteratorFrom(iterator: Iterator<T>, transform: (T) -> dynamic = { it }): dynamic {
|
||||
val iteratorNext = { iterator.next() }
|
||||
val iteratorHasNext = { iterator.hasNext() }
|
||||
return js("""{
|
||||
next: function() {
|
||||
var result = { done: !iteratorHasNext() };
|
||||
if (!result.done) result.value = transform(iteratorNext());
|
||||
return result;
|
||||
}
|
||||
}""")
|
||||
}
|
||||
|
||||
private fun forEach(cb: (dynamic, dynamic, dynamic) -> Unit, thisArg: dynamic) {
|
||||
val iterator = thisArg.entries()
|
||||
var result = iterator.next()
|
||||
|
||||
while (!result.done) {
|
||||
val value = result.value
|
||||
cb(value[0], value[1], thisArg)
|
||||
result = iterator.next()
|
||||
}
|
||||
}
|
||||
@@ -208,7 +208,7 @@ internal fun protoOf(constructor: Any) =
|
||||
js("constructor.prototype")
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
internal fun <T> objectCreate(proto: T?) =
|
||||
internal fun <T> objectCreate(proto: T? = null): T =
|
||||
js("Object.create(proto)")
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
|
||||
@@ -44,16 +44,17 @@ internal fun safePropertySet(self: dynamic, setterName: String, propName: String
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
|
||||
internal annotation class JsFun(val code: String)
|
||||
|
||||
/**
|
||||
* The annotation is needed for annotating class declarations and type alias which are used inside exported declarations, but
|
||||
* doesn't contain @JsExport annotation
|
||||
* This information is used for generating special tagged types inside d.ts files, for more strict usage of implicitly exported entities
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
internal annotation class JsImplicitExport
|
||||
|
||||
/**
|
||||
* The annotation is needed for annotating function declarations that should be compiled as ES6 generators
|
||||
*/
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
internal annotation class JsGenerator
|
||||
|
||||
/**
|
||||
* The annotation is needed for annotating class declarations and type alias which are used inside exported declarations, but
|
||||
* doesn't contain @JsExport annotation
|
||||
* This information is used for generating special tagged types inside d.ts files, for more strict usage of implicitly exported entities
|
||||
*/
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
internal annotation class JsImplicitExport(val couldBeConvertedToExplicitExport: Boolean)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
package kotlin.collections
|
||||
|
||||
import kotlin.js.collections.JsArray
|
||||
|
||||
/**
|
||||
* Provides a [MutableList] implementation, which uses a resizable array as its backing storage.
|
||||
*
|
||||
@@ -178,6 +180,10 @@ public actual open class ArrayList<E> internal constructor(private var array: Ar
|
||||
return js("[]").slice.call(array)
|
||||
}
|
||||
|
||||
@ExperimentalJsExport
|
||||
@ExperimentalJsCollectionsApi
|
||||
@SinceKotlin("1.9")
|
||||
override fun asJsArrayView(): JsArray<E> = array.unsafeCast<JsArray<E>>()
|
||||
|
||||
internal override fun checkIsMutable() {
|
||||
if (isReadOnly) throw UnsupportedOperationException()
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.js.collections
|
||||
|
||||
|
||||
/**
|
||||
* Exposes the TypeScript [ReadonlyArray](https://www.typescriptlang.org/docs/handbook/2/objects.html#the-readonlyarray-type) to Kotlin.
|
||||
*/
|
||||
@JsName("ReadonlyArray")
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalJsCollectionsApi
|
||||
public external interface JsReadonlyArray<out E>
|
||||
|
||||
/**
|
||||
* Exposes the JavaScript [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) to Kotlin.
|
||||
*/
|
||||
@JsName("Array")
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalJsCollectionsApi
|
||||
public external open class JsArray<E> : JsReadonlyArray<E>
|
||||
|
||||
/**
|
||||
* Exposes the TypeScript [ReadonlySet](https://github.com/microsoft/TypeScript/blob/bd952a7a83ce04b3541b952238b6c0e4316b7d5d/src/lib/es2015.collection.d.ts#L103) to Kotlin.
|
||||
*/
|
||||
@JsName("ReadonlySet")
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalJsCollectionsApi
|
||||
public external interface JsReadonlySet<out E>
|
||||
|
||||
/**
|
||||
* Exposes the JavaScript [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) to Kotlin.
|
||||
*/
|
||||
@JsName("Set")
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalJsCollectionsApi
|
||||
public external open class JsSet<E> : JsReadonlySet<E>
|
||||
|
||||
/**
|
||||
* Exposes the TypeScript [ReadonlyMap](https://github.com/microsoft/TypeScript/blob/bd952a7a83ce04b3541b952238b6c0e4316b7d5d/src/lib/es2015.collection.d.ts#L37) to Kotlin.
|
||||
*/
|
||||
@JsName("ReadonlyMap")
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalJsCollectionsApi
|
||||
public external interface JsReadonlyMap<K, out V>
|
||||
|
||||
/**
|
||||
* Exposes the JavaScript [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) to Kotlin.
|
||||
*/
|
||||
@JsName("Map")
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalJsCollectionsApi
|
||||
public external open class JsMap<K, V> : JsReadonlyMap<K, V>
|
||||
+3
@@ -3384,6 +3384,9 @@ public final class kotlin/jdk7/AutoCloseableKt {
|
||||
public static final fun closeFinally (Ljava/lang/AutoCloseable;Ljava/lang/Throwable;)V
|
||||
}
|
||||
|
||||
public abstract interface annotation class kotlin/js/ExperimentalJsCollectionsApi : java/lang/annotation/Annotation {
|
||||
}
|
||||
|
||||
public abstract interface annotation class kotlin/js/ExperimentalJsExport : java/lang/annotation/Annotation {
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user