Deserializer: Fix initialization order

Top-level initializers must be deserialized in the same order they
where in the original source file. Before this patch this rule might
be violated. Consider the following case:

// lib1.kt
val a = 5
val b = a * 5

// lib2.kt
fun foo() = println(b)

Compile both files into libraries and then link an application
against them. During deserialization of lib2 'b' will be added
to the deserialization queue of lib1. After that we will add 'a'
to this queue too. Thus 'b' and 'a' will be in a wrong order
in this queue resulting in a wrong order of corresponding
top-level initializers in a final executable.

This patch fixes this issue by adding all top-level initializers
to a deserialization queue before all other declarations.
This commit is contained in:
Ilya Matveev
2019-10-30 14:03:05 +03:00
parent 78b29349c9
commit 7c4033f0bf
@@ -91,7 +91,7 @@ abstract class KotlinIrLinker(
} }
class SimpleDeserializationState: DeserializationState<UniqId>() { class SimpleDeserializationState: DeserializationState<UniqId>() {
private val reachableTopLevels = mutableSetOf<UniqId>() private val reachableTopLevels = LinkedHashSet<UniqId>()
override fun addUniqID(key: UniqId) { override fun addUniqID(key: UniqId) {
reachableTopLevels.add(key) reachableTopLevels.add(key)
@@ -140,8 +140,11 @@ abstract class KotlinIrLinker(
// This is a heavy initializer // This is a heavy initializer
val module = deserializeIrModuleHeader() val module = deserializeIrModuleHeader()
inner class IrDeserializerForFile(private var annotations: List<ProtoConstructorCall>?, private var forcedDeclarations: List<UniqId>?, private val fileIndex: Int, onlyHeaders: Boolean) : inner class IrDeserializerForFile(
IrFileDeserializer(logger, builtIns, symbolTable) { private var annotations: List<ProtoConstructorCall>?,
private val fileIndex: Int,
onlyHeaders: Boolean
) : IrFileDeserializer(logger, builtIns, symbolTable) {
private var fileLoops = mutableMapOf<Int, IrLoopBase>() private var fileLoops = mutableMapOf<Int, IrLoopBase>()
@@ -385,10 +388,6 @@ abstract class KotlinIrLinker(
file.annotations.addAll(deserializeAnnotations(it)) file.annotations.addAll(deserializeAnnotations(it))
annotations = null annotations = null
} }
forcedDeclarations?.let {
it.forEach { fileLocalDeserializationState.addUniqID(it) }
forcedDeclarations = null
}
} }
fun deserializeAllFileReachableTopLevel() { fun deserializeAllFileReachableTopLevel() {
@@ -405,10 +404,14 @@ abstract class KotlinIrLinker(
val fileEntry = NaiveSourceBasedFileEntryImpl(fileName, fileProto.fileEntry.lineStartOffsetsList.toIntArray()) val fileEntry = NaiveSourceBasedFileEntryImpl(fileName, fileProto.fileEntry.lineStartOffsetsList.toIntArray())
val explicitlyExported = mutableListOf<UniqId>()
val fileDeserializer = val fileDeserializer =
IrDeserializerForFile(fileProto.annotationList, explicitlyExported, fileIndex, !deserializationStrategy.needBodies) IrDeserializerForFile(fileProto.annotationList, fileIndex, !deserializationStrategy.needBodies).apply {
// Explicitly exported declarations (e.g. top-level initializers) must be deserialized before all other declarations.
// Thus we schedule their deserialization in deserializer's constructor.
fileProto.explicitlyExportedToCompilerList.forEach {
fileLocalDeserializationState.addUniqID(UniqId(loadSymbolData(it).topLevelUniqIdIndex))
}
}
val fqName = fileDeserializer.deserializeFqName(fileProto.fqNameList) val fqName = fileDeserializer.deserializeFqName(fileProto.fqNameList)
@@ -426,10 +429,6 @@ abstract class KotlinIrLinker(
moduleReversedFileIndex.getOrPut(it) { fileDeserializer } moduleReversedFileIndex.getOrPut(it) { fileDeserializer }
} }
fileProto.explicitlyExportedToCompilerList.mapTo(explicitlyExported) {
UniqId(fileDeserializer.loadSymbolData(it).topLevelUniqIdIndex)
}
if (deserializationStrategy.theWholeWorld) { if (deserializationStrategy.theWholeWorld) {
for (id in fileUniqIdIndex) { for (id in fileUniqIdIndex) {
assert(id.isPublic) assert(id.isPublic)