[K/JS] Support KClass<*>.createInstance reflection method ^KT-58684 Fixed
This commit is contained in:
@@ -30,6 +30,9 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
|||||||
|
|
||||||
// TODO: Should we drop operator intrinsics in favor of IrDynamicOperatorExpression?
|
// TODO: Should we drop operator intrinsics in favor of IrDynamicOperatorExpression?
|
||||||
|
|
||||||
|
// Modes
|
||||||
|
val jsIsEs6 = getInternalFunction("jsIsEs6")
|
||||||
|
|
||||||
// Global variables
|
// Global variables
|
||||||
val void = getInternalProperty("VOID")
|
val void = getInternalProperty("VOID")
|
||||||
val globalThis = getInternalProperty("globalThis")
|
val globalThis = getInternalProperty("globalThis")
|
||||||
|
|||||||
@@ -140,6 +140,12 @@ private val validateIrAfterLowering = makeCustomJsModulePhase(
|
|||||||
description = "Validate IR after lowering"
|
description = "Validate IR after lowering"
|
||||||
).toModuleLowering()
|
).toModuleLowering()
|
||||||
|
|
||||||
|
private val collectClassDefaultConstructorsPhase = makeDeclarationTransformerPhase(
|
||||||
|
::CollectClassDefaultConstructorsLowering,
|
||||||
|
name = "CollectClassDefaultConstructorsLowering",
|
||||||
|
description = "Collect classes default constructors to add it to metadata on code generating phase"
|
||||||
|
)
|
||||||
|
|
||||||
private val preventExportOfSyntheticDeclarationsLowering = makeDeclarationTransformerPhase(
|
private val preventExportOfSyntheticDeclarationsLowering = makeDeclarationTransformerPhase(
|
||||||
::ExcludeSyntheticDeclarationsFromExportLowering,
|
::ExcludeSyntheticDeclarationsFromExportLowering,
|
||||||
name = "ExcludeSyntheticDeclarationsFromExportLowering",
|
name = "ExcludeSyntheticDeclarationsFromExportLowering",
|
||||||
@@ -898,6 +904,7 @@ val loweringList = listOf<Lowering>(
|
|||||||
initializersLoweringPhase,
|
initializersLoweringPhase,
|
||||||
initializersCleanupLoweringPhase,
|
initializersCleanupLoweringPhase,
|
||||||
kotlinNothingValueExceptionPhase,
|
kotlinNothingValueExceptionPhase,
|
||||||
|
collectClassDefaultConstructorsPhase,
|
||||||
// Common prefix ends
|
// Common prefix ends
|
||||||
enumWhenPhase,
|
enumWhenPhase,
|
||||||
enumEntryInstancesLoweringPhase,
|
enumEntryInstancesLoweringPhase,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.ir.declarations.*
|
|||||||
import java.util.WeakHashMap
|
import java.util.WeakHashMap
|
||||||
|
|
||||||
class JsMapping : DefaultMapping() {
|
class JsMapping : DefaultMapping() {
|
||||||
|
val classToItsDefaultConstructor = WeakHashMap<IrClass, IrConstructor>()
|
||||||
|
|
||||||
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
|
val esClassWhichNeedBoxParameters = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, Boolean>()
|
||||||
val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>()
|
val esClassToPossibilityForOptimization = DefaultDelegateFactory.newDeclarationToValueMapping<IrClass, MutableReference<Boolean>>()
|
||||||
|
|
||||||
|
|||||||
+14
-3
@@ -70,10 +70,14 @@ internal class JsUsefulDeclarationProcessor(
|
|||||||
referencedJsClassesFromExpressions += ref.owner
|
referencedJsClassesFromExpressions += ref.owner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context.reflectionSymbols.getKClass -> {
|
||||||
|
addConstructedClass(expression.getTypeArgument(0)!!.classifierOrFail.owner as IrClass)
|
||||||
|
}
|
||||||
|
|
||||||
context.intrinsics.jsObjectCreateSymbol -> {
|
context.intrinsics.jsObjectCreateSymbol -> {
|
||||||
val classToCreate = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
val classToCreate = expression.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
||||||
classToCreate.enqueue(data, "intrinsic: jsObjectCreateSymbol")
|
classToCreate.enqueue(data, "intrinsic: jsObjectCreateSymbol")
|
||||||
constructedClasses += classToCreate
|
addConstructedClass(classToCreate)
|
||||||
}
|
}
|
||||||
|
|
||||||
context.intrinsics.jsCreateThisSymbol -> {
|
context.intrinsics.jsCreateThisSymbol -> {
|
||||||
@@ -88,7 +92,7 @@ internal class JsUsefulDeclarationProcessor(
|
|||||||
val classToCreate = classTypeToCreate.classifierOrFail.owner as IrClass
|
val classToCreate = classTypeToCreate.classifierOrFail.owner as IrClass
|
||||||
|
|
||||||
classToCreate.enqueue(data, "intrinsic: jsCreateThis")
|
classToCreate.enqueue(data, "intrinsic: jsCreateThis")
|
||||||
constructedClasses += classToCreate
|
addConstructedClass(classToCreate)
|
||||||
}
|
}
|
||||||
|
|
||||||
context.intrinsics.jsEquals -> {
|
context.intrinsics.jsEquals -> {
|
||||||
@@ -117,7 +121,14 @@ internal class JsUsefulDeclarationProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addConstructedClass(irClass: IrClass) {
|
||||||
|
super.addConstructedClass(irClass)
|
||||||
|
|
||||||
|
if (irClass.isClass) {
|
||||||
|
context.findDefaultConstructorFor(irClass)?.enqueue(irClass, "intrinsic: KClass<*>.createInstance")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processSuperTypes(irClass: IrClass) {
|
override fun processSuperTypes(irClass: IrClass) {
|
||||||
@@ -178,7 +189,7 @@ internal class JsUsefulDeclarationProcessor(
|
|||||||
super.processSimpleFunction(irFunction)
|
super.processSimpleFunction(irFunction)
|
||||||
|
|
||||||
if (irFunction.isEs6ConstructorReplacement) {
|
if (irFunction.isEs6ConstructorReplacement) {
|
||||||
constructedClasses += irFunction.dispatchReceiverParameter?.type?.classOrNull?.owner!!
|
addConstructedClass(irFunction.dispatchReceiverParameter?.type?.classOrNull?.owner!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (irFunction.isReal && irFunction.body != null) {
|
if (irFunction.isReal && irFunction.body != null) {
|
||||||
|
|||||||
+5
-1
@@ -139,6 +139,10 @@ abstract class UsefulDeclarationProcessor(
|
|||||||
|
|
||||||
val usefulPolyfilledDeclarations = hashSetOf<IrDeclaration>()
|
val usefulPolyfilledDeclarations = hashSetOf<IrDeclaration>()
|
||||||
|
|
||||||
|
protected open fun addConstructedClass(irClass: IrClass) {
|
||||||
|
constructedClasses += irClass
|
||||||
|
}
|
||||||
|
|
||||||
protected open fun processField(irField: IrField): Unit = Unit
|
protected open fun processField(irField: IrField): Unit = Unit
|
||||||
|
|
||||||
protected open fun processClass(irClass: IrClass) {
|
protected open fun processClass(irClass: IrClass) {
|
||||||
@@ -176,7 +180,7 @@ abstract class UsefulDeclarationProcessor(
|
|||||||
// Collect instantiated classes.
|
// Collect instantiated classes.
|
||||||
irConstructor.constructedClass.let {
|
irConstructor.constructedClass.let {
|
||||||
it.enqueue(irConstructor, "constructed class")
|
it.enqueue(irConstructor, "constructed class")
|
||||||
constructedClasses += it
|
addConstructedClass(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -60,6 +60,12 @@ class UselessDeclarationsRemover(
|
|||||||
.flatMap { it.classOrNull?.collectUsedSuperTypes() ?: emptyList() }
|
.flatMap { it.classOrNull?.collectUsedSuperTypes() ?: emptyList() }
|
||||||
.distinct()
|
.distinct()
|
||||||
.memoryOptimizedMap { it.defaultType }
|
.memoryOptimizedMap { it.defaultType }
|
||||||
|
|
||||||
|
// Remove default constructor if the class was never constructed
|
||||||
|
val defaultConstructor = context.findDefaultConstructorFor(declaration)
|
||||||
|
if (defaultConstructor != null && defaultConstructor !in usefulDeclarations) {
|
||||||
|
context.mapping.classToItsDefaultConstructor.remove(declaration)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrClassSymbol.collectUsedSuperTypes(): Set<IrClassSymbol> {
|
private fun IrClassSymbol.collectUsedSuperTypes(): Set<IrClassSymbol> {
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.descriptors.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.constructors
|
||||||
|
import org.jetbrains.kotlin.ir.util.isClass
|
||||||
|
|
||||||
|
class CollectClassDefaultConstructorsLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
|
||||||
|
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
|
||||||
|
if (declaration is IrClass && declaration.couldContainDefaultConstructor()) {
|
||||||
|
context.mapping.classToItsDefaultConstructor[declaration] = declaration.defaultConstructor ?: return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrClass.couldContainDefaultConstructor(): Boolean {
|
||||||
|
return isClass && !isValue && !isExpect && modality != Modality.ABSTRACT && modality != Modality.SEALED
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IrClass.defaultConstructor: IrConstructor?
|
||||||
|
get() = constructors.singleOrNull { it.visibility == DescriptorVisibilities.PUBLIC && it.isDefaultConstructor() }
|
||||||
|
|
||||||
|
private fun IrFunction.isDefaultConstructor(): Boolean {
|
||||||
|
return valueParameters.isEmpty() || valueParameters.all { it.defaultValue != null }
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.utils.newHashMapWithExpectedSize
|
|||||||
|
|
||||||
object ES6_INIT_CALL : IrStatementOriginImpl("ES6_INIT_CALL")
|
object ES6_INIT_CALL : IrStatementOriginImpl("ES6_INIT_CALL")
|
||||||
object ES6_CONSTRUCTOR_REPLACEMENT : IrDeclarationOriginImpl("ES6_CONSTRUCTOR_REPLACEMENT")
|
object ES6_CONSTRUCTOR_REPLACEMENT : IrDeclarationOriginImpl("ES6_CONSTRUCTOR_REPLACEMENT")
|
||||||
|
object ES6_SYNTHETIC_EXPORT_CONSTRUCTOR: IrDeclarationOriginImpl("ES6_SYNTHETIC_EXPORT_CONSTRUCTOR")
|
||||||
object ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT : IrDeclarationOriginImpl("ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT")
|
object ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT : IrDeclarationOriginImpl("ES6_PRIMARY_CONSTRUCTOR_REPLACEMENT")
|
||||||
object ES6_INIT_FUNCTION : IrDeclarationOriginImpl("ES6_INIT_FUNCTION")
|
object ES6_INIT_FUNCTION : IrDeclarationOriginImpl("ES6_INIT_FUNCTION")
|
||||||
object ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT : IrStatementOriginImpl("ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT")
|
object ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT : IrStatementOriginImpl("ES6_DELEGATING_CONSTRUCTOR_REPLACEMENT")
|
||||||
@@ -50,6 +51,9 @@ val IrDeclaration.isInitFunction: Boolean
|
|||||||
val IrFunctionAccessExpression.isInitCall: Boolean
|
val IrFunctionAccessExpression.isInitCall: Boolean
|
||||||
get() = origin == ES6_INIT_CALL
|
get() = origin == ES6_INIT_CALL
|
||||||
|
|
||||||
|
val IrDeclaration.isSyntheticConstructorForExport: Boolean
|
||||||
|
get() = origin == ES6_SYNTHETIC_EXPORT_CONSTRUCTOR
|
||||||
|
|
||||||
class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTransformer {
|
class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTransformer {
|
||||||
private var IrConstructor.constructorFactory by context.mapping.secondaryConstructorToFactory
|
private var IrConstructor.constructorFactory by context.mapping.secondaryConstructorToFactory
|
||||||
|
|
||||||
@@ -77,6 +81,7 @@ class ES6ConstructorLowering(val context: JsIrBackendContext) : DeclarationTrans
|
|||||||
statements.add(JsIrBuilder.buildReturn(symbol, selfReplacedConstructorCall, returnType))
|
statements.add(JsIrBuilder.buildReturn(symbol, selfReplacedConstructorCall, returnType))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
origin = ES6_SYNTHETIC_EXPORT_CONSTRUCTOR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-5
@@ -12,10 +12,7 @@ import org.jetbrains.kotlin.ir.IrStatement
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
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.ir.JsIrBuilder
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.JsAnnotations
|
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.MutableReference
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.irEmpty
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.mutableReferenceOf
|
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
|
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
@@ -35,12 +32,19 @@ class ES6PrimaryConstructorOptimizationLowering(private val context: JsIrBackend
|
|||||||
}
|
}
|
||||||
|
|
||||||
val irClass = declaration.parentAsClass
|
val irClass = declaration.parentAsClass
|
||||||
|
val defaultConstructor = context.findDefaultConstructorFor(irClass)
|
||||||
|
|
||||||
if (irClass.isExported(context)) {
|
if (irClass.isExported(context)) {
|
||||||
irClass.removeConstructorForExport()
|
irClass.removeConstructorForExport()
|
||||||
}
|
}
|
||||||
|
|
||||||
return listOf(declaration.convertToRegularConstructor(irClass))
|
val constructorReplacement = declaration.convertToRegularConstructor(irClass)
|
||||||
|
|
||||||
|
if (declaration == defaultConstructor) {
|
||||||
|
context.mapping.classToItsDefaultConstructor[irClass] = constructorReplacement
|
||||||
|
}
|
||||||
|
|
||||||
|
return listOf(constructorReplacement)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrFunction.convertToRegularConstructor(irClass: IrClass): IrConstructor {
|
private fun IrFunction.convertToRegularConstructor(irClass: IrClass): IrConstructor {
|
||||||
|
|||||||
+42
-20
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.compilationException
|
|||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.isAllowedFakeOverriddenDeclaration
|
import org.jetbrains.kotlin.ir.backend.js.export.isAllowedFakeOverriddenDeclaration
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.isOverriddenExported
|
import org.jetbrains.kotlin.ir.backend.js.export.isOverriddenExported
|
||||||
@@ -24,12 +25,15 @@ import org.jetbrains.kotlin.js.backend.ast.JsVars.JsVar
|
|||||||
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
import org.jetbrains.kotlin.js.common.isValidES5Identifier
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.butIf
|
import org.jetbrains.kotlin.utils.addToStdlib.butIf
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||||
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
import org.jetbrains.kotlin.utils.memoryOptimizedMap
|
||||||
import org.jetbrains.kotlin.utils.toSmartList
|
import org.jetbrains.kotlin.utils.toSmartList
|
||||||
|
|
||||||
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationContext) {
|
||||||
|
private val backendContext = context.staticContext.backendContext
|
||||||
|
|
||||||
private val perFile = context.staticContext.isPerFile
|
private val perFile = context.staticContext.isPerFile
|
||||||
private val es6mode = context.staticContext.backendContext.es6mode
|
private val es6mode = backendContext.es6mode
|
||||||
|
|
||||||
private val className = context.getNameForClass(irClass)
|
private val className = context.getNameForClass(irClass)
|
||||||
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
|
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
|
||||||
@@ -151,7 +155,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
property.isFakeOverride &&
|
property.isFakeOverride &&
|
||||||
!property.isAllowedFakeOverriddenDeclaration(context.staticContext.backendContext)
|
!property.isAllowedFakeOverriddenDeclaration(backendContext)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -170,8 +174,6 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
|
|
||||||
val overriddenSymbols = property.getter?.overriddenSymbols.orEmpty()
|
val overriddenSymbols = property.getter?.overriddenSymbols.orEmpty()
|
||||||
|
|
||||||
val backendContext = context.staticContext.backendContext
|
|
||||||
|
|
||||||
// Don't generate `defineProperty` if the property overrides a property from an exported class,
|
// Don't generate `defineProperty` if the property overrides a property from an exported class,
|
||||||
// because we've already generated `defineProperty` for the base class property.
|
// because we've already generated `defineProperty` for the base class property.
|
||||||
// In other words, we only want to generate `defineProperty` once for each property.
|
// In other words, we only want to generate `defineProperty` once for each property.
|
||||||
@@ -184,14 +186,14 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
|
|
||||||
val getterOverridesExternal = property.getter?.overridesExternal() == true
|
val getterOverridesExternal = property.getter?.overridesExternal() == true
|
||||||
val overriddenExportedGetter = !property.getter?.overriddenSymbols.isNullOrEmpty() &&
|
val overriddenExportedGetter = !property.getter?.overriddenSymbols.isNullOrEmpty() &&
|
||||||
property.getter?.isOverriddenExported(context.staticContext.backendContext) == true
|
property.getter?.isOverriddenExported(backendContext) == true
|
||||||
|
|
||||||
val noOverriddenExportedSetter = property.setter?.isOverriddenExported(context.staticContext.backendContext) == false
|
val noOverriddenExportedSetter = property.setter?.isOverriddenExported(backendContext) == false
|
||||||
|
|
||||||
val needsOverride = (overriddenExportedGetter && noOverriddenExportedSetter) ||
|
val needsOverride = (overriddenExportedGetter && noOverriddenExportedSetter) ||
|
||||||
property.isAllowedFakeOverriddenDeclaration(context.staticContext.backendContext)
|
property.isAllowedFakeOverriddenDeclaration(backendContext)
|
||||||
|
|
||||||
if (irClass.isExported(context.staticContext.backendContext) &&
|
if (irClass.isExported(backendContext) &&
|
||||||
(overriddenSymbols.isEmpty() || needsOverride) ||
|
(overriddenSymbols.isEmpty() || needsOverride) ||
|
||||||
hasOverriddenExportedInterfaceProperties ||
|
hasOverriddenExportedInterfaceProperties ||
|
||||||
getterOverridesExternal ||
|
getterOverridesExternal ||
|
||||||
@@ -213,7 +215,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
val getterForwarder = property.getter
|
val getterForwarder = property.getter
|
||||||
.takeIf { it.shouldExportAccessor(context.staticContext.backendContext) }
|
.takeIf { it.shouldExportAccessor(backendContext) }
|
||||||
.getOrGenerateIfFinalOrEs6Mode {
|
.getOrGenerateIfFinalOrEs6Mode {
|
||||||
propertyAccessorForwarder("getter forwarder") {
|
propertyAccessorForwarder("getter forwarder") {
|
||||||
JsReturn(JsInvocation(it))
|
JsReturn(JsInvocation(it))
|
||||||
@@ -221,7 +223,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
val setterForwarder = property.setter
|
val setterForwarder = property.setter
|
||||||
.takeIf { it.shouldExportAccessor(context.staticContext.backendContext) }
|
.takeIf { it.shouldExportAccessor(backendContext) }
|
||||||
.getOrGenerateIfFinalOrEs6Mode {
|
.getOrGenerateIfFinalOrEs6Mode {
|
||||||
val setterArgName = JsName("value", false)
|
val setterArgName = JsName("value", false)
|
||||||
propertyAccessorForwarder("setter forwarder") {
|
propertyAccessorForwarder("setter forwarder") {
|
||||||
@@ -266,7 +268,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
|
|
||||||
private fun IrSimpleFunction.isDefinedInsideExportedInterface(): Boolean {
|
private fun IrSimpleFunction.isDefinedInsideExportedInterface(): Boolean {
|
||||||
if (isJsExportIgnore() || correspondingPropertySymbol?.owner?.isJsExportIgnore() == true) return false
|
if (isJsExportIgnore() || correspondingPropertySymbol?.owner?.isJsExportIgnore() == true) return false
|
||||||
return (!isFakeOverride && parentClassOrNull.isExportedInterface(context.staticContext.backendContext)) ||
|
return (!isFakeOverride && parentClassOrNull.isExportedInterface(backendContext)) ||
|
||||||
overriddenSymbols.any { it.owner.isDefinedInsideExportedInterface() }
|
overriddenSymbols.any { it.owner.isDefinedInsideExportedInterface() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +283,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
|
|
||||||
private fun IrSimpleFunction.generateAssignmentIfMangled(memberName: JsName) {
|
private fun IrSimpleFunction.generateAssignmentIfMangled(memberName: JsName) {
|
||||||
if (
|
if (
|
||||||
irClass.isExported(context.staticContext.backendContext) &&
|
irClass.isExported(backendContext) &&
|
||||||
visibility.isPublicAPI && hasMangledName() &&
|
visibility.isPublicAPI && hasMangledName() &&
|
||||||
correspondingPropertySymbol == null
|
correspondingPropertySymbol == null
|
||||||
) {
|
) {
|
||||||
@@ -370,24 +372,33 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateSetMetadataCall(): JsStatement {
|
private fun generateSetMetadataCall(): JsStatement {
|
||||||
val setMetadataFor = context.staticContext.backendContext.intrinsics.setMetadataForSymbol.owner
|
val setMetadataFor = backendContext.intrinsics.setMetadataForSymbol.owner
|
||||||
|
|
||||||
val ctor = classNameRef
|
val ctor = classNameRef
|
||||||
val parent = baseClassRef?.takeIf { !es6mode }
|
val parent = baseClassRef?.takeIf { !es6mode }
|
||||||
val name = generateSimpleName()
|
val name = generateSimpleName()
|
||||||
val interfaces = generateInterfacesList()
|
val interfaces = generateInterfacesList()
|
||||||
val metadataConstructor = getMetadataConstructor()
|
val metadataConstructor = getMetadataConstructor()
|
||||||
|
val defaultConstructor = runIf(irClass.isClass, ::findDefaultConstructor)
|
||||||
val associatedObjectKey = generateAssociatedObjectKey()
|
val associatedObjectKey = generateAssociatedObjectKey()
|
||||||
val associatedObjects = generateAssociatedObjects()
|
val associatedObjects = generateAssociatedObjects()
|
||||||
val suspendArity = generateSuspendArity()
|
val suspendArity = generateSuspendArity()
|
||||||
|
|
||||||
val undefined = context.staticContext.backendContext.getVoid().accept(IrElementToJsExpressionTransformer(), context)
|
|
||||||
|
|
||||||
return JsInvocation(
|
return JsInvocation(
|
||||||
JsNameRef(context.getNameForStaticFunction(setMetadataFor)),
|
JsNameRef(context.getNameForStaticFunction(setMetadataFor)),
|
||||||
listOf(ctor, name, metadataConstructor, parent, interfaces, associatedObjectKey, associatedObjects, suspendArity)
|
listOf(
|
||||||
|
ctor,
|
||||||
|
name,
|
||||||
|
metadataConstructor,
|
||||||
|
parent,
|
||||||
|
interfaces,
|
||||||
|
defaultConstructor,
|
||||||
|
associatedObjectKey,
|
||||||
|
associatedObjects,
|
||||||
|
suspendArity
|
||||||
|
)
|
||||||
.dropLastWhile { it == null }
|
.dropLastWhile { it == null }
|
||||||
.memoryOptimizedMap { it ?: undefined }
|
.memoryOptimizedMap { it ?: jsUndefined }
|
||||||
).makeStmt()
|
).makeStmt()
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -408,7 +419,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getMetadataConstructor(): JsNameRef {
|
private fun getMetadataConstructor(): JsNameRef {
|
||||||
val metadataConstructorSymbol = with(context.staticContext.backendContext.intrinsics) {
|
val metadataConstructorSymbol = with(backendContext.intrinsics) {
|
||||||
when {
|
when {
|
||||||
irClass.isInterface -> metadataInterfaceConstructorSymbol
|
irClass.isInterface -> metadataInterfaceConstructorSymbol
|
||||||
irClass.isObject -> metadataObjectConstructorSymbol
|
irClass.isObject -> metadataObjectConstructorSymbol
|
||||||
@@ -428,8 +439,19 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
return JsArrayLiteral(listRef.toSmartList())
|
return JsArrayLiteral(listRef.toSmartList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun findDefaultConstructor(): JsNameRef? {
|
||||||
|
return when (val defaultConstructor = backendContext.findDefaultConstructorFor(irClass)) {
|
||||||
|
is IrConstructor -> context.getNameForConstructor(defaultConstructor).makeRef()
|
||||||
|
is IrSimpleFunction -> when {
|
||||||
|
es6mode -> JsNameRef(context.getNameForMemberFunction(defaultConstructor), classNameRef)
|
||||||
|
else -> context.getNameForStaticFunction(defaultConstructor).makeRef()
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun generateSuspendArity(): JsArrayLiteral? {
|
private fun generateSuspendArity(): JsArrayLiteral? {
|
||||||
val invokeFunctions = context.staticContext.backendContext.mapping.suspendArityStore[irClass] ?: return null
|
val invokeFunctions = backendContext.mapping.suspendArityStore[irClass] ?: return null
|
||||||
val arity = invokeFunctions
|
val arity = invokeFunctions
|
||||||
.map { it.valueParameters.size }
|
.map { it.valueParameters.size }
|
||||||
.distinct()
|
.distinct()
|
||||||
@@ -447,7 +469,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
val annotationClass = annotation.symbol.owner.constructedClass
|
val annotationClass = annotation.symbol.owner.constructedClass
|
||||||
context.getAssociatedObjectKey(annotationClass)?.let { key ->
|
context.getAssociatedObjectKey(annotationClass)?.let { key ->
|
||||||
annotation.associatedObject()?.let { obj ->
|
annotation.associatedObject()?.let { obj ->
|
||||||
context.staticContext.backendContext.mapping.objectToGetInstanceFunction[obj]?.let { factory ->
|
backendContext.mapping.objectToGetInstanceFunction[obj]?.let { factory ->
|
||||||
JsPropertyInitializer(JsIntLiteral(key), context.staticContext.getNameForStaticFunction(factory).makeRef())
|
JsPropertyInitializer(JsIntLiteral(key), context.staticContext.getNameForStaticFunction(factory).makeRef())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -86,6 +86,8 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
|||||||
|
|
||||||
prefixOp(intrinsics.jsTypeOf, JsUnaryOperator.TYPEOF)
|
prefixOp(intrinsics.jsTypeOf, JsUnaryOperator.TYPEOF)
|
||||||
|
|
||||||
|
add(intrinsics.jsIsEs6) { _, _ -> JsBooleanLiteral(backendContext.es6mode) }
|
||||||
|
|
||||||
add(intrinsics.jsObjectCreateSymbol) { call, context ->
|
add(intrinsics.jsObjectCreateSymbol) { call, context ->
|
||||||
val classToCreate = call.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
val classToCreate = call.getTypeArgument(0)!!.classifierOrFail.owner as IrClass
|
||||||
val className = classToCreate.getClassRef(context.staticContext)
|
val className = classToCreate.getClassRef(context.staticContext)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.parents
|
import org.jetbrains.kotlin.backend.common.lower.parents
|
||||||
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.descriptors.isClass
|
import org.jetbrains.kotlin.descriptors.isClass
|
||||||
import org.jetbrains.kotlin.descriptors.isInterface
|
import org.jetbrains.kotlin.descriptors.isInterface
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
@@ -14,6 +15,10 @@ import org.jetbrains.kotlin.ir.backend.js.JsLoweredDeclarationOrigin
|
|||||||
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
||||||
import org.jetbrains.kotlin.ir.backend.js.export.isExported
|
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.ir.JsIrBuilder
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.isBoxParameter
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.isEs6ConstructorReplacement
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.isSyntheticConstructorForExport
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.isSyntheticPrimaryConstructor
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
@@ -25,13 +30,10 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
|||||||
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
import org.jetbrains.kotlin.js.backend.ast.JsNameRef
|
||||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
|
||||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
|
||||||
import org.jetbrains.kotlin.ir.util.isEnumClass
|
|
||||||
import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.utils.filterIsInstanceAnd
|
||||||
|
|
||||||
fun IrClass.jsConstructorReference(context: JsIrBackendContext): IrExpression {
|
fun IrClass.jsConstructorReference(context: JsIrBackendContext): IrExpression {
|
||||||
return JsIrBuilder.buildCall(context.intrinsics.jsClass, origin = JsStatementOrigins.CLASS_REFERENCE)
|
return JsIrBuilder.buildCall(context.intrinsics.jsClass, origin = JsStatementOrigins.CLASS_REFERENCE)
|
||||||
@@ -127,3 +129,9 @@ fun IrDeclaration.isObjectInstanceField(): Boolean {
|
|||||||
fun IrField.isObjectInstanceField(): Boolean {
|
fun IrField.isObjectInstanceField(): Boolean {
|
||||||
return origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE
|
return origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun JsIrBackendContext.findDefaultConstructorFor(irClass: IrClass): IrFunction? {
|
||||||
|
return mapping.classToItsDefaultConstructor[irClass]?.let {
|
||||||
|
mapping.secondaryConstructorToFactory[it] ?: it
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -9914,6 +9914,18 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
|||||||
runTest("js/js.translator/testData/box/reflection/classJsName.kt");
|
runTest("js/js.translator/testData/box/reflection/classJsName.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("createInstance.kt")
|
||||||
|
public void testCreateInstance() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/reflection/createInstance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("createInstanceByInstance.kt")
|
||||||
|
public void testCreateInstanceByInstance() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/reflection/createInstanceByInstance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("external.kt")
|
@TestMetadata("external.kt")
|
||||||
public void testExternal() throws Exception {
|
public void testExternal() throws Exception {
|
||||||
|
|||||||
+12
@@ -10020,6 +10020,18 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
|||||||
runTest("js/js.translator/testData/box/reflection/classJsName.kt");
|
runTest("js/js.translator/testData/box/reflection/classJsName.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("createInstance.kt")
|
||||||
|
public void testCreateInstance() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/reflection/createInstance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("createInstanceByInstance.kt")
|
||||||
|
public void testCreateInstanceByInstance() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/reflection/createInstanceByInstance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("external.kt")
|
@TestMetadata("external.kt")
|
||||||
public void testExternal() throws Exception {
|
public void testExternal() throws Exception {
|
||||||
|
|||||||
+12
@@ -9914,6 +9914,18 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
runTest("js/js.translator/testData/box/reflection/classJsName.kt");
|
runTest("js/js.translator/testData/box/reflection/classJsName.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("createInstance.kt")
|
||||||
|
public void testCreateInstance() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/reflection/createInstance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("createInstanceByInstance.kt")
|
||||||
|
public void testCreateInstanceByInstance() throws Exception {
|
||||||
|
runTest("js/js.translator/testData/box/reflection/createInstanceByInstance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("external.kt")
|
@TestMetadata("external.kt")
|
||||||
public void testExternal() throws Exception {
|
public void testExternal() throws Exception {
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// IGNORE_BACKEND: JS
|
||||||
|
// KJS_WITH_FULL_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.reflect.createInstance
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
// Good classes
|
||||||
|
|
||||||
|
class Simple
|
||||||
|
class PrimaryWithDefaults(val d1: String = "d1", val d2: Int = 2)
|
||||||
|
class Secondary(val s: String) {
|
||||||
|
constructor() : this("s")
|
||||||
|
}
|
||||||
|
class SecondaryWithDefaults(val s: String) {
|
||||||
|
constructor(x: Int = 0) : this(x.toString())
|
||||||
|
}
|
||||||
|
class SecondaryWithDefaultsNoPrimary {
|
||||||
|
constructor(x: Int) {}
|
||||||
|
constructor(s: String = "") {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad classes
|
||||||
|
|
||||||
|
class NoNoArgConstructor(val s: String) {
|
||||||
|
constructor(x: Int) : this(x.toString())
|
||||||
|
}
|
||||||
|
class NoArgAndDefault() {
|
||||||
|
constructor(x: Int = 0) : this()
|
||||||
|
}
|
||||||
|
class DefaultPrimaryAndDefaultSecondary(val s: String = "") {
|
||||||
|
constructor(x: Int = 0) : this(x.toString())
|
||||||
|
}
|
||||||
|
class SeveralDefaultSecondaries {
|
||||||
|
constructor(x: Int = 0) {}
|
||||||
|
constructor(s: String = "") {}
|
||||||
|
constructor(d: Double = 3.14) {}
|
||||||
|
}
|
||||||
|
class PrivateConstructor private constructor()
|
||||||
|
object Object
|
||||||
|
interface Interface
|
||||||
|
enum class EnumFoo { A, B }
|
||||||
|
|
||||||
|
abstract class AbstractClass
|
||||||
|
|
||||||
|
sealed class SealedClass
|
||||||
|
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
inline fun <reified T : Any> test() {
|
||||||
|
val instance = T::class.createInstance()
|
||||||
|
assertTrue(instance is T)
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Any> testFail() {
|
||||||
|
try {
|
||||||
|
T::class.createInstance()
|
||||||
|
fail("createInstance should have failed on ${T::class}")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
test<Any>()
|
||||||
|
test<Simple>()
|
||||||
|
test<PrimaryWithDefaults>()
|
||||||
|
test<Secondary>()
|
||||||
|
test<SecondaryWithDefaults>()
|
||||||
|
test<SecondaryWithDefaultsNoPrimary>()
|
||||||
|
|
||||||
|
testFail<NoNoArgConstructor>()
|
||||||
|
testFail<NoArgAndDefault>()
|
||||||
|
testFail<DefaultPrimaryAndDefaultSecondary>()
|
||||||
|
testFail<SeveralDefaultSecondaries>()
|
||||||
|
testFail<PrivateConstructor>()
|
||||||
|
testFail<Object>()
|
||||||
|
testFail<Interface>()
|
||||||
|
testFail<EnumFoo>()
|
||||||
|
testFail<AbstractClass>()
|
||||||
|
testFail<SealedClass>()
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// IGNORE_BACKEND: JS
|
||||||
|
// KJS_WITH_FULL_RUNTIME
|
||||||
|
|
||||||
|
import kotlin.reflect.createInstance
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
// Good classes
|
||||||
|
|
||||||
|
class Simple
|
||||||
|
class PrimaryWithDefaults(val d1: String = "d1", val d2: Int = 2)
|
||||||
|
class Secondary(val s: String) {
|
||||||
|
constructor() : this("s")
|
||||||
|
}
|
||||||
|
class SecondaryWithDefaults(val s: String) {
|
||||||
|
constructor(x: Int = 0) : this(x.toString())
|
||||||
|
}
|
||||||
|
class SecondaryWithDefaultsNoPrimary {
|
||||||
|
constructor(x: Int) {}
|
||||||
|
constructor(s: String = "") {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad classes
|
||||||
|
|
||||||
|
class NoNoArgConstructor(val s: String) {
|
||||||
|
constructor(x: Int) : this(x.toString())
|
||||||
|
}
|
||||||
|
class NoArgAndDefault() {
|
||||||
|
constructor(x: Int = 0) : this()
|
||||||
|
}
|
||||||
|
class DefaultPrimaryAndDefaultSecondary(val s: String = "") {
|
||||||
|
constructor(x: Int = 0) : this(x.toString())
|
||||||
|
}
|
||||||
|
class SeveralDefaultSecondaries {
|
||||||
|
constructor(x: Int = 0) {}
|
||||||
|
constructor(s: String = "") {}
|
||||||
|
constructor(d: Double = 3.14) {}
|
||||||
|
}
|
||||||
|
class PrivateConstructor private constructor() {
|
||||||
|
companion object {
|
||||||
|
fun create() = PrivateConstructor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object Object
|
||||||
|
enum class EnumFoo { A, B }
|
||||||
|
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
inline fun <T : Any> testInstance(x: T) {
|
||||||
|
val kclass = x::class
|
||||||
|
val anotherInstance = kclass.createInstance()
|
||||||
|
assertTrue(kclass.isInstance(x) && kclass.isInstance(anotherInstance))
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <T : Any> testInstanceFail(x: T) {
|
||||||
|
try {
|
||||||
|
val kclass = x::class
|
||||||
|
kclass.createInstance()
|
||||||
|
fail("createInstance should have failed on $kclass")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
testInstance(Simple())
|
||||||
|
testInstance(PrimaryWithDefaults("d2"))
|
||||||
|
testInstance(Secondary("test"))
|
||||||
|
testInstance(SecondaryWithDefaults("test"))
|
||||||
|
testInstance(SecondaryWithDefaultsNoPrimary(2))
|
||||||
|
|
||||||
|
testInstanceFail(NoNoArgConstructor(4))
|
||||||
|
testInstanceFail(NoArgAndDefault())
|
||||||
|
testInstanceFail(DefaultPrimaryAndDefaultSecondary(4))
|
||||||
|
testInstanceFail(SeveralDefaultSecondaries(4))
|
||||||
|
testInstanceFail(PrivateConstructor.create())
|
||||||
|
testInstanceFail(Object)
|
||||||
|
testInstanceFail(EnumFoo.A)
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -216,6 +216,15 @@ public final annotation class ExperimentalJsFileName : kotlin.Annotation {
|
|||||||
public constructor ExperimentalJsFileName()
|
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 external object JSON {
|
||||||
public final fun <T> parse(text: kotlin.String): T
|
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
|
@kotlin.internal.LowPriorityInOverloadResolution
|
||||||
public fun <T : kotlin.Any> kotlin.reflect.KClass<T>.cast(value: kotlin.Any?): T
|
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
|
@kotlin.reflect.ExperimentalAssociatedObjects
|
||||||
public inline fun <reified T : kotlin.Annotation> kotlin.reflect.KClass<*>.findAssociatedObject(): kotlin.Any?
|
public inline fun <reified T : kotlin.Annotation> kotlin.reflect.KClass<*>.findAssociatedObject(): kotlin.Any?
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package kotlin.js
|
package kotlin.js
|
||||||
|
|
||||||
import kotlin.annotation.AnnotationTarget.*
|
import kotlin.annotation.AnnotationTarget.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives a declaration (a function, a property or a class) specific name in JavaScript.
|
* 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()
|
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
|
internal typealias BitMask = IntArray
|
||||||
|
|
||||||
private fun bitMaskWith(activeBit: Int): BitMask {
|
private fun bitMaskWith(activeBit: Int): BitMask {
|
||||||
val intArray = IntArray((activeBit shr 5) + 1)
|
|
||||||
val numberIndex = activeBit shr 5
|
val numberIndex = activeBit shr 5
|
||||||
|
val intArray = IntArray(numberIndex + 1)
|
||||||
val positionInNumber = activeBit and 31
|
val positionInNumber = activeBit and 31
|
||||||
val numberWithSettledBit = 1 shl positionInNumber
|
val numberWithSettledBit = 1 shl positionInNumber
|
||||||
intArray[numberIndex] = intArray[numberIndex] or numberWithSettledBit
|
intArray[numberIndex] = intArray[numberIndex] or numberWithSettledBit
|
||||||
|
|||||||
@@ -228,3 +228,6 @@ internal fun jsDelete(e: Any?)
|
|||||||
|
|
||||||
@JsIntrinsic
|
@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(
|
internal fun setMetadataFor(
|
||||||
ctor: Ctor,
|
ctor: Ctor,
|
||||||
name: String?,
|
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?,
|
parent: Ctor?,
|
||||||
interfaces: Array<dynamic>?,
|
interfaces: Array<dynamic>?,
|
||||||
|
defaultConstructor: dynamic,
|
||||||
associatedObjectKey: Number?,
|
associatedObjectKey: Number?,
|
||||||
associatedObjects: dynamic,
|
associatedObjects: dynamic,
|
||||||
suspendArity: Array<Int>?
|
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
|
ctor.`$metadata$` = metadata
|
||||||
|
|
||||||
if (interfaces != null) {
|
if (interfaces != null) {
|
||||||
@@ -45,16 +46,34 @@ private fun generateInterfaceId(): Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun interfaceMeta(name: String?, associatedObjectKey: Number?, associatedObjects: dynamic, suspendArity: Array<Int>?): Metadata {
|
internal fun interfaceMeta(
|
||||||
return createMetadata("interface", name, associatedObjectKey, associatedObjects, suspendArity, generateInterfaceId())
|
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 {
|
internal fun objectMeta(
|
||||||
return createMetadata("object", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
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 {
|
internal fun classMeta(
|
||||||
return createMetadata("class", name, associatedObjectKey, associatedObjects, suspendArity, null)
|
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
|
// 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(
|
private fun createMetadata(
|
||||||
kind: String,
|
kind: String,
|
||||||
name: String?,
|
name: String?,
|
||||||
|
defaultConstructor: dynamic,
|
||||||
associatedObjectKey: Number?,
|
associatedObjectKey: Number?,
|
||||||
associatedObjects: dynamic,
|
associatedObjects: dynamic,
|
||||||
suspendArity: Array<Int>?,
|
suspendArity: Array<Int>?,
|
||||||
@@ -75,6 +95,7 @@ private fun createMetadata(
|
|||||||
associatedObjects: associatedObjects,
|
associatedObjects: associatedObjects,
|
||||||
suspendArity: suspendArity,
|
suspendArity: suspendArity,
|
||||||
${'$'}kClass$: undef,
|
${'$'}kClass$: undef,
|
||||||
|
defaultConstructor: defaultConstructor,
|
||||||
iid: iid
|
iid: iid
|
||||||
})""")
|
})""")
|
||||||
}
|
}
|
||||||
@@ -90,6 +111,7 @@ internal external interface Metadata {
|
|||||||
val iid: Int?
|
val iid: Int?
|
||||||
|
|
||||||
var `$kClass$`: dynamic
|
var `$kClass$`: dynamic
|
||||||
|
val defaultConstructor: dynamic
|
||||||
|
|
||||||
var errorInfo: Int? // Bits set for overridden properties: "message" => 0x1, "cause" => 0x2
|
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 {
|
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>>(
|
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()")
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -3387,6 +3387,9 @@ public abstract interface annotation class kotlin/js/ExperimentalJsExport : java
|
|||||||
public abstract interface annotation class kotlin/js/ExperimentalJsFileName : java/lang/annotation/Annotation {
|
public abstract interface annotation class kotlin/js/ExperimentalJsFileName : java/lang/annotation/Annotation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract interface annotation class kotlin/js/ExperimentalJsReflectionCreateInstance : java/lang/annotation/Annotation {
|
||||||
|
}
|
||||||
|
|
||||||
public final class kotlin/jvm/JvmClassMappingKt {
|
public final class kotlin/jvm/JvmClassMappingKt {
|
||||||
public static final fun getAnnotationClass (Ljava/lang/annotation/Annotation;)Lkotlin/reflect/KClass;
|
public static final fun getAnnotationClass (Ljava/lang/annotation/Annotation;)Lkotlin/reflect/KClass;
|
||||||
public static final fun getJavaClass (Ljava/lang/Object;)Ljava/lang/Class;
|
public static final fun getJavaClass (Ljava/lang/Object;)Ljava/lang/Class;
|
||||||
|
|||||||
Reference in New Issue
Block a user