diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt index faba82b2d31..101e1a7f5e9 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt @@ -55,50 +55,59 @@ abstract class KotlinIrLinker( ) : DescriptorUniqIdAware, IrDeserializer { - sealed class DeserializationState { + sealed class DeserializationState { val deserializedSymbols = mutableMapOf() - val reachableTopLevels = mutableSetOf() - val deserializedTopLevels = mutableSetOf() operator fun contains(key: UniqId) = key in deserializedSymbols operator fun get(key: UniqId): IrSymbol = deserializedSymbols[key] ?: error("No deserialized symbol found for $key") abstract fun addUniqID(key: UniqId) - protected open fun peekReachableKey(): UniqId? = reachableTopLevels.firstOrNull() + abstract fun processPendingDeclarations(processor: (T) -> Unit) + + class ModuleDeserializationState(val module: IrModuleDeserializer): DeserializationState() { + private val filesWithPendingTopLevels = mutableSetOf() + + fun enqueueFile(fileDeserializer: IrModuleDeserializer.IrDeserializerForFile) { + filesWithPendingTopLevels.add(fileDeserializer) + module.enqueueModule() + } - class ModuleDeserializationState(val module: IrModuleDeserializer): DeserializationState() { override fun addUniqID(key: UniqId) { - module.addModuleReachableTopLevel(key) + val fileDeserializer = module.moduleReversedFileIndex[key] ?: error("No file found for key $key") + fileDeserializer.fileLocalDeserializationState.addUniqID(key) + + enqueueFile(fileDeserializer) + } + + override fun processPendingDeclarations(processor: (IrModuleDeserializer.IrDeserializerForFile) -> Unit) { + while (filesWithPendingTopLevels.isNotEmpty()) { + val pendingDeserializer = filesWithPendingTopLevels.first() + + processor(pendingDeserializer) + + filesWithPendingTopLevels.remove(pendingDeserializer) + } } } - class SimpleDeserializationState: DeserializationState() { + class SimpleDeserializationState: DeserializationState() { + private val reachableTopLevels = mutableSetOf() + override fun addUniqID(key: UniqId) { reachableTopLevels.add(key) } - override fun peekReachableKey(): UniqId { - return reachableTopLevels.firstOrNull() ?: error("Expecting non-empty set") - } - } + override fun processPendingDeclarations(processor: (UniqId) -> Unit) { + while (reachableTopLevels.isNotEmpty()) { + val reachableKey = reachableTopLevels.first() - fun processPendingDeclaration(processor: (UniqId) -> Unit) { - do { + if (deserializedSymbols[reachableKey]?.isBound != true) { + processor(reachableKey) + } - val reachableKey = peekReachableKey() ?: return - - if (deserializedSymbols[reachableKey]?.isBound == true) { reachableTopLevels.remove(reachableKey) - deserializedTopLevels.add(reachableKey) - continue } - - processor(reachableKey) - - reachableTopLevels.remove(reachableKey) - deserializedTopLevels.add(reachableKey) - - } while (reachableTopLevels.isNotEmpty()) + } } } @@ -123,7 +132,7 @@ abstract class KotlinIrLinker( protected val moduleResolvedForwardDeclarations = mutableMapOf() private val moduleDeserializationState = DeserializationState.ModuleDeserializationState(this) - private val moduleReversedFileIndex = mutableMapOf() + val moduleReversedFileIndex = mutableMapOf() private val moduleDependencies by lazy { moduleDescriptor.allDependencyModules.filter { it != moduleDescriptor }.map { deserializersForModules[it]!! } } @@ -131,7 +140,7 @@ abstract class KotlinIrLinker( // This is a heavy initializer val module = deserializeIrModuleHeader() - inner class IrDeserializerForFile(private var annotations: List?, private val fileIndex: Int, onlyHeaders: Boolean) : + inner class IrDeserializerForFile(private var annotations: List?, private var forcedDeclarations: List?, private val fileIndex: Int, onlyHeaders: Boolean) : IrFileDeserializer(logger, builtIns, symbolTable) { private var fileLoops = mutableMapOf() @@ -281,7 +290,7 @@ abstract class KotlinIrLinker( return moduleDependencies.firstOrNull { key in it.moduleReversedFileIndex } } - private fun getStateForID(key: UniqId): DeserializationState { + private fun getStateForID(key: UniqId): DeserializationState<*> { if (key.isLocal) return fileLocalDeserializationState if (isGlobalUniqID(key)) return globalDeserializationState return getModuleForTopLevelId(key)?.moduleDeserializationState ?: handleNoModuleDeserializerFound(key) @@ -371,18 +380,19 @@ abstract class KotlinIrLinker( } } - fun deserializeFileAnnotationsIfFirstUse() { + fun deserializeFileImplicitDataIfFirstUse() { annotations?.let { file.annotations.addAll(deserializeAnnotations(it)) annotations = null } + forcedDeclarations?.let { + it.forEach { fileLocalDeserializationState.addUniqID(it) } + forcedDeclarations = null + } } - fun deserializeFileTopLevelDeclaration(key: UniqId) { - - fileLocalDeserializationState.addUniqID(key) - - fileLocalDeserializationState.processPendingDeclaration { + fun deserializeAllFileReachableTopLevel() { + fileLocalDeserializationState.processPendingDeclarations { val declaration = deserializeDeclaration(it) file.declarations.add(declaration) } @@ -395,7 +405,10 @@ abstract class KotlinIrLinker( val fileEntry = NaiveSourceBasedFileEntryImpl(fileName, fileProto.fileEntry.lineStartOffsetsList.toIntArray()) - val fileDeserializer = IrDeserializerForFile(fileProto.annotationList, fileIndex, !deserializationStrategy.needBodies) + val explicitlyExported = mutableListOf() + + val fileDeserializer = + IrDeserializerForFile(fileProto.annotationList, explicitlyExported, fileIndex, !deserializationStrategy.needBodies) val fqName = fileDeserializer.deserializeFqName(fileProto.fqNameList) @@ -413,10 +426,8 @@ abstract class KotlinIrLinker( moduleReversedFileIndex.getOrPut(it) { fileDeserializer } } - for (d in fileProto.explicitlyExportedToCompilerList) { - fileDeserializer.run { - fileLocalDeserializationState.addUniqID(UniqId(loadSymbolData(d).topLevelUniqIdIndex)) - } + fileProto.explicitlyExportedToCompilerList.mapTo(explicitlyExported) { + UniqId(fileDeserializer.loadSymbolData(it).topLevelUniqIdIndex) } if (deserializationStrategy.theWholeWorld) { @@ -425,7 +436,7 @@ abstract class KotlinIrLinker( moduleDeserializationState.addUniqID(id) } } else if (deserializationStrategy.explicitlyExported) { - modulesWithReachableTopLevels.add(this) + moduleDeserializationState.enqueueFile(fileDeserializer) } return file @@ -444,20 +455,19 @@ abstract class KotlinIrLinker( } fun deserializeAllModuleReachableTopLevels() { - - moduleDeserializationState.processPendingDeclaration { - val fileDeserializer = moduleReversedFileIndex[it] - ?: error("No file deserializer for key $it") - - fileDeserializer.deserializeFileTopLevelDeclaration(it) - fileDeserializer.deserializeFileAnnotationsIfFirstUse() + moduleDeserializationState.processPendingDeclarations { fileDeserializer -> + fileDeserializer.deserializeFileImplicitDataIfFirstUse() + fileDeserializer.deserializeAllFileReachableTopLevel() } } - fun addModuleReachableTopLevel(topLevelKey: UniqId) { - moduleDeserializationState.reachableTopLevels.add(topLevelKey) + fun enqueueModule() { modulesWithReachableTopLevels.add(this) } + + fun addModuleReachableTopLevel(key: UniqId) { + moduleDeserializationState.addUniqID(key) + } } protected abstract val descriptorReferenceDeserializer: DescriptorReferenceDeserializer @@ -492,7 +502,7 @@ abstract class KotlinIrLinker( protected abstract fun readFileCount(moduleDescriptor: ModuleDescriptor): Int protected abstract fun checkAccessibility(declarationDescriptor: DeclarationDescriptor): Boolean - protected open fun handleNoModuleDeserializerFound(key: UniqId): DeserializationState { + protected open fun handleNoModuleDeserializerFound(key: UniqId): DeserializationState<*> { error("Deserializer for declaration $key is not found") } @@ -515,7 +525,6 @@ abstract class KotlinIrLinker( "Locally accessible declarations should not be accessed here $topLevelDescriptor" } - if (topLevelDescriptor !is DeserializedClassDescriptor && topLevelDescriptor !is DeserializedCallableMemberDescriptor) { return null } diff --git a/compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt b/compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt new file mode 100644 index 00000000000..fb836b35446 --- /dev/null +++ b/compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt @@ -0,0 +1,25 @@ +// IGNORE_BACKEND: JVM, JVM_IR +// IGNORE_LIGHT_ANALYSIS +// MODULE: lib1 +// FILE: lib1.kt + +// KT-34273 + +var foo = "Fail" + +// MODULE: lib2(lib1) +// FILE: lib2.kt + +private val bar = run { + foo = "OK" + 42 +} + + +// MODULE: main(lib1, lib2) +// FILE: main.kt + +// TODO: the proper behaviour of this test is still open design issue +// K/N & K/JS used to go like this when JVM fails + +fun box(): String = foo diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index b05a5f8d434..4443da49244 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -18674,6 +18674,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/properties/protectedJavaPropertyInCompanion.kt"); } + @TestMetadata("sideEffectInTopLevelInitializerMultiModule.kt") + public void testSideEffectInTopLevelInitializerMultiModule() throws Exception { + runTest("compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt"); + } + @TestMetadata("substituteJavaSuperField.kt") public void testSubstituteJavaSuperField() throws Exception { runTest("compiler/testData/codegen/box/properties/substituteJavaSuperField.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 6400fee2bc2..6dac1f448e8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -18336,6 +18336,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/properties/augmentedAssignmentsAndIncrements.kt"); } + @TestMetadata("sideEffectInTopLevelInitializerMultiModule.kt") + public void ignoreSideEffectInTopLevelInitializerMultiModule() throws Exception { + runTest("compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt"); + } + private void runTest(String testDataFilePath) throws Exception { KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 6c194e84fb3..e64f0f794c2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17554,6 +17554,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/properties/protectedJavaPropertyInCompanion.kt"); } + @TestMetadata("sideEffectInTopLevelInitializerMultiModule.kt") + public void testSideEffectInTopLevelInitializerMultiModule() throws Exception { + runTest("compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt"); + } + @TestMetadata("substituteJavaSuperField.kt") public void testSubstituteJavaSuperField() throws Exception { runTest("compiler/testData/codegen/box/properties/substituteJavaSuperField.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 068e8f53030..c72e8a20247 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -14494,6 +14494,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/properties/privatePropertyWithoutBackingField.kt"); } + @TestMetadata("sideEffectInTopLevelInitializerMultiModule.kt") + public void testSideEffectInTopLevelInitializerMultiModule() throws Exception { + runTest("compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt"); + } + @TestMetadata("twoAnnotatedExtensionPropertiesWithoutBackingFields.kt") public void testTwoAnnotatedExtensionPropertiesWithoutBackingFields() throws Exception { runTest("compiler/testData/codegen/box/properties/twoAnnotatedExtensionPropertiesWithoutBackingFields.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index a8ac2a89fcf..71acb94a24a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -15649,6 +15649,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/properties/privatePropertyWithoutBackingField.kt"); } + @TestMetadata("sideEffectInTopLevelInitializerMultiModule.kt") + public void testSideEffectInTopLevelInitializerMultiModule() throws Exception { + runTest("compiler/testData/codegen/box/properties/sideEffectInTopLevelInitializerMultiModule.kt"); + } + @TestMetadata("twoAnnotatedExtensionPropertiesWithoutBackingFields.kt") public void testTwoAnnotatedExtensionPropertiesWithoutBackingFields() throws Exception { runTest("compiler/testData/codegen/box/properties/twoAnnotatedExtensionPropertiesWithoutBackingFields.kt");