[Wasm, JS] refactor static initialization check to the following: 1. check for successfull initialization 2. check for previously failed initialization (with obtaining KClass and throwing staticInitializationFailure) 3. try to initialize So, we do not create KClass instances on successful path Fixes ^KT-88002
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/BackendJsSymbols.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/BackendJsSymbols.kt index 0f4b651..b1751c9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/BackendJsSymbols.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/BackendJsSymbols.kt
@@ -522,7 +522,7 @@ val constructCallableReferenceSymbol by CallableIds.constructCallableReference.functionSymbol() - val checkStaticInitializationState by CallableIds.checkStaticInitializationState.functionSymbol() + val staticInitializationFailureWithClassName by CallableIds.staticInitializationFailureWithClassName.functionSymbol() } private object ClassIds { @@ -794,5 +794,5 @@ val test = CallableId(StandardClassIds.BASE_TEST_PACKAGE, Name.identifier("test")) val suite = CallableId(StandardClassIds.BASE_TEST_PACKAGE, Name.identifier("suite")) val EmptyContinuation = CallableId(FqName.fromSegments(listOf("kotlin", "coroutines", "js", "internal")), Name.identifier("EmptyContinuation")) - val checkStaticInitializationState = "checkStaticInitializationState".jsCallableId + val staticInitializationFailureWithClassName = "staticInitializationFailureWithClassName".jsCallableId }
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsInitializersLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsInitializersLowering.kt index cca5141..e53f73d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsInitializersLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/JsInitializersLowering.kt
@@ -25,6 +25,6 @@ context, shouldEraseFieldInitializer = { it.correspondingPropertySymbol?.owner?.isConst != true && - it.origin != WebStaticInitializersDeclarationLowering.STATIC_CLASS_INITIALIZER // We need to preserve initializers for `static_init_called` fields (KT-89144). + it.origin != WebStaticInitializersDeclarationLowering.STATIC_CLASS_INITIALIZER // We need to preserve initializers for `static_init_state` fields (KT-89144). } )
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/LazyGlobalInitializationGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/LazyGlobalInitializationGenerator.kt index af162a5..852d35f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/LazyGlobalInitializationGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/LazyGlobalInitializationGenerator.kt
@@ -8,6 +8,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irCatch import org.jetbrains.kotlin.backend.common.lower.irIfThen +import org.jetbrains.kotlin.backend.common.lower.irNot import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.ir.IrStatement @@ -20,7 +21,6 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetField import org.jetbrains.kotlin.ir.types.IrType @@ -34,12 +34,36 @@ protected abstract val backendContext: JsCommonBackendContext private object InitializationState { - const val UNINITIALIZED: Int = 0 - const val INITIALIZED: Int = 1 + const val UNINITIALIZED: Int = 1 + const val INITIALIZED: Int = 0 const val ERROR: Int = 2 } - protected abstract fun IrBuilderWithScope.generateStaticInitializationStateCheck(getStateField: IrGetField, klass: IrClass?): IrCall + protected abstract fun IrBuilderWithScope.staticInitializationFailureBranch(klass: IrClass?): IrExpression + + private fun IrBuilderWithScope.generateStaticInitializationStateChecks( + getStateField: IrGetField, + klass: IrClass? + ): List<IrStatement> { + + val state = scope.createTemporaryVariable( + getStateField, + nameHint = "state", + inventUniqueName = false, + ) + + return listOf( + state, + irIfThen( + irNot(irGet(state)), // state == InitializationState.INITIALIZED + irReturnUnit() + ), + irIfThen( + irEqeqeq(irGet(state), irInt(InitializationState.ERROR)), + staticInitializationFailureBranch(klass) + ) + ) + } protected open fun IrBuilderWithScope.undefinedOrNull(): IrExpression = irNull() @@ -82,8 +106,7 @@ val builder = backendContext.createIrBuilder(symbol) body = backendContext.irFactory.createBlockBody(startOffset, endOffset) { with(builder) { - val stateCheck = generateStaticInitializationStateCheck(irGetField(null, stateField), klass) - statements += irIfThen(stateCheck, irReturnUnit()) + statements += generateStaticInitializationStateChecks(irGetField(null, stateField), klass) statements += irSetField(null, stateField, irInt(InitializationState.INITIALIZED)) val allInitializers = irComposite { beforeAll() @@ -115,10 +138,9 @@ } class JsLazyGlobalInitializationGenerator(override val backendContext: JsIrBackendContext) : LazyGlobalInitializationGenerator() { - override fun IrBuilderWithScope.generateStaticInitializationStateCheck(getStateField: IrGetField, klass: IrClass?): IrCall = - irCall(backendContext.symbols.checkStaticInitializationState).apply { - arguments[0] = getStateField - arguments[1] = klass?.jsConstructorReference(backendContext) ?: backendContext.getVoid() + override fun IrBuilderWithScope.staticInitializationFailureBranch(klass: IrClass?): IrExpression = + irCall(backendContext.symbols.staticInitializationFailureWithClassName).apply { + arguments[0] = klass?.jsConstructorReference(backendContext) ?: backendContext.getVoid() } override fun IrBuilderWithScope.undefinedOrNull(): IrExpression = backendContext.getVoid()
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersDeclarationLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersDeclarationLowering.kt index f625a91..6a99b6a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersDeclarationLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersDeclarationLowering.kt
@@ -56,17 +56,20 @@ * ```kotlin * class Foo { * companion { - * var static_init_called = 0 + * var static_init_state = 1 * static_init() { - * if (checkInitializationState(static_init_called, Foo::class)) return - * static_init_called = 1 + * if (!static_init_state) return + * if (static_init_state == 2) { + * staticInitializationFailureWithClassName(Foo::class) + * } + * static_init_state = 0 * try { * first = initFirst() * second = initSecond() * third = initThird() * } catch (reason: Throwable) { - * static_init_called = 2 - * kotlint.internal.staticInitializationFailure(reason, null) + * static_init_state = 2 + * kotlin.internal.staticInitializationFailure(reason, null) * } * } * } @@ -88,7 +91,7 @@ val STATIC_CLASS_INITIALIZER by IrDeclarationOriginImpl.Synthetic const val STATIC_INIT_FUNCTION_NAME = "static_init" - const val STATIC_INIT_CALLED_PROPERTY_NAME = "static_init_called" + const val STATIC_INIT_STATE_PROPERTY_NAME = "static_init_state" } protected abstract val context: JsCommonBackendContext @@ -180,10 +183,10 @@ // Both declarations must be created within the *same* restrictTo block: the stage controller resets its signature index // on every restrictTo call, so creating them in two separate blocks gives both the very same // `IdSignature.LoweredDeclarationSignature`. Cross-file references are resolved by the rendered signature, so the - // collision makes a reference to `static_init` resolve to `static_init_called` instead. - val [staticInitCalledField, staticInitFunction] = context.irFactory.stageController.restrictTo(container) { + // collision makes a reference to `static_init` resolve to `static_init_state` instead. + val [staticInitStateField, staticInitFunction] = context.irFactory.stageController.restrictTo(container) { val stateField = initializationGenerator.createStateField( - name = Name.identifier(STATIC_INIT_CALLED_PROPERTY_NAME), + name = Name.identifier(STATIC_INIT_STATE_PROPERTY_NAME), origin = STATIC_CLASS_INITIALIZER, ).apply { parent = container @@ -212,7 +215,7 @@ // Adding static_init declaration after adding its usages to make sure we don't insert usages inside static_init itself container.staticInitFunction = staticInitFunction container.companionObject()?.staticInitFunction = staticInitFunction - container.declarations.addAll(0, listOf(staticInitCalledField, staticInitFunction)) + container.declarations.addAll(0, listOf(staticInitStateField, staticInitFunction)) } private fun IrClass.createInitializer(declaration: IrDeclaration, field: IrField, initializer: IrExpression): IrSetField =
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersUsageLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersUsageLowering.kt index 2426c1f..da4dba4 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersUsageLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/WebStaticInitializersUsageLowering.kt
@@ -34,13 +34,21 @@ * ```kotlin * class Foo { * companion { - * var static_init_called = false + * var static_init_state = 1 * static_init() { - * if (static_init_called) return - * static_init_called = true - * first = initFirst() - * second = initSecond() - * third = initThird() + * if (!static_init_state) return + * if (static_init_state == 2) { + * staticInitializationFailureWithClassName(Foo::class) + * } + * static_init_state = 0 + * try { + * first = initFirst() + * second = initSecond() + * third = initThird() + * } catch (reason: Throwable) { + * static_init_state = 2 + * kotlin.internal.staticInitializationFailure(reason, null) + * } * } * } * companion { @@ -62,10 +70,10 @@ * static_init() * } * companion { - * var static_init_called = false + * var static_init_state = 1 * static_init() { - * static_init_called = true - * // ... + * if (!static_init_state) return + * ... * } * } * companion {
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/BackendWasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/BackendWasmSymbols.kt index fdaafa7..11351d5 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/BackendWasmSymbols.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/BackendWasmSymbols.kt
@@ -317,7 +317,7 @@ val wasmAnyRefClass = ClassIds.anyref.classSymbol() - val checkStaticInitializationState by CallableIds.checkStaticInitializationState.functionSymbol() + val staticInitializationFailureWithClassName by CallableIds.staticInitializationFailureWithClassName.functionSymbol() inner class JsInteropAdapters { val kotlinToJsStringAdapter by CallableIds.kotlinToJsStringAdapter.functionSymbol() @@ -678,7 +678,7 @@ val js = CallableId(JsStandardClassIds.BASE_JS_PACKAGE, Name.identifier("js")) val registerRootSuiteBlock = CallableId(StandardClassIds.BASE_TEST_PACKAGE, Name.identifier("registerRootSuiteBlock")) val runRootSuites = CallableId(StandardClassIds.BASE_TEST_PACKAGE, Name.identifier("runRootSuites")) - val checkStaticInitializationState = "checkStaticInitializationState".wasmCallableId + val staticInitializationFailureWithClassName = "staticInitializationFailureWithClassName".wasmCallableId val cabiRealloc = "cabi_realloc".wasmCallableId }
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmInitializersLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmInitializersLowering.kt index ebb3805a..228dffc 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmInitializersLowering.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmInitializersLowering.kt
@@ -25,6 +25,6 @@ context, shouldEraseFieldInitializer = { it.correspondingPropertySymbol?.owner?.isConst != true && - it.origin != WebStaticInitializersDeclarationLowering.STATIC_CLASS_INITIALIZER // We need to preserve initializers for `static_init_called` fields (KT-89144). + it.origin != WebStaticInitializersDeclarationLowering.STATIC_CLASS_INITIALIZER // We need to preserve initializers for `static_init_state` fields (KT-89144). } )
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmLazyGlobalInitializationGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmLazyGlobalInitializationGenerator.kt index aa96b15..125bc7c 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmLazyGlobalInitializationGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmLazyGlobalInitializationGenerator.kt
@@ -12,14 +12,12 @@ import org.jetbrains.kotlin.ir.builders.irNull import org.jetbrains.kotlin.ir.builders.kClassReference import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrGetField +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.types.starProjectedType class WasmLazyGlobalInitializationGenerator(override val backendContext: WasmBackendContext) : LazyGlobalInitializationGenerator() { - override fun IrBuilderWithScope.generateStaticInitializationStateCheck(getStateField: IrGetField, klass: IrClass?): IrCall = - irCall(backendContext.symbols.checkStaticInitializationState).apply { - arguments[0] = getStateField - arguments[1] = klass?.let { kClassReference(it.symbol.starProjectedType) } ?: irNull() + override fun IrBuilderWithScope.staticInitializationFailureBranch(klass: IrClass?): IrExpression = + irCall(backendContext.symbols.staticInitializationFailureWithClassName).apply { + arguments[0] = klass?.let { kClassReference(it.symbol.starProjectedType) } ?: irNull() } }
diff --git a/libraries/stdlib/js/runtime/staticInitialization.kt b/libraries/stdlib/js/runtime/staticInitialization.kt index d7a3c9b..4b6e07b 100644 --- a/libraries/stdlib/js/runtime/staticInitialization.kt +++ b/libraries/stdlib/js/runtime/staticInitialization.kt
@@ -11,6 +11,7 @@ private const val INITIALIZATION_STATE_INITIALIZED: Int = 1 private const val INITIALIZATION_STATE_ERROR: Int = 2 +// TODO: remove after bootstrap KT-89126 @UsedFromCompilerGeneratedCode internal fun checkStaticInitializationState(state: Int, ctor: Ctor?): Boolean { if (state == INITIALIZATION_STATE_ERROR) { @@ -18,3 +19,8 @@ } return state == INITIALIZATION_STATE_INITIALIZED } + +@UsedFromCompilerGeneratedCode +internal fun staticInitializationFailureWithClassName(ctor: Ctor?): Nothing { + staticInitializationFailure(null, ctor?.`$metadata$`?.simpleName) +}
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ThrowHelpers.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ThrowHelpers.kt index 9e5572f..7cb0662 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ThrowHelpers.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/ThrowHelpers.kt
@@ -73,6 +73,7 @@ private const val INITIALIZATION_STATE_INITIALIZED: Int = 1 private const val INITIALIZATION_STATE_ERROR: Int = 2 +// TODO: Remove after bootstrap KT-89126 @UsedFromCompilerGeneratedCode internal fun checkStaticInitializationState(state: Int, klass: KClass<*>?): Boolean { if (state == INITIALIZATION_STATE_ERROR) { @@ -80,3 +81,12 @@ } return state == INITIALIZATION_STATE_INITIALIZED } + +@UsedFromCompilerGeneratedCode +internal fun staticInitializationFailureWithClassName(klass: KClass<*>?): Nothing { + var className: String? = null + if (klass != null) { + className = klass.qualifiedName ?: klass.simpleName + } + staticInitializationFailure(null, className) +}