diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt index ba48a5f16a0..f67ffa3da66 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt @@ -390,17 +390,44 @@ class CacheUpdater( val dependentSrcMetadata = dependentCache.fetchSourceFileFullMetadata(dependentSrcFile) val dependentSignatures = dependentSrcMetadata.directDependencies[libFile, srcFile] ?: emptyMap() when { + // ignore if the dependent file is already dirty dependentSrcMetadata is DirtyFileMetadata -> continue - this[dependentLibFile, dependentSrcFile]?.importedSignaturesModified == true -> continue + + // ignore if the dependent file imports have been modified, the metadata for it will be rebuilt later + this[dependentLibFile, dependentSrcFile]?.importedSignaturesState == ImportedSignaturesState.MODIFIED -> continue + + // update metadata if the direct dependencies have been modified dependentSignatures.any { signatureHashCalculator[it.key] != it.value } -> { val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata) - newMetadata.importedSignaturesModified = true + newMetadata.importedSignaturesState = ImportedSignaturesState.MODIFIED } + // update metadata if the signature set of the direct dependencies has been updated dependentSignatures.keys != newSignatures -> { val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata) - newMetadata.directDependencies[libFile, srcFile] = newSignatures.associateWithTo(HashMap(newSignatures.size)) { - signatureHashCalculator[it] ?: notFoundIcError("signature $it hash", libFile, srcFile) + + if (newMetadata.importedSignaturesState == ImportedSignaturesState.UNKNOWN) { + val isNonModified = dependentSrcMetadata.directDependencies.allFiles { _, _, signatures -> + signatures.all { + val newHash = signatureHashCalculator[it.key] + // a new hash may be not calculated for the non-loaded symbols, it is ok + newHash == null || newHash == it.value + } + } + newMetadata.importedSignaturesState = if (isNonModified) { + ImportedSignaturesState.NON_MODIFIED + } else { + ImportedSignaturesState.MODIFIED + } + } + + // if imports have been modified, metadata for the file will be rebuilt later, + // so if the imports haven't been modified, update the metadata manually + if (newMetadata.importedSignaturesState == ImportedSignaturesState.NON_MODIFIED) { + val newDirectDependencies = newSignatures.associateWithTo(HashMap(newSignatures.size)) { + signatureHashCalculator[it] ?: notFoundIcError("signature $it hash", libFile, srcFile) + } + newMetadata.directDependencies[libFile, srcFile] = newDirectDependencies } } } @@ -434,13 +461,13 @@ class CacheUpdater( for ((srcFile, srcFileMetadata) in srcFiles) { val isExportedSignatureUpdated = srcFileMetadata.isExportedSignaturesUpdated() - if (isExportedSignatureUpdated || srcFileMetadata.importedSignaturesModified) { + if (isExportedSignatureUpdated || srcFileMetadata.importedSignaturesState == ImportedSignaturesState.MODIFIED) { // if exported signatures or imported inline functions were modified - rebuild filesToRebuild[srcFile] = srcFileMetadata if (isExportedSignatureUpdated) { fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_EXPORTS) } - if (srcFileMetadata.importedSignaturesModified) { + if (srcFileMetadata.importedSignaturesState == ImportedSignaturesState.MODIFIED) { fileStats.addDirtFileStat(srcFile, DirtyFileState.UPDATED_IMPORTS) } } else { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt index 430d7e8f33c..5f5959bbf22 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt @@ -136,11 +136,13 @@ internal class DirtyFileMetadata( } } +internal enum class ImportedSignaturesState { UNKNOWN, MODIFIED, NON_MODIFIED } + internal class UpdatedDependenciesMetadata(oldMetadata: KotlinSourceFileMetadata) : KotlinSourceFileMetadata() { private val oldInverseDependencies = oldMetadata.inverseDependencies private val newExportedSignatures: Set by lazy { inverseDependencies.flatSignatures() } - var importedSignaturesModified = false + var importedSignaturesState = ImportedSignaturesState.UNKNOWN override val inverseDependencies = oldMetadata.inverseDependencies.toMutable() override val directDependencies = oldMetadata.directDependencies.toMutable() diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java index e5f40a211d2..1611f4d6884 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/incremental/InvalidationTestGenerated.java @@ -245,6 +245,11 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest { runTest("js/js.translator/testData/incremental/invalidation/mainModuleInvalidation/"); } + @TestMetadata("moveAndModifyInlineFunction") + public void testMoveAndModifyInlineFunction() throws Exception { + runTest("js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/"); + } + @TestMetadata("moveFilesBetweenModules") public void testMoveFilesBetweenModules() throws Exception { runTest("js/js.translator/testData/incremental/invalidation/moveFilesBetweenModules/"); diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1a.0.kt b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1a.0.kt new file mode 100644 index 00000000000..60346415326 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1a.0.kt @@ -0,0 +1 @@ +inline fun inlineFun() = 0 diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1a.1.kt b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1a.1.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1b.0.kt b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1b.0.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1b.1.kt b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1b.1.kt new file mode 100644 index 00000000000..6d3b26335e6 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/l1b.1.kt @@ -0,0 +1 @@ +inline fun inlineFun() = 1 diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/module.info b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/module.info new file mode 100644 index 00000000000..4502df2f9e6 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/module.info @@ -0,0 +1,11 @@ +STEP 0: + modifications: + U : l1a.0.kt -> l1a.kt + U : l1b.0.kt -> l1b.kt + added file: l1a.kt, l1b.kt, test.kt +STEP 1: + modifications: + U : l1a.1.kt -> l1a.kt + U : l1b.1.kt -> l1b.kt + modified ir: l1a.kt, l1b.kt + updated imports: test.kt diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/test.kt b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/test.kt new file mode 100644 index 00000000000..1eac76ce724 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/lib1/test.kt @@ -0,0 +1 @@ +fun test() = inlineFun() diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/main/m.kt b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/main/m.kt new file mode 100644 index 00000000000..9b36f2c5696 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/main/m.kt @@ -0,0 +1,7 @@ +fun box(stepId: Int): String { + val x = test() + if (x != stepId) { + return "Fail: $x != $stepId" + } + return "OK" +} diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/main/module.info b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/main/module.info new file mode 100644 index 00000000000..5d087aa0643 --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/main/module.info @@ -0,0 +1,5 @@ +STEP 0: + dependencies: lib1 + added file: m.kt +STEP 1: + dependencies: lib1 diff --git a/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/project.info b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/project.info new file mode 100644 index 00000000000..425cf7cc77f --- /dev/null +++ b/js/js.translator/testData/incremental/invalidation/moveAndModifyInlineFunction/project.info @@ -0,0 +1,8 @@ +MODULES: lib1, main + +STEP 0: + libs: lib1, main + dirty js: lib1, main +STEP 1: + libs: lib1, main + dirty js: lib1