[K/JS] Reexport exported declarations in a proxy module file with per-file compilation
This commit is contained in:
+125
-65
@@ -6,10 +6,10 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.ic
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.serialization.cityHash64
|
||||
import org.jetbrains.kotlin.backend.common.serialization.cityHash64String
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
|
||||
import org.jetbrains.kotlin.protobuf.CodedInputStream
|
||||
import org.jetbrains.kotlin.protobuf.CodedOutputStream
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.butIf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import java.io.File
|
||||
|
||||
@@ -22,38 +22,50 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
private const val CACHED_FILE_D_TS = "file.d.ts"
|
||||
}
|
||||
|
||||
class CachedFileInfo(
|
||||
val moduleArtifact: ModuleArtifact,
|
||||
val fileArtifact: SrcFileArtifact,
|
||||
val isExportFileCachedInfo: Boolean = false,
|
||||
) : CacheInfo {
|
||||
var dtsHash: Long? = null
|
||||
sealed class CachedFileInfo(val moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader?) : CacheInfo {
|
||||
var crossFileReferencesHash: ICHash = ICHash()
|
||||
var exportFileCachedInfo: CachedFileInfo? = null
|
||||
override lateinit var jsIrHeader: JsIrModuleHeader
|
||||
final override lateinit var jsIrHeader: JsIrModuleHeader
|
||||
|
||||
constructor(
|
||||
jsIrModuleHeader: JsIrModuleHeader,
|
||||
moduleArtifact: ModuleArtifact,
|
||||
fileArtifact: SrcFileArtifact,
|
||||
isExportFileCachedInfo: Boolean = false,
|
||||
tsDeclarationsHash: Long? = null,
|
||||
) : this(moduleArtifact, fileArtifact, isExportFileCachedInfo) {
|
||||
jsIrHeader = jsIrModuleHeader
|
||||
dtsHash = tsDeclarationsHash
|
||||
init {
|
||||
if (moduleHeader != null) jsIrHeader = moduleHeader
|
||||
}
|
||||
|
||||
val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) }
|
||||
|
||||
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(if (isExportFileCachedInfo) CACHED_EXPORT_FILE_JS else CACHED_FILE_JS) }
|
||||
val dtsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { runIf(isExportFileCachedInfo) { getArtifactWithName(CACHED_FILE_D_TS) } }
|
||||
val sourceMapFileArtifact by lazy(LazyThreadSafetyMode.NONE) { runIf(!isExportFileCachedInfo) { getArtifactWithName(CACHED_FILE_JS_MAP) } }
|
||||
sealed class SerializableCachedFileInfo(
|
||||
moduleArtifact: ModuleArtifact,
|
||||
val fileArtifact: SrcFileArtifact,
|
||||
moduleHeader: JsIrModuleHeader?
|
||||
) : CachedFileInfo(moduleArtifact, moduleHeader) {
|
||||
fun getArtifactWithName(name: String): File? = moduleArtifact.artifactsDir?.let { File(it, "$filePrefix.$name") }
|
||||
private val filePrefix by lazy(LazyThreadSafetyMode.NONE) { fileArtifact.srcFilePath.run { "${substringAfterLast('/')}.${cityHash64()}" } }
|
||||
}
|
||||
|
||||
private fun getArtifactWithName(name: String): File? = moduleArtifact.artifactsDir?.let { File(it, "$filePrefix.$name") }
|
||||
class MainFileCachedInfo(moduleArtifact: ModuleArtifact, fileArtifact: SrcFileArtifact, moduleHeader: JsIrModuleHeader? = null) :
|
||||
SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) {
|
||||
var exportFileCachedInfo: ExportFileCachedInfo? = null
|
||||
|
||||
private val filePrefix by lazy(LazyThreadSafetyMode.NONE) {
|
||||
val pathHash = fileArtifact.srcFilePath.cityHash64String()
|
||||
"${fileArtifact.srcFilePath.substringAfterLast('/')}.$pathHash"
|
||||
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS) }
|
||||
val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) }
|
||||
val sourceMapFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS_MAP) }
|
||||
}
|
||||
|
||||
class ExportFileCachedInfo(
|
||||
moduleArtifact: ModuleArtifact,
|
||||
fileArtifact: SrcFileArtifact,
|
||||
moduleHeader: JsIrModuleHeader? = null,
|
||||
var tsDeclarationsHash: Long? = null
|
||||
) : SerializableCachedFileInfo(moduleArtifact, fileArtifact, moduleHeader) {
|
||||
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_EXPORT_FILE_JS) }
|
||||
val dtsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_D_TS) }
|
||||
}
|
||||
|
||||
class ModuleProxyFileCachedInfo(moduleArtifact: ModuleArtifact, moduleHeader: JsIrModuleHeader? = null) :
|
||||
CachedFileInfo(moduleArtifact, moduleHeader) {
|
||||
val jsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_JS) }
|
||||
val dtsFileArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(CACHED_FILE_D_TS) }
|
||||
val moduleHeaderArtifact by lazy(LazyThreadSafetyMode.NONE) { getArtifactWithName(JS_MODULE_HEADER) }
|
||||
|
||||
private fun getArtifactWithName(name: String): File? = moduleArtifact.artifactsDir?.let { File(it, "entry.$name") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,13 +78,14 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
private fun JsIrProgramFragment.getExportFragmentExternalName(moduleArtifact: ModuleArtifact) =
|
||||
moduleFragmentToExternalName.getExternalNameForExporterFile(name, packageFqn, moduleArtifact.moduleExternalName)
|
||||
|
||||
private fun JsIrProgramFragment.asIrModuleHeader(moduleName: String): JsIrModuleHeader {
|
||||
private fun JsIrProgramFragment.asIrModuleHeader(moduleName: String, reexportedIn: String? = null): JsIrModuleHeader {
|
||||
return JsIrModuleHeader(
|
||||
moduleName = moduleName,
|
||||
externalModuleName = moduleName,
|
||||
definitions = definitions,
|
||||
nameBindings = nameBindings.mapValues { v -> v.value.toString() },
|
||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||
reexportedInModuleWithName = reexportedIn,
|
||||
associatedModule = null
|
||||
)
|
||||
}
|
||||
@@ -80,15 +93,20 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
private fun SrcFileArtifact.loadJsIrModuleHeaders(moduleArtifact: ModuleArtifact) = with(loadJsIrFragments()) {
|
||||
LoadedJsIrModuleHeaders(
|
||||
mainFragment.run { asIrModuleHeader(getMainFragmentExternalName(moduleArtifact)) },
|
||||
exportFragment?.run { asIrModuleHeader(mainFragment.getExportFragmentExternalName(moduleArtifact)) },
|
||||
exportFragment?.run { asIrModuleHeader(mainFragment.getExportFragmentExternalName(moduleArtifact), moduleArtifact.moduleExternalName) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun CodedInputStream.loadSingleCachedFileInfo(cachedFileInfo: CachedFileInfo) = cachedFileInfo.also {
|
||||
private fun <T : CachedFileInfo> CodedInputStream.loadSingleCachedFileInfo(cachedFileInfo: T): T = cachedFileInfo.also {
|
||||
val moduleName = readString()
|
||||
var reexportedIn: String? = null
|
||||
|
||||
it.crossFileReferencesHash = ICHash.fromProtoStream(this)
|
||||
it.dtsHash = runIf(readBool()) { readInt64() }
|
||||
|
||||
if (it is CachedFileInfo.ExportFileCachedInfo) {
|
||||
it.tsDeclarationsHash = runIf(readBool()) { readInt64() }
|
||||
reexportedIn = cachedFileInfo.moduleArtifact.moduleExternalName
|
||||
}
|
||||
|
||||
val (definitions, nameBindings, optionalCrossModuleImports) = fetchJsIrModuleHeaderNames()
|
||||
|
||||
@@ -98,15 +116,16 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
definitions = definitions,
|
||||
nameBindings = nameBindings,
|
||||
optionalCrossModuleImports = optionalCrossModuleImports,
|
||||
associatedModule = null
|
||||
reexportedInModuleWithName = reexportedIn,
|
||||
associatedModule = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun <T> CachedFileInfo.readModuleHeaderCache(f: CodedInputStream.() -> T): T? = moduleHeaderArtifact?.useCodedInputIfExists(f)
|
||||
private fun <T> CachedFileInfo.MainFileCachedInfo.readModuleHeaderCache(f: CodedInputStream.() -> T): T? =
|
||||
moduleHeaderArtifact?.useCodedInputIfExists(f)
|
||||
|
||||
private fun ModuleArtifact.fetchFileInfoFor(fileArtifact: SrcFileArtifact): List<CachedFileInfo>? {
|
||||
val moduleArtifact = this
|
||||
val mainFileCachedFileInfo = CachedFileInfo(moduleArtifact, fileArtifact)
|
||||
val mainFileCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact)
|
||||
|
||||
return mainFileCachedFileInfo.readModuleHeaderCache {
|
||||
mainFileCachedFileInfo.run {
|
||||
@@ -117,48 +136,66 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
}
|
||||
}
|
||||
|
||||
private fun CodedInputStream.fetchFileInfoForExportedPart(mainCachedFileInfo: CachedFileInfo): CachedFileInfo? {
|
||||
private fun ModuleArtifact.fetchModuleProxyFileInfo(): CachedFileInfo.ModuleProxyFileCachedInfo? {
|
||||
val mainFileCachedFileInfo = CachedFileInfo.ModuleProxyFileCachedInfo(this)
|
||||
return mainFileCachedFileInfo.moduleHeaderArtifact?.useCodedInputIfExists { loadSingleCachedFileInfo(mainFileCachedFileInfo) }
|
||||
}
|
||||
|
||||
private fun CodedInputStream.fetchFileInfoForExportedPart(mainCachedFileInfo: CachedFileInfo.MainFileCachedInfo): CachedFileInfo.ExportFileCachedInfo? {
|
||||
return ifTrue {
|
||||
loadSingleCachedFileInfo(
|
||||
CachedFileInfo(
|
||||
mainCachedFileInfo.moduleArtifact,
|
||||
mainCachedFileInfo.fileArtifact,
|
||||
isExportFileCachedInfo = true
|
||||
)
|
||||
CachedFileInfo.ExportFileCachedInfo(mainCachedFileInfo.moduleArtifact, mainCachedFileInfo.fileArtifact)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CodedOutputStream.commitSingleFileInfo(cachedFileInfo: CachedFileInfo) {
|
||||
private fun CodedOutputStream.commitSingleFileInfo(cachedFileInfo: CachedFileInfo.SerializableCachedFileInfo) {
|
||||
writeStringNoTag(cachedFileInfo.jsIrHeader.externalModuleName)
|
||||
cachedFileInfo.crossFileReferencesHash.toProtoStream(this)
|
||||
ifNotNull(cachedFileInfo.dtsHash, ::writeInt64NoTag)
|
||||
if (cachedFileInfo is CachedFileInfo.ExportFileCachedInfo) {
|
||||
ifNotNull(cachedFileInfo.tsDeclarationsHash, ::writeInt64NoTag)
|
||||
}
|
||||
commitJsIrModuleHeaderNames(cachedFileInfo.jsIrHeader)
|
||||
}
|
||||
|
||||
private fun CachedFileInfo.commitFileInfo() = runIf(!isExportFileCachedInfo) {
|
||||
moduleHeaderArtifact?.useCodedOutput {
|
||||
ifNotNull(exportFileCachedInfo) { commitSingleFileInfo(it) }
|
||||
commitSingleFileInfo(this@commitFileInfo)
|
||||
private fun CachedFileInfo.commitFileInfo() = when (this) {
|
||||
is CachedFileInfo.MainFileCachedInfo -> {
|
||||
moduleHeaderArtifact?.useCodedOutput {
|
||||
ifNotNull(exportFileCachedInfo) { commitSingleFileInfo(it) }
|
||||
commitSingleFileInfo(this@commitFileInfo)
|
||||
}
|
||||
}
|
||||
is CachedFileInfo.ModuleProxyFileCachedInfo -> {
|
||||
moduleHeaderArtifact?.useCodedOutput {
|
||||
writeStringNoTag(jsIrHeader.externalModuleName)
|
||||
crossFileReferencesHash.toProtoStream(this)
|
||||
commitJsIrModuleHeaderNames(jsIrHeader)
|
||||
}
|
||||
}
|
||||
is CachedFileInfo.ExportFileCachedInfo -> {}
|
||||
}
|
||||
|
||||
private fun ModuleArtifact.generateModuleProxyFileCachedInfo(): CachedFileInfo {
|
||||
return CachedFileInfo.ModuleProxyFileCachedInfo(
|
||||
this,
|
||||
generateProxyIrModuleWith(moduleExternalName, moduleExternalName).makeModuleHeader()
|
||||
)
|
||||
}
|
||||
|
||||
private fun ModuleArtifact.loadFileInfoFor(fileArtifact: SrcFileArtifact): List<CachedFileInfo> {
|
||||
val moduleArtifact = this
|
||||
val headers = fileArtifact.loadJsIrModuleHeaders(moduleArtifact)
|
||||
val headers = fileArtifact.loadJsIrModuleHeaders(this)
|
||||
|
||||
val mainCachedFileInfo = CachedFileInfo(headers.mainHeader, this, fileArtifact)
|
||||
val mainCachedFileInfo = CachedFileInfo.MainFileCachedInfo(this, fileArtifact, headers.mainHeader)
|
||||
|
||||
if (headers.exportHeader != null) {
|
||||
val tsDeclarationsHash = fileArtifact.loadJsIrFragments().exportFragment?.dts?.raw?.cityHash64()
|
||||
val cachedExportFileInfo = mainCachedFileInfo.readModuleHeaderCache { fetchFileInfoForExportedPart(mainCachedFileInfo) }
|
||||
mainCachedFileInfo.exportFileCachedInfo = if (cachedExportFileInfo?.dtsHash != tsDeclarationsHash) {
|
||||
CachedFileInfo(
|
||||
headers.exportHeader,
|
||||
moduleArtifact,
|
||||
mainCachedFileInfo.exportFileCachedInfo = if (cachedExportFileInfo?.tsDeclarationsHash != tsDeclarationsHash) {
|
||||
CachedFileInfo.ExportFileCachedInfo(
|
||||
this,
|
||||
fileArtifact,
|
||||
tsDeclarationsHash = tsDeclarationsHash,
|
||||
isExportFileCachedInfo = true
|
||||
headers.exportHeader,
|
||||
tsDeclarationsHash,
|
||||
)
|
||||
} else {
|
||||
cachedExportFileInfo
|
||||
@@ -169,7 +206,11 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
}
|
||||
|
||||
private val CachedFileInfo.cachedFiles: CachedFileArtifacts?
|
||||
get() = jsFileArtifact?.let { CachedFileArtifacts(it, sourceMapFileArtifact, dtsFileArtifact) }
|
||||
get() = when (this) {
|
||||
is CachedFileInfo.MainFileCachedInfo -> jsFileArtifact?.let { CachedFileArtifacts(it, sourceMapFileArtifact, null) }
|
||||
is CachedFileInfo.ExportFileCachedInfo -> jsFileArtifact?.let { CachedFileArtifacts(it, null, dtsFileArtifact) }
|
||||
is CachedFileInfo.ModuleProxyFileCachedInfo -> jsFileArtifact?.let { CachedFileArtifacts(it, null, dtsFileArtifact) }
|
||||
}
|
||||
|
||||
override fun getMainModuleAndDependencies(cacheInfo: List<CachedFileInfo>) = null to cacheInfo
|
||||
|
||||
@@ -188,23 +229,40 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
} ?: compilationOutputs
|
||||
|
||||
override fun loadJsIrModule(cacheInfo: CachedFileInfo): JsIrModule {
|
||||
if (cacheInfo !is CachedFileInfo.SerializableCachedFileInfo) {
|
||||
return generateProxyIrModuleWith(cacheInfo.jsIrHeader.externalModuleName, cacheInfo.jsIrHeader.externalModuleName)
|
||||
}
|
||||
|
||||
val fragments = cacheInfo.fileArtifact.loadJsIrFragments()
|
||||
val isExportFileCachedInfo = cacheInfo is CachedFileInfo.ExportFileCachedInfo
|
||||
return JsIrModule(
|
||||
cacheInfo.jsIrHeader.moduleName,
|
||||
cacheInfo.jsIrHeader.externalModuleName,
|
||||
listOf(if (cacheInfo.isExportFileCachedInfo) fragments.exportFragment!! else fragments.mainFragment)
|
||||
listOf(if (isExportFileCachedInfo) fragments.exportFragment!! else fragments.mainFragment),
|
||||
runIf(isExportFileCachedInfo) { cacheInfo.moduleArtifact.moduleSafeName }
|
||||
)
|
||||
}
|
||||
|
||||
override fun loadProgramHeadersFromCache(): List<CachedFileInfo> {
|
||||
return moduleArtifacts
|
||||
.flatMap { module ->
|
||||
module.fileArtifacts.flatMap {
|
||||
if (it.isModified())
|
||||
module.loadFileInfoFor(it)
|
||||
else
|
||||
module.fetchFileInfoFor(it) ?: module.loadFileInfoFor(it)
|
||||
}
|
||||
.flatMap { moduleArtifact ->
|
||||
var hasFileWithJsExportedDeclaration = false
|
||||
moduleArtifact.fileArtifacts
|
||||
.flatMap { srcFileArtifact ->
|
||||
val cachedFileInfo = if (srcFileArtifact.isModified())
|
||||
moduleArtifact.loadFileInfoFor(srcFileArtifact)
|
||||
else
|
||||
moduleArtifact.fetchFileInfoFor(srcFileArtifact) ?: moduleArtifact.loadFileInfoFor(srcFileArtifact)
|
||||
|
||||
if (!hasFileWithJsExportedDeclaration && cachedFileInfo.hasExportFile()) {
|
||||
hasFileWithJsExportedDeclaration = true
|
||||
}
|
||||
|
||||
cachedFileInfo
|
||||
}
|
||||
.butIf(hasFileWithJsExportedDeclaration) {
|
||||
it.plus(moduleArtifact.fetchModuleProxyFileInfo() ?: moduleArtifact.generateModuleProxyFileCachedInfo())
|
||||
}
|
||||
}
|
||||
.onEach { headerToCachedInfo[it.jsIrHeader] = it }
|
||||
}
|
||||
@@ -228,4 +286,6 @@ class JsPerFileCache(private val moduleArtifacts: List<ModuleArtifact>) : JsMult
|
||||
|
||||
private data class CachedFileArtifacts(val jsCodeFile: File, val sourceMapFile: File?, val tsDeclarationsFile: File?)
|
||||
private data class LoadedJsIrModuleHeaders(val mainHeader: JsIrModuleHeader, val exportHeader: JsIrModuleHeader?)
|
||||
}
|
||||
|
||||
private fun List<JsPerFileCache.CachedFileInfo>.hasExportFile(): Boolean = size > 1
|
||||
}
|
||||
+7
-2
@@ -47,6 +47,12 @@ val String.safeModuleName: String
|
||||
val IrModuleFragment.safeName: String
|
||||
get() = name.asString().safeModuleName
|
||||
|
||||
fun generateProxyIrModuleWith(safeName: String, externalName: String) = JsIrModule(
|
||||
safeName,
|
||||
externalName,
|
||||
listOf(JsIrProgramFragment(safeName, "<proxy-file>"))
|
||||
)
|
||||
|
||||
enum class JsGenerationGranularity {
|
||||
WHOLE_PROGRAM,
|
||||
PER_MODULE,
|
||||
@@ -301,10 +307,9 @@ class IrModuleToJsTransformer(
|
||||
}
|
||||
|
||||
private fun IrAndExportedDeclarations.toJsIrProxyModule(): JsIrModule {
|
||||
return JsIrModule(
|
||||
return generateProxyIrModuleWith(
|
||||
fragment.safeName,
|
||||
moduleFragmentToNameMapper.getExternalNameFor(fragment),
|
||||
listOf(JsIrProgramFragment(fragment.safeName, "<proxy-file>"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user