[JS IR] Add annotation for eager property initialization
This commit is contained in:
@@ -282,6 +282,8 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
||||
val doNotIntrinsifyAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("DoNotIntrinsify"))
|
||||
val jsFunAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsFun"))
|
||||
|
||||
val jsEagerInitializationAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsEagerInitialization"))
|
||||
|
||||
// TODO move CharSequence-related stiff to IntrinsifyCallsLowering
|
||||
val charSequenceClassSymbol = context.symbolTable.referenceClass(context.getClass(FqName("kotlin.CharSequence")))
|
||||
val charSequenceLengthPropertyGetterSymbol by context.lazy2 {
|
||||
|
||||
+17
-8
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrElementBase
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
@@ -51,7 +52,7 @@ class PropertyLazyInitLowering(
|
||||
if (container !is IrField && container !is IrSimpleFunction && container !is IrProperty)
|
||||
return
|
||||
|
||||
if (!container.isCompatibleDeclaration()) return
|
||||
if (!container.isCompatibleDeclaration(context)) return
|
||||
|
||||
val file = container.parent as? IrFile
|
||||
?: return
|
||||
@@ -98,7 +99,8 @@ class PropertyLazyInitLowering(
|
||||
val declarations = file.declarations.toList()
|
||||
|
||||
val fieldToInitializer = calculateFieldToExpression(
|
||||
declarations
|
||||
declarations,
|
||||
context
|
||||
)
|
||||
|
||||
if (fieldToInitializer.isEmpty()) return null
|
||||
@@ -206,7 +208,7 @@ class RemoveInitializersForLazyProperties(
|
||||
|
||||
if (declaration !is IrField) return null
|
||||
|
||||
if (!declaration.isCompatibleDeclaration()) return null
|
||||
if (!declaration.isCompatibleDeclaration(context)) return null
|
||||
|
||||
val file = declaration.parent as? IrFile ?: return null
|
||||
|
||||
@@ -232,7 +234,7 @@ class RemoveInitializersForLazyProperties(
|
||||
|
||||
private fun calculateFileFieldsPureness(file: IrFile): Boolean {
|
||||
val declarations = file.declarations.toList()
|
||||
val expressions = calculateFieldToExpression(declarations)
|
||||
val expressions = calculateFieldToExpression(declarations, context)
|
||||
.values
|
||||
|
||||
val allFieldsInFilePure = allFieldsInFilePure(expressions)
|
||||
@@ -241,10 +243,10 @@ class RemoveInitializersForLazyProperties(
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateFieldToExpression(declarations: Collection<IrDeclaration>): Map<IrField, IrExpression> =
|
||||
private fun calculateFieldToExpression(declarations: Collection<IrDeclaration>, context: JsIrBackendContext): Map<IrField, IrExpression> =
|
||||
declarations
|
||||
.asSequence()
|
||||
.filter { it.isCompatibleDeclaration() }
|
||||
.filter { it.isCompatibleDeclaration(context) }
|
||||
.map { it.correspondingProperty }
|
||||
.filterNotNull()
|
||||
.filter { it.isForLazyInit() }
|
||||
@@ -274,12 +276,19 @@ private val IrDeclaration.correspondingProperty: IrProperty?
|
||||
}
|
||||
|
||||
private fun IrDeclaration.propertyWithPersistentSafe(transform: IrDeclaration.() -> IrProperty?): IrProperty? =
|
||||
withPersistentSafe(transform)
|
||||
|
||||
private fun <T> IrDeclaration.withPersistentSafe(transform: IrDeclaration.() -> T?): T? =
|
||||
if (this !is PersistentIrElementBase<*> || this.createdOn <= this.factory.stageController.currentStage) {
|
||||
transform()
|
||||
} else null
|
||||
|
||||
private fun IrDeclaration.isCompatibleDeclaration() =
|
||||
origin in compatibleOrigins
|
||||
private fun IrDeclaration.isCompatibleDeclaration(context: JsIrBackendContext) =
|
||||
(this as? IrField)
|
||||
?.correspondingProperty
|
||||
?.hasAnnotation(context.intrinsics.jsEagerInitializationAnnotationSymbol) != true &&
|
||||
withPersistentSafe { origin in compatibleOrigins } == true
|
||||
|
||||
|
||||
private fun IrDeclaration.assertCompatibleDeclaration() {
|
||||
assert(this !is PersistentIrElementBase<*> || createdOn == 0)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||
// PROPERTY_LAZY_INITIALIZATION
|
||||
|
||||
// FILE: lib.kt
|
||||
var z1 = false
|
||||
var z2 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@JsEagerInitialization
|
||||
val x = foo()
|
||||
|
||||
private fun foo(): Int {
|
||||
z1 = true
|
||||
return 42
|
||||
}
|
||||
|
||||
// Will be initialized since [x]'s initializer calls a function from the file.
|
||||
val y = run { z2 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun box(): String {
|
||||
return return if (z1 && z2) "OK" else "fail"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: WASM
|
||||
// PROPERTY_LAZY_INITIALIZATION
|
||||
|
||||
// FILE: lib.kt
|
||||
var z1 = false
|
||||
var z2 = false
|
||||
|
||||
// FILE: lib2.kt
|
||||
|
||||
@OptIn(kotlin.ExperimentalStdlibApi::class)
|
||||
@JsEagerInitialization
|
||||
val x = run { z1 = true; 42 }
|
||||
|
||||
// Won't be initialized (cause no function from the file will be called during [x] initialization).
|
||||
val y = run { z2 = true; 117 }
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun box(): String {
|
||||
return if (z1 && !z2) "OK" else "fail"
|
||||
}
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+10
@@ -19842,6 +19842,16 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
||||
runTest("compiler/testData/codegen/box/properties/complexPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("eagerInitializationGlobal1.kt")
|
||||
public void testEagerInitializationGlobal1() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/properties/eagerInitializationGlobal1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("eagerInitializationGlobal2.kt")
|
||||
public void testEagerInitializationGlobal2() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/properties/eagerInitializationGlobal2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("field.kt")
|
||||
public void testField() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/properties/field.kt");
|
||||
|
||||
Generated
+10
@@ -19248,6 +19248,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/properties/complexPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("eagerInitializationGlobal1.kt")
|
||||
public void testEagerInitializationGlobal1() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/properties/eagerInitializationGlobal1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("eagerInitializationGlobal2.kt")
|
||||
public void testEagerInitializationGlobal2() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/properties/eagerInitializationGlobal2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("field.kt")
|
||||
public void testField() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/properties/field.kt");
|
||||
|
||||
@@ -191,3 +191,11 @@ public annotation class JsQualifier(val value: String)
|
||||
@Target(CLASS, PROPERTY, FUNCTION, FILE)
|
||||
@SinceKotlin("1.3")
|
||||
public actual annotation class JsExport
|
||||
|
||||
/**
|
||||
* Forces a top-level property to be initialized eagerly, opposed to lazily on the first access to file and/or property.
|
||||
*/
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@ExperimentalStdlibApi
|
||||
public annotation class JsEagerInitialization
|
||||
|
||||
Reference in New Issue
Block a user