From f82d3e63a20ad66544036cc27bd3e5cf1010c9c9 Mon Sep 17 00:00:00 2001 From: Alexander Korepanov Date: Mon, 6 Mar 2023 17:28:12 +0100 Subject: [PATCH] [JS IR] Use a backing field initializer for js() code folding During a translation of the code from js() call to statement list, the compiler folds the code string. For that it uses property bodies. However the bodies can not be loaded in an incremental rebuild. The patch uses the backing field initializers of constant properties. The initializers are always loaded. ^KT-57002 Fixed --- .../ir/backend/js/ic/HashCalculatorForIC.kt | 6 +++ .../backend/js/transformers/irToJs/jsCode.kt | 16 ++++++-- .../JsIrES6InvalidationTestGenerated.java | 5 +++ .../JsIrInvalidationTestGenerated.java | 5 +++ .../invalidation/constVals/lib2/module.info | 1 + .../invalidation/constVals/project.info | 2 +- .../jsCodeWithConstString/lib1/js.0.kt | 3 ++ .../jsCodeWithConstString/lib1/js.1.kt | 3 ++ .../jsCodeWithConstString/lib1/js.2.kt | 3 ++ .../jsCodeWithConstString/lib1/js.6.kt | 3 ++ .../jsCodeWithConstString/lib1/l1.0.kt | 4 ++ .../jsCodeWithConstString/lib1/l1.3.kt | 7 ++++ .../jsCodeWithConstString/lib1/l1.5.kt | 7 ++++ .../jsCodeWithConstString/lib1/l1.7.kt | 7 ++++ .../jsCodeWithConstString/lib1/module.info | 38 +++++++++++++++++++ .../jsCodeWithConstString/lib1/test.0.kt | 1 + .../jsCodeWithConstString/lib1/test.4.kt | 1 + .../jsCodeWithConstString/main/m.kt | 8 ++++ .../jsCodeWithConstString/main/module.info | 5 +++ .../jsCodeWithConstString/project.info | 8 ++++ 20 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.0.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.1.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.2.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.6.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.0.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.3.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.5.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.7.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/module.info create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.0.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.4.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/m.kt create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/module.info create mode 100644 js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/project.info diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/HashCalculatorForIC.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/HashCalculatorForIC.kt index 42361b6a559..ea957de17d0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/HashCalculatorForIC.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/HashCalculatorForIC.kt @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CrossModuleReferen import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor @@ -131,6 +132,11 @@ internal class ICHasher { } } (symbol.owner as? IrAnnotationContainer)?.let(hashCalculator::updateAnnotationContainer) + (symbol.owner as? IrProperty)?.let { irProperty -> + if (irProperty.isConst) { + irProperty.backingField?.initializer?.let(hashCalculator::update) + } + } return hashCalculator.finalize() } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt index a8c6af82cf8..e976b2284ed 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsCode.kt @@ -63,7 +63,7 @@ private fun translateJsCodeIntoStatementList( return parseExpressionOrStatement(jsCode, ThrowExceptionOnErrorReporter, currentScope, CodePosition(startLine, offset), fileName) } -fun foldString(expression: IrExpression, context: JsIrBackendContext?): String? { +private fun foldString(expression: IrExpression, context: JsIrBackendContext?): String? { val builder = StringBuilder() var foldingFailed = false expression.acceptVoid(object : IrElementVisitorVoid { @@ -92,6 +92,7 @@ fun foldString(expression: IrExpression, context: JsIrBackendContext?): String? override fun visitCall(expression: IrCall) { val owner = expression.symbol.owner + val propertySymbol = owner.correspondingPropertySymbol return when { expression.origin == IrStatementOrigin.PLUS -> expression.acceptChildrenVoid(this) @@ -99,8 +100,17 @@ fun foldString(expression: IrExpression, context: JsIrBackendContext?): String? owner.body?.acceptChildrenVoid(InitFunVisitor(context)) expression.acceptChildrenVoid(this) } - owner == owner.correspondingPropertySymbol?.owner?.getter -> { - owner.body?.acceptChildrenVoid(this) + propertySymbol != null && owner == propertySymbol.owner.getter -> { + if (propertySymbol.owner.isConst) { + val initializer = propertySymbol.owner.backingField?.initializer + if (initializer != null) { + initializer.acceptChildrenVoid(this) + } else { + foldingFailed = true + } + } else { + owner.body?.acceptChildrenVoid(this) + } expression.acceptChildrenVoid(this) } else -> super.visitCall(expression) diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java index 9778e6bd4d8..a590d5d7420 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrES6InvalidationTestGenerated.java @@ -250,6 +250,11 @@ public class JsIrES6InvalidationTestGenerated extends AbstractJsIrES6Invalidatio runTest("js/js.translator/testData/incremental/invalidation/jsCode/"); } + @TestMetadata("jsCodeWithConstString") + public void testJsCodeWithConstString() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/"); + } + @TestMetadata("jsExport") public void testJsExport() throws Exception { runTest("js/js.translator/testData/incremental/invalidation/jsExport/"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java index ed6cddee232..2db7d7075ba 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/JsIrInvalidationTestGenerated.java @@ -250,6 +250,11 @@ public class JsIrInvalidationTestGenerated extends AbstractJsIrInvalidationTest runTest("js/js.translator/testData/incremental/invalidation/jsCode/"); } + @TestMetadata("jsCodeWithConstString") + public void testJsCodeWithConstString() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/"); + } + @TestMetadata("jsExport") public void testJsExport() throws Exception { runTest("js/js.translator/testData/incremental/invalidation/jsExport/"); diff --git a/js/js.translator/testData/incremental/invalidation/constVals/lib2/module.info b/js/js.translator/testData/incremental/invalidation/constVals/lib2/module.info index d1d31ca5618..e3581e75e4a 100644 --- a/js/js.translator/testData/incremental/invalidation/constVals/lib2/module.info +++ b/js/js.translator/testData/incremental/invalidation/constVals/lib2/module.info @@ -3,3 +3,4 @@ STEP 0: added file: l2.kt STEP 1: dependencies: lib1 + updated imports: l2.kt diff --git a/js/js.translator/testData/incremental/invalidation/constVals/project.info b/js/js.translator/testData/incremental/invalidation/constVals/project.info index 3a84c058f4e..f1968258cbd 100644 --- a/js/js.translator/testData/incremental/invalidation/constVals/project.info +++ b/js/js.translator/testData/incremental/invalidation/constVals/project.info @@ -5,4 +5,4 @@ STEP 0: dirty js: lib1, lib2, main STEP 1: libs: lib1, lib2, main - dirty js: lib1 + dirty js: lib1, lib2 diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.0.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.0.kt new file mode 100644 index 00000000000..58b0cb0bcea --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.0.kt @@ -0,0 +1,3 @@ +fun test() : dynamic { + return js("var testObj = { $constKey: 0 }; testObj.$constKey") +} diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.1.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.1.kt new file mode 100644 index 00000000000..0178a191cce --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.1.kt @@ -0,0 +1,3 @@ +fun test() : dynamic { + return js("var testObj = { $constKey: 1 }; testObj.$constKey") +} diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.2.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.2.kt new file mode 100644 index 00000000000..875fefa0a02 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.2.kt @@ -0,0 +1,3 @@ +fun test() : dynamic { + return js("var testObj = { $constKey: $constVal }; testObj.$constKey") +} diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.6.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.6.kt new file mode 100644 index 00000000000..882e6b8832e --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/js.6.kt @@ -0,0 +1,3 @@ +fun test() : dynamic { + return js("var testObj = { ${constKey1}__bar: $constVal + 1 }; testObj.foo__$constKey2") +} diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.0.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.0.kt new file mode 100644 index 00000000000..10bd1daa5d8 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.0.kt @@ -0,0 +1,4 @@ +internal const val constKey = "foo" + +internal const val constVal = "2" + diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.3.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.3.kt new file mode 100644 index 00000000000..d28821835bd --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.3.kt @@ -0,0 +1,7 @@ +internal const val constKey1 = "foo" +internal const val constKey2 = "bar" + +internal const val constKey = constKey1 + "_" + constKey2 + +internal const val constVal = "3" + diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.5.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.5.kt new file mode 100644 index 00000000000..ea30024b876 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.5.kt @@ -0,0 +1,7 @@ +internal const val constKey1 = "foo" +internal const val constKey2 = "bar" + +internal const val constKey = constKey1 + "__" + constKey2 + +internal const val constVal = "4" + diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.7.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.7.kt new file mode 100644 index 00000000000..a2fc3ba44dd --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/l1.7.kt @@ -0,0 +1,7 @@ +internal const val constKey1 = "foo" +internal const val constKey2 = "bar" + +internal const val constKey = constKey1 + "__" + constKey2 + +internal const val constVal = 5 + diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/module.info b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/module.info new file mode 100644 index 00000000000..d0496c3a3e8 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/module.info @@ -0,0 +1,38 @@ +STEP 0: + modifications: + U : l1.0.kt -> l1.kt + U : js.0.kt -> js.kt + U : test.0.kt -> test.kt + added file: l1.kt, js.kt, test.kt +STEP 1: + modifications: + U : js.1.kt -> js.kt + modified ir: js.kt +STEP 2: + modifications: + U : js.2.kt -> js.kt + modified ir: js.kt + updated exports: l1.kt +STEP 3: + modifications: + U : l1.3.kt -> l1.kt + modified ir: l1.kt + updated imports: js.kt +STEP 4: + modifications: + U : test.4.kt -> test.kt + modified ir: test.kt +STEP 5: + modifications: + U : l1.5.kt -> l1.kt + modified ir: l1.kt + updated imports: js.kt +STEP 6: + modifications: + U : js.6.kt -> js.kt + modified ir: js.kt + updated exports: l1.kt +STEP 7: + modifications: + U : l1.7.kt -> l1.kt + modified ir: l1.kt, js.kt diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.0.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.0.kt new file mode 100644 index 00000000000..7d2b2081795 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.0.kt @@ -0,0 +1 @@ +fun doTest() = test() diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.4.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.4.kt new file mode 100644 index 00000000000..e5dbf0f159f --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/lib1/test.4.kt @@ -0,0 +1 @@ +fun doTest() = test() + 1 diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/m.kt b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/m.kt new file mode 100644 index 00000000000..dfd50ba62ca --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/m.kt @@ -0,0 +1,8 @@ +fun box(stepId: Int): String { + val x = doTest() + when (stepId) { + in 0..7 -> if (x != stepId) return "Fail, got $x" + else -> return "Unknown" + } + return "OK" +} diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/module.info b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/module.info new file mode 100644 index 00000000000..586221fecd5 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/main/module.info @@ -0,0 +1,5 @@ +STEP 0: + dependencies: lib1 + added file: m.kt +STEP 1..7: + dependencies: lib1 diff --git a/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/project.info b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/project.info new file mode 100644 index 00000000000..aa728a8c0c3 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/jsCodeWithConstString/project.info @@ -0,0 +1,8 @@ +MODULES: lib1, main + +STEP 0: + libs: lib1, main + dirty js: lib1, main +STEP 1..7: + libs: lib1, main + dirty js: lib1