[JS IR] Support function type interfaces in incremental cache infrastructure

The patch removes logic of generating extra IrFiles (fake file) into
 IrModuleFragment for the function type interfaces during klib deserialization,
 because IC infrastructure can not process files which do not exist in klib.

 Instead of adding extra IrFiles during deserialization, the empty files
 with required packages are added into Kotlin/JS stdlib physically.
 These files are used as containers for function type interface declarations.

 Since Kotlin/WASM uses the same klib loading infrastructure as Kotlin/JS,
 the the empty files are added into Kotlin/WASM stdlib as well.

 The patch also adds a check that IrModuleFagment has files only from klib.

^KT-55720 Fixed
This commit is contained in:
Alexander Korepanov
2023-02-03 12:23:48 +00:00
committed by Space Team
parent 1c3c5417ad
commit 9324cf3360
46 changed files with 574 additions and 81 deletions
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.serialization.checkIsFunctionTypeInterfacePackageFqName
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
class FunctionTypeInterfacePackages {
companion object {
private const val FUNCTION_TYPE_INTERFACE_DIR = "function-type-interface"
}
private val functionTypeInterfacePackageFiles = hashSetOf<IrFile>()
// Function type interfaces are not declared in a standard library, and they are generated on flight during a klib deserialization.
// The accessor allows finding a package for storing the generated function type interfaces.
// The optimization reduces the size of a standard library klib.
// Here are some numbers:
// 255 interfaces in one package increase the size of uncompressed klib up to 3.5+MB;
// 512 interfaces in one package increase the size of uncompressed klib up to 14.5+MB!
// We need 3 packages.
fun makePackageAccessor(stdlibModule: IrModuleFragment) = { packageFragmentDescriptor: PackageFragmentDescriptor ->
val packageFqName = packageFragmentDescriptor.fqName.toString()
check(checkIsFunctionTypeInterfacePackageFqName(packageFqName)) { "unexpected function type interface package $packageFqName" }
val fileWithRequiredPackage = "${packageFqName.replace('.', '-')}-package.kt"
val packageFile = stdlibModule.files.singleOrNull {
// Do not check by name "$FUNCTION_TYPE_INTERFACE_DIR/$fileWithRequiredPackage" because the path separator depends on OS
it.fileEntry.name.endsWith(fileWithRequiredPackage) && it.fileEntry.name.contains(FUNCTION_TYPE_INTERFACE_DIR)
} ?: error("can not find a functional interface file for $packageFqName package")
check(packageFragmentDescriptor.fqName == packageFile.fqName) {
"unexpected package in file ${packageFile.fileEntry.name}; expected $packageFqName, got ${packageFile.fqName}"
}
functionTypeInterfacePackageFiles += packageFile
packageFile
}
fun isFunctionTypeInterfacePackageFile(file: IrFile) = file in functionTypeInterfacePackageFiles
}
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
@@ -38,7 +37,6 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.*
import org.jetbrains.kotlin.ir.declarations.IrFactory
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.descriptors.IrDescriptorBasedFunctionFactory
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
import org.jetbrains.kotlin.ir.symbols.IrSymbol
@@ -71,6 +69,7 @@ import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.util.DummyLogger
import org.jetbrains.kotlin.util.Logger
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
import java.io.File
val KotlinLibrary.moduleName: String
@@ -184,14 +183,6 @@ fun deserializeDependencies(
}
}
fun getFunctionFactoryCallback(stdlibModule: IrModuleFragment) = { packageFragmentDescriptor: PackageFragmentDescriptor ->
IrFileImpl(
NaiveSourceBasedFileEntryImpl("${packageFragmentDescriptor.fqName}-[K][Suspend]Functions"),
packageFragmentDescriptor,
stdlibModule
).also { stdlibModule.files += it }
}
fun loadIr(
depsDescriptors: ModulesStructure,
irFactory: IrFactory,
@@ -287,7 +278,9 @@ fun getIrModuleInfoForKlib(
irBuiltIns,
symbolTable,
typeTranslator,
if (loadFunctionInterfacesIntoStdlib) getFunctionFactoryCallback(deserializedModuleFragments.first()) else null,
loadFunctionInterfacesIntoStdlib.ifTrue {
FunctionTypeInterfacePackages().makePackageAccessor(deserializedModuleFragments.first())
},
true
)
@@ -345,7 +338,9 @@ fun getIrModuleInfoForSourceFiles(
irBuiltIns,
symbolTable,
psi2IrContext.typeTranslator,
if (loadFunctionInterfacesIntoStdlib) getFunctionFactoryCallback(deserializedModuleFragments.first()) else null,
loadFunctionInterfacesIntoStdlib.ifTrue {
FunctionTypeInterfacePackages().makePackageAccessor(deserializedModuleFragments.first())
},
true
)