[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 doNotIntrinsifyAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("DoNotIntrinsify"))
|
||||||
val jsFunAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsFun"))
|
val jsFunAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsFun"))
|
||||||
|
|
||||||
|
val jsEagerInitializationAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsEagerInitialization"))
|
||||||
|
|
||||||
// TODO move CharSequence-related stiff to IntrinsifyCallsLowering
|
// TODO move CharSequence-related stiff to IntrinsifyCallsLowering
|
||||||
val charSequenceClassSymbol = context.symbolTable.referenceClass(context.getClass(FqName("kotlin.CharSequence")))
|
val charSequenceClassSymbol = context.symbolTable.referenceClass(context.getClass(FqName("kotlin.CharSequence")))
|
||||||
val charSequenceLengthPropertyGetterSymbol by context.lazy2 {
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrElementBase
|
import org.jetbrains.kotlin.ir.declarations.persistent.PersistentIrElementBase
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
@@ -51,7 +52,7 @@ class PropertyLazyInitLowering(
|
|||||||
if (container !is IrField && container !is IrSimpleFunction && container !is IrProperty)
|
if (container !is IrField && container !is IrSimpleFunction && container !is IrProperty)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (!container.isCompatibleDeclaration()) return
|
if (!container.isCompatibleDeclaration(context)) return
|
||||||
|
|
||||||
val file = container.parent as? IrFile
|
val file = container.parent as? IrFile
|
||||||
?: return
|
?: return
|
||||||
@@ -98,7 +99,8 @@ class PropertyLazyInitLowering(
|
|||||||
val declarations = file.declarations.toList()
|
val declarations = file.declarations.toList()
|
||||||
|
|
||||||
val fieldToInitializer = calculateFieldToExpression(
|
val fieldToInitializer = calculateFieldToExpression(
|
||||||
declarations
|
declarations,
|
||||||
|
context
|
||||||
)
|
)
|
||||||
|
|
||||||
if (fieldToInitializer.isEmpty()) return null
|
if (fieldToInitializer.isEmpty()) return null
|
||||||
@@ -206,7 +208,7 @@ class RemoveInitializersForLazyProperties(
|
|||||||
|
|
||||||
if (declaration !is IrField) return null
|
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
|
val file = declaration.parent as? IrFile ?: return null
|
||||||
|
|
||||||
@@ -232,7 +234,7 @@ class RemoveInitializersForLazyProperties(
|
|||||||
|
|
||||||
private fun calculateFileFieldsPureness(file: IrFile): Boolean {
|
private fun calculateFileFieldsPureness(file: IrFile): Boolean {
|
||||||
val declarations = file.declarations.toList()
|
val declarations = file.declarations.toList()
|
||||||
val expressions = calculateFieldToExpression(declarations)
|
val expressions = calculateFieldToExpression(declarations, context)
|
||||||
.values
|
.values
|
||||||
|
|
||||||
val allFieldsInFilePure = allFieldsInFilePure(expressions)
|
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
|
declarations
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filter { it.isCompatibleDeclaration() }
|
.filter { it.isCompatibleDeclaration(context) }
|
||||||
.map { it.correspondingProperty }
|
.map { it.correspondingProperty }
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.filter { it.isForLazyInit() }
|
.filter { it.isForLazyInit() }
|
||||||
@@ -274,12 +276,19 @@ private val IrDeclaration.correspondingProperty: IrProperty?
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun IrDeclaration.propertyWithPersistentSafe(transform: IrDeclaration.() -> IrProperty?): 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) {
|
if (this !is PersistentIrElementBase<*> || this.createdOn <= this.factory.stageController.currentStage) {
|
||||||
transform()
|
transform()
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
private fun IrDeclaration.isCompatibleDeclaration() =
|
private fun IrDeclaration.isCompatibleDeclaration(context: JsIrBackendContext) =
|
||||||
origin in compatibleOrigins
|
(this as? IrField)
|
||||||
|
?.correspondingProperty
|
||||||
|
?.hasAnnotation(context.intrinsics.jsEagerInitializationAnnotationSymbol) != true &&
|
||||||
|
withPersistentSafe { origin in compatibleOrigins } == true
|
||||||
|
|
||||||
|
|
||||||
private fun IrDeclaration.assertCompatibleDeclaration() {
|
private fun IrDeclaration.assertCompatibleDeclaration() {
|
||||||
assert(this !is PersistentIrElementBase<*> || createdOn == 0)
|
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");
|
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")
|
@TestMetadata("field.kt")
|
||||||
public void testField() throws Exception {
|
public void testField() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/properties/field.kt");
|
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");
|
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")
|
@TestMetadata("field.kt")
|
||||||
public void testField() throws Exception {
|
public void testField() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/properties/field.kt");
|
runTest("compiler/testData/codegen/box/properties/field.kt");
|
||||||
|
|||||||
@@ -191,3 +191,11 @@ public annotation class JsQualifier(val value: String)
|
|||||||
@Target(CLASS, PROPERTY, FUNCTION, FILE)
|
@Target(CLASS, PROPERTY, FUNCTION, FILE)
|
||||||
@SinceKotlin("1.3")
|
@SinceKotlin("1.3")
|
||||||
public actual annotation class JsExport
|
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