From 503ab6bea3a712b2a904bffe3b85633538bf2cb3 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 (cherry picked from commit f82d3e63a20ad66544036cc27bd3e5cf1010c9c9) --- .../ir/backend/js/ic/HashCalculatorForIC.kt | 7 ++++ .../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, 130 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 6bc32cedbe71c..6a9e7adc8c8f6 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,8 @@ 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.IrSimpleFunction +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 @@ -139,6 +141,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 a8c6af82cf889..e976b2284eded 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 1137cd32c10df..d9c879317aa18 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 void testJsCode() throws Exception { 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 81aced2458ceb..040c47454abea 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 void testJsCode() throws Exception { 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 d1d31ca561885..e3581e75e4a55 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 3a84c058f4efe..f1968258cbddd 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 0000000000000..58b0cb0bceaf2 --- /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 0000000000000..0178a191ccedf --- /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 0000000000000..875fefa0a0238 --- /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 0000000000000..882e6b8832eac --- /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 0000000000000..10bd1daa5d87b --- /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 0000000000000..d28821835bd9a --- /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 0000000000000..ea30024b87673 --- /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 0000000000000..a2fc3ba44dd3d --- /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 0000000000000..d0496c3a3e87d --- /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 0000000000000..7d2b2081795fb --- /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 0000000000000..e5dbf0f159fc8 --- /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 0000000000000..dfd50ba62caae --- /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 0000000000000..586221fecd540 --- /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 0000000000000..aa728a8c0c3d3 --- /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