[K/JS] Support KClass<*>.createInstance reflection method ^KT-58684 Fixed
This commit is contained in:
@@ -216,6 +216,15 @@ public final annotation class ExperimentalJsFileName : kotlin.Annotation {
|
||||
public constructor ExperimentalJsFileName()
|
||||
}
|
||||
|
||||
@kotlin.RequiresOptIn(level = Level.WARNING)
|
||||
@kotlin.annotation.Retention(value = AnnotationRetention.BINARY)
|
||||
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FIELD, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.TYPEALIAS})
|
||||
@kotlin.annotation.MustBeDocumented
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
public final annotation class ExperimentalJsReflectionCreateInstance : kotlin.Annotation {
|
||||
public constructor ExperimentalJsReflectionCreateInstance()
|
||||
}
|
||||
|
||||
public external object JSON {
|
||||
public final fun <T> parse(text: kotlin.String): T
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ public inline fun <reified T> typeOf(): kotlin.reflect.KType
|
||||
@kotlin.internal.LowPriorityInOverloadResolution
|
||||
public fun <T : kotlin.Any> kotlin.reflect.KClass<T>.cast(value: kotlin.Any?): T
|
||||
|
||||
@kotlin.SinceKotlin(version = "1.9")
|
||||
@kotlin.js.ExperimentalJsReflectionCreateInstance
|
||||
public fun <T : kotlin.Any> kotlin.reflect.KClass<T>.createInstance(): T
|
||||
|
||||
@kotlin.reflect.ExperimentalAssociatedObjects
|
||||
public inline fun <reified T : kotlin.Annotation> kotlin.reflect.KClass<*>.findAssociatedObject(): kotlin.Any?
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package kotlin.js
|
||||
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Gives a declaration (a function, a property or a class) specific name in JavaScript.
|
||||
@@ -100,3 +101,30 @@ public expect annotation class JsExport() {
|
||||
public annotation class Ignore()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This annotation marks the experimental Kotlin/JS reflection API that allows to create an instance of provided [KClass]
|
||||
* The API can be removed completely in any further release.
|
||||
*
|
||||
* Any usage of a declaration annotated with `@ExperimentalJsReflectionCreateInstance` should be accepted either by
|
||||
* annotating that usage with the [OptIn] annotation, e.g. `@OptIn(ExperimentalJsReflectionCreateInstance::class)`,
|
||||
* or by using the compiler argument `-opt-in=kotlin.js.ExperimentalJsReflectionCreateInstance`.
|
||||
*/
|
||||
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(
|
||||
AnnotationTarget.CLASS,
|
||||
AnnotationTarget.ANNOTATION_CLASS,
|
||||
AnnotationTarget.PROPERTY,
|
||||
AnnotationTarget.FIELD,
|
||||
AnnotationTarget.LOCAL_VARIABLE,
|
||||
AnnotationTarget.VALUE_PARAMETER,
|
||||
AnnotationTarget.CONSTRUCTOR,
|
||||
AnnotationTarget.FUNCTION,
|
||||
AnnotationTarget.PROPERTY_GETTER,
|
||||
AnnotationTarget.PROPERTY_SETTER,
|
||||
AnnotationTarget.TYPEALIAS
|
||||
)
|
||||
@MustBeDocumented
|
||||
@SinceKotlin("1.9")
|
||||
public annotation class ExperimentalJsReflectionCreateInstance
|
||||
@@ -8,8 +8,8 @@ package kotlin.js
|
||||
internal typealias BitMask = IntArray
|
||||
|
||||
private fun bitMaskWith(activeBit: Int): BitMask {
|
||||
val intArray = IntArray((activeBit shr 5) + 1)
|
||||
val numberIndex = activeBit shr 5
|
||||
val intArray = IntArray(numberIndex + 1)
|
||||
val positionInNumber = activeBit and 31
|
||||
val numberWithSettledBit = 1 shl positionInNumber
|
||||
intArray[numberIndex] = intArray[numberIndex] or numberWithSettledBit
|
||||
|
||||
@@ -227,4 +227,7 @@ internal fun jsInIntrinsic(lhs: Any?, rhs: Any): Boolean
|
||||
internal fun jsDelete(e: Any?)
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsContextfulRef(context: dynamic, fn: dynamic): dynamic
|
||||
internal fun jsContextfulRef(context: dynamic, fn: dynamic): dynamic
|
||||
|
||||
@JsIntrinsic
|
||||
internal fun jsIsEs6(): Boolean
|
||||
|
||||
@@ -7,9 +7,10 @@ package kotlin.js
|
||||
internal fun setMetadataFor(
|
||||
ctor: Ctor,
|
||||
name: String?,
|
||||
metadataConstructor: (name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?) -> Metadata,
|
||||
metadataConstructor: (name: String?, defaultConstructor: dynamic, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?) -> Metadata,
|
||||
parent: Ctor?,
|
||||
interfaces: Array<dynamic>?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
@@ -21,7 +22,7 @@ internal fun setMetadataFor(
|
||||
""")
|
||||
}
|
||||
|
||||
val metadata = metadataConstructor(name, associatedObjectKey, associatedObjects, suspendArity ?: js("[]"))
|
||||
val metadata = metadataConstructor(name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity ?: js("[]"))
|
||||
ctor.`$metadata$` = metadata
|
||||
|
||||
if (interfaces != null) {
|
||||
@@ -45,16 +46,34 @@ private fun generateInterfaceId(): Int {
|
||||
}
|
||||
|
||||
|
||||
internal fun interfaceMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
||||
return createMetadata("interface", name, associatedObjectKey, associatedObjects, suspendArity, generateInterfaceId())
|
||||
internal fun interfaceMeta(
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
): Metadata {
|
||||
return createMetadata("interface", name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, generateInterfaceId())
|
||||
}
|
||||
|
||||
internal fun objectMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
||||
return createMetadata("object", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||
internal fun objectMeta(
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
): Metadata {
|
||||
return createMetadata("object", name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||
}
|
||||
|
||||
internal fun classMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
||||
return createMetadata("class", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||
internal fun classMeta(
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?
|
||||
): Metadata {
|
||||
return createMetadata("class", name, defaultConstructor, associatedObjectKey, associatedObjects, suspendArity, null)
|
||||
}
|
||||
|
||||
// Seems like we need to disable this check if variables are used inside js annotation
|
||||
@@ -62,6 +81,7 @@ internal fun classMeta(name: String?, associatedObjectKey: Number?, associatedOb
|
||||
private fun createMetadata(
|
||||
kind: String,
|
||||
name: String?,
|
||||
defaultConstructor: dynamic,
|
||||
associatedObjectKey: Number?,
|
||||
associatedObjects: dynamic,
|
||||
suspendArity: Array<Int>?,
|
||||
@@ -75,6 +95,7 @@ private fun createMetadata(
|
||||
associatedObjects: associatedObjects,
|
||||
suspendArity: suspendArity,
|
||||
${'$'}kClass$: undef,
|
||||
defaultConstructor: defaultConstructor,
|
||||
iid: iid
|
||||
})""")
|
||||
}
|
||||
@@ -90,6 +111,7 @@ internal external interface Metadata {
|
||||
val iid: Int?
|
||||
|
||||
var `$kClass$`: dynamic
|
||||
val defaultConstructor: dynamic
|
||||
|
||||
var errorInfo: Int? // Bits set for overridden properties: "message" => 0x1, "cause" => 0x2
|
||||
}
|
||||
@@ -44,7 +44,7 @@ private fun getKPropMetadata(paramCount: Int, setter: Any?): dynamic {
|
||||
}
|
||||
|
||||
private fun metadataObject(): Metadata {
|
||||
return classMeta(VOID, VOID, VOID, VOID)
|
||||
return classMeta(VOID, VOID, VOID, VOID, VOID)
|
||||
}
|
||||
|
||||
private val propertyRefClassMetadataCache: Array<Array<dynamic>> = arrayOf<Array<dynamic>>(
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.reflect
|
||||
|
||||
/**
|
||||
* Creates a new instance of the class, calling a constructor which either has no parameters or all parameters of which have
|
||||
* a default value. If there are no or many such constructors, an exception is thrown.
|
||||
*/
|
||||
@OptIn(JsIntrinsic::class)
|
||||
@SinceKotlin("1.9")
|
||||
@ExperimentalJsReflectionCreateInstance
|
||||
public fun <T : Any> KClass<T>.createInstance(): T {
|
||||
val jsClass = js.asDynamic()
|
||||
|
||||
if (jsClass === js("Object")) return js("{}")
|
||||
|
||||
val noArgsConstructor = jsClass.`$metadata$`.unsafeCast<Metadata?>()?.defaultConstructor
|
||||
?: throw IllegalArgumentException("Class \"$simpleName\" should have a single no-arg constructor")
|
||||
|
||||
return if (jsIsEs6() && noArgsConstructor !== jsClass) {
|
||||
js("noArgsConstructor.call(jsClass)")
|
||||
} else {
|
||||
js("new noArgsConstructor()")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user