[JS IR] DTS generation refactoring

Keep a DTS fragment in JsIrProgramFragment
 for each file and build the module DTS from them.
This commit is contained in:
Alexander Korepanov
2022-12-01 22:12:09 +01:00
committed by Space Team
parent 03a43e0dc0
commit 0a35d84193
10 changed files with 110 additions and 95 deletions
@@ -119,8 +119,6 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
return K2JSCompilerArguments() return K2JSCompilerArguments()
} }
private data class TransformResult(val out: CompilationOutputs, val dts: String?)
private class Ir2JsTransformer( private class Ir2JsTransformer(
val arguments: K2JSCompilerArguments, val arguments: K2JSCompilerArguments,
val module: ModulesStructure, val module: ModulesStructure,
@@ -150,18 +148,16 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
) )
} }
private fun makeJsCodeGeneratorAndDts(): Pair<JsCodeGenerator, String?> { private fun makeJsCodeGenerator(): JsCodeGenerator {
val ir = lowerIr() val ir = lowerIr()
val transformer = IrModuleToJsTransformer(ir.context, mainCallArguments, ir.moduleFragmentToUniqueName) val transformer = IrModuleToJsTransformer(ir.context, mainCallArguments, ir.moduleFragmentToUniqueName)
val mode = TranslationMode.fromFlags(arguments.irDce, arguments.irPerModule, arguments.irMinimizedMemberNames) val mode = TranslationMode.fromFlags(arguments.irDce, arguments.irPerModule, arguments.irMinimizedMemberNames)
return transformer.makeJsCodeGeneratorAndDts(ir.allModules, mode) return transformer.makeJsCodeGenerator(ir.allModules, mode)
} }
fun compileAndTransformIrNew(): TransformResult { fun compileAndTransformIrNew(): CompilationOutputs {
val (generator, dts) = makeJsCodeGeneratorAndDts() return makeJsCodeGenerator().generateJsCode(relativeRequirePath = true, outJsProgram = false)
val out = generator.generateJsCode(relativeRequirePath = true, outJsProgram = false)
return TransformResult(out, dts)
} }
} }
@@ -308,6 +304,8 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
} }
if (arguments.irProduceJs) { if (arguments.irProduceJs) {
val moduleKind = configurationJs[JSConfigurationKeys.MODULE_KIND] ?: error("cannot get 'module kind' from configuration")
messageCollector.report(INFO, "Produce executable: $outputDirPath") messageCollector.report(INFO, "Produce executable: $outputDirPath")
messageCollector.report(INFO, "Cache directory: ${arguments.cacheDirectory}") messageCollector.report(INFO, "Cache directory: ${arguments.cacheDirectory}")
@@ -316,14 +314,14 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
val jsExecutableProducer = JsExecutableProducer( val jsExecutableProducer = JsExecutableProducer(
mainModuleName = moduleName, mainModuleName = moduleName,
moduleKind = configurationJs[JSConfigurationKeys.MODULE_KIND]!!, moduleKind = moduleKind,
sourceMapsInfo = SourceMapsInfo.from(configurationJs), sourceMapsInfo = SourceMapsInfo.from(configurationJs),
caches = icCaches, caches = icCaches,
relativeRequirePath = true relativeRequirePath = true
) )
val (outputs, rebuiltModules) = jsExecutableProducer.buildExecutable(arguments.irPerModule, outJsProgram = false) val (outputs, rebuiltModules) = jsExecutableProducer.buildExecutable(arguments.irPerModule, outJsProgram = false)
outputs.write(outputDir, outputName) outputs.write(outputDir, outputName, arguments.generateDts, moduleName, moduleKind)
messageCollector.report(INFO, "Executable production duration (IC): ${System.currentTimeMillis() - beforeIc2Js}ms") messageCollector.report(INFO, "Executable production duration (IC): ${System.currentTimeMillis() - beforeIc2Js}ms")
for ((event, duration) in jsExecutableProducer.getStopwatchLaps()) { for ((event, duration) in jsExecutableProducer.getStopwatchLaps()) {
@@ -390,16 +388,11 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
try { try {
val ir2JsTransformer = Ir2JsTransformer(arguments, module, phaseConfig, messageCollector, mainCallArguments) val ir2JsTransformer = Ir2JsTransformer(arguments, module, phaseConfig, messageCollector, mainCallArguments)
val (outputs, tsDefinitions) = ir2JsTransformer.compileAndTransformIrNew() val outputs = ir2JsTransformer.compileAndTransformIrNew()
messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms") messageCollector.report(INFO, "Executable production duration: ${System.currentTimeMillis() - start}ms")
outputs.write(outputDir, outputName) outputs.write(outputDir, outputName, arguments.generateDts, moduleName, moduleKind)
if (tsDefinitions != null) {
val dtsFile = outputDir.resolve("$outputName.d.ts")
dtsFile.writeText(tsDefinitions)
}
} catch (e: CompilationException) { } catch (e: CompilationException) {
messageCollector.report( messageCollector.report(
ERROR, ERROR,
@@ -707,7 +700,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
return icCaches return icCaches
} }
private fun CompilationOutputs.write(outputDir: File, outputName: String) { private fun CompilationOutputs.write(outputDir: File, outputName: String, genDTS: Boolean, moduleName: String, moduleKind: ModuleKind) {
val outputFile = outputDir.resolve("$outputName.js") val outputFile = outputDir.resolve("$outputName.js")
outputFile.parentFile.mkdirs() outputFile.parentFile.mkdirs()
outputFile.write(this) outputFile.write(this)
@@ -717,6 +710,11 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
it.write(content) it.write(content)
} }
} }
if (genDTS) {
val dtsFile = outputDir.resolve("$outputName.d.ts")
dtsFile.writeText(getFullTsDefinition(moduleName, moduleKind))
}
} }
private fun File.write(outputs: CompilationOutputs) { private fun File.write(outputs: CompilationOutputs) {
@@ -11,6 +11,8 @@ import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnb
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
import org.jetbrains.kotlin.ir.backend.js.export.TypeScriptFragment
import org.jetbrains.kotlin.ir.backend.js.export.toTypeScript
import org.jetbrains.kotlin.ir.backend.js.lower.collectNativeImplementations import org.jetbrains.kotlin.ir.backend.js.lower.collectNativeImplementations
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
@@ -24,19 +26,30 @@ import org.jetbrains.kotlin.js.backend.ast.JsProgram
import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File import java.io.File
class CompilerResult( class CompilerResult(
val outputs: Map<TranslationMode, CompilationOutputs>, val outputs: Map<TranslationMode, CompilationOutputs>,
val tsDefinitions: String? = null
) )
class CompilationOutputs( class CompilationOutputs(
val jsCode: String, val jsCode: String,
val tsDefinitions: TypeScriptFragment? = null,
val jsProgram: JsProgram? = null, val jsProgram: JsProgram? = null,
val sourceMap: String? = null, val sourceMap: String? = null,
val dependencies: Iterable<Pair<String, CompilationOutputs>> = emptyList() val dependencies: Iterable<Pair<String, CompilationOutputs>> = emptyList()
) ) {
fun addDependencies(depends: Iterable<Pair<String, CompilationOutputs>>): CompilationOutputs {
return CompilationOutputs(jsCode, tsDefinitions, jsProgram, sourceMap, depends)
}
fun getFullTsDefinition(name: String, moduleKind: ModuleKind): String {
val allTsDefinitions = dependencies.mapNotNull { it.second.tsDefinitions } + listOfNotNull(tsDefinitions)
return allTsDefinitions.toTypeScript(name, moduleKind)
}
}
class LoweredIr( class LoweredIr(
val context: JsIrBackendContext, val context: JsIrBackendContext,
@@ -25,12 +25,19 @@ private const val declareExorted = "export $declare"
private const val NonExistent = "__NonExistent" private const val NonExistent = "__NonExistent"
private const val syntheticObjectNameSeparator = '$' private const val syntheticObjectNameSeparator = '$'
fun ExportedModule.toTypeScript(): String { @JvmInline
return ExportModelToTsDeclarations().generateTypeScript(name, this) value class TypeScriptFragment(val raw: String)
fun List<ExportedDeclaration>.toTypeScriptFragment(moduleKind: ModuleKind): TypeScriptFragment {
return ExportModelToTsDeclarations().generateTypeScriptFragment(moduleKind, this)
} }
fun List<ExportedDeclaration>.toTypeScript(moduleKind: ModuleKind): String { fun List<TypeScriptFragment>.joinTypeScriptFragments(): TypeScriptFragment {
return ExportModelToTsDeclarations().generateTypeScript(moduleKind, this) return TypeScriptFragment(joinToString("\n") { it.raw })
}
fun List<TypeScriptFragment>.toTypeScript(name: String, moduleKind: ModuleKind): String {
return ExportModelToTsDeclarations().generateTypeScript(name, moduleKind, this)
} }
// TODO: Support module kinds other than plain // TODO: Support module kinds other than plain
@@ -40,24 +47,24 @@ class ExportModelToTsDeclarations {
private val ModuleKind.indent: String private val ModuleKind.indent: String
get() = if (this == ModuleKind.PLAIN) " " else "" get() = if (this == ModuleKind.PLAIN) " " else ""
fun generateTypeScript(name: String, module: ExportedModule): String { fun generateTypeScript(name: String, moduleKind: ModuleKind, declarations: List<TypeScriptFragment>): String {
val types = """ val types = """
type $Nullable<T> = T | null | undefined type $Nullable<T> = T | null | undefined
""".trimIndent().prependIndent(module.moduleKind.indent) + "\n" """.trimIndent().prependIndent(moduleKind.indent) + "\n"
val declarationsDts = types + module.declarations.toTypeScript(module.moduleKind) val declarationsDts = types + declarations.joinTypeScriptFragments().raw
val namespaceName = sanitizeName(name, withHash = false) val namespaceName = sanitizeName(name, withHash = false)
return when (module.moduleKind) { return when (moduleKind) {
ModuleKind.PLAIN -> "declare namespace $namespaceName {\n$declarationsDts\n}\n" ModuleKind.PLAIN -> "declare namespace $namespaceName {\n$declarationsDts\n}\n"
ModuleKind.AMD, ModuleKind.COMMON_JS, ModuleKind.ES -> declarationsDts ModuleKind.AMD, ModuleKind.COMMON_JS, ModuleKind.ES -> declarationsDts
ModuleKind.UMD -> "$declarationsDts\nexport as namespace $namespaceName;" ModuleKind.UMD -> "$declarationsDts\nexport as namespace $namespaceName;"
} }
} }
fun generateTypeScript(moduleKind: ModuleKind, declarations: List<ExportedDeclaration>): String { fun generateTypeScriptFragment(moduleKind: ModuleKind, declarations: List<ExportedDeclaration>): TypeScriptFragment {
return declarations.toTypeScript(moduleKind) return TypeScriptFragment(declarations.toTypeScript(moduleKind))
} }
private fun List<ExportedDeclaration>.toTypeScript(moduleKind: ModuleKind): String { private fun List<ExportedDeclaration>.toTypeScript(moduleKind: ModuleKind): String {
@@ -100,7 +100,7 @@ class JsExecutableProducer(
it.jsIrHeader.externalModuleName to it.compileModule(it.jsIrHeader.externalModuleName, false) it.jsIrHeader.externalModuleName to it.compileModule(it.jsIrHeader.externalModuleName, false)
} }
stopwatch.stop() stopwatch.stop()
val compilationOut = CompilationOutputs(mainModule.jsCode, mainModule.jsProgram, mainModule.sourceMap, dependencies) val compilationOut = mainModule.addDependencies(dependencies)
return BuildResult(compilationOut, rebuildModules) return BuildResult(compilationOut, rebuildModules)
} }
} }
@@ -74,7 +74,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
fun fetchCompiledJsCode(artifact: ModuleArtifact) = artifact.artifactsDir?.let { cacheDir -> fun fetchCompiledJsCode(artifact: ModuleArtifact) = artifact.artifactsDir?.let { cacheDir ->
val jsCode = File(cacheDir, CACHED_MODULE_JS).ifExists { readText() } val jsCode = File(cacheDir, CACHED_MODULE_JS).ifExists { readText() }
val sourceMap = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { readText() } val sourceMap = File(cacheDir, CACHED_MODULE_JS_MAP).ifExists { readText() }
jsCode?.let { CompilationOutputs(it, null, sourceMap) } jsCode?.let { CompilationOutputs(it, null, null, sourceMap) }
} }
fun commitCompiledJsCode(artifact: ModuleArtifact, compilationOutputs: CompilationOutputs) = artifact.artifactsDir?.let { cacheDir -> fun commitCompiledJsCode(artifact: ModuleArtifact, compilationOutputs: CompilationOutputs) = artifact.artifactsDir?.let { cacheDir ->
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.js.sourceMap.SourceMapBuilderConsumer
import org.jetbrains.kotlin.js.util.TextOutputImpl import org.jetbrains.kotlin.js.util.TextOutputImpl
import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.io.ByteArrayOutputStream import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
import java.io.File import java.io.File
import java.util.* import java.util.*
@@ -107,19 +107,15 @@ class IrModuleToJsTransformer(
private val isEsModules = moduleKind == ModuleKind.ES private val isEsModules = moduleKind == ModuleKind.ES
private val sourceMapInfo = SourceMapsInfo.from(backendContext.configuration) private val sourceMapInfo = SourceMapsInfo.from(backendContext.configuration)
private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<Pair<IrFile, List<ExportedDeclaration>>>) private class IrFileExports(val file: IrFile, val exports: List<ExportedDeclaration>, val tsDeclarations: TypeScriptFragment?)
private fun List<IrAndExportedDeclarations>.flatExportedDeclarations(): List<ExportedDeclaration> { private class IrAndExportedDeclarations(val fragment: IrModuleFragment, val files: List<IrFileExports>)
return this.flatMap { data -> data.files.flatMap { it.second } }
}
private fun associateIrAndExport(modules: Iterable<IrModuleFragment>): List<IrAndExportedDeclarations> { private fun associateIrAndExport(modules: Iterable<IrModuleFragment>): List<IrAndExportedDeclarations> {
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules) val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
return modules.map { module -> return modules.map { module ->
val files = module.files.map { file -> val files = exportModelGenerator.generateExportWithExternals(module.files)
file to exportModelGenerator.generateExportWithExternals(file)
}
IrAndExportedDeclarations(module, files) IrAndExportedDeclarations(module, files)
} }
} }
@@ -144,9 +140,6 @@ class IrModuleToJsTransformer(
fun generateModule(modules: Iterable<IrModuleFragment>, modes: Set<TranslationMode>, relativeRequirePath: Boolean): CompilerResult { fun generateModule(modules: Iterable<IrModuleFragment>, modes: Set<TranslationMode>, relativeRequirePath: Boolean): CompilerResult {
val exportData = associateIrAndExport(modules) val exportData = associateIrAndExport(modules)
val dts = runIf(shouldGenerateTypeScriptDefinitions) {
ExportedModule(mainModuleName, moduleKind, exportData.flatExportedDeclarations()).toTypeScript()
}
doStaticMembersLowering(modules) doStaticMembersLowering(modules)
val result = EnumMap<TranslationMode, CompilationOutputs>(TranslationMode::class.java) val result = EnumMap<TranslationMode, CompilationOutputs>(TranslationMode::class.java)
@@ -163,39 +156,41 @@ class IrModuleToJsTransformer(
result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true) result[it] = makeJsCodeGeneratorFromIr(exportData, it).generateJsCode(relativeRequirePath, true)
} }
return CompilerResult(result, dts) return CompilerResult(result)
} }
fun makeJsCodeGeneratorAndDts(modules: Iterable<IrModuleFragment>, mode: TranslationMode): Pair<JsCodeGenerator, String?> { fun makeJsCodeGenerator(modules: Iterable<IrModuleFragment>, mode: TranslationMode): JsCodeGenerator {
val exportData = associateIrAndExport(modules) val exportData = associateIrAndExport(modules)
val dts = runIf(shouldGenerateTypeScriptDefinitions) {
ExportedModule(mainModuleName, moduleKind, exportData.flatExportedDeclarations()).toTypeScript()
}
doStaticMembersLowering(modules) doStaticMembersLowering(modules)
if (mode.dce) { if (mode.dce) {
eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects) eliminateDeadDeclarations(modules, backendContext, removeUnusedAssociatedObjects)
} }
return makeJsCodeGeneratorFromIr(exportData, mode) to dts return makeJsCodeGeneratorFromIr(exportData, mode)
} }
fun makeIrFragmentsGenerators(files: Collection<IrFile>, allModules: Collection<IrModuleFragment>): List<() -> JsIrProgramFragment> { fun makeIrFragmentsGenerators(files: Collection<IrFile>, allModules: Collection<IrModuleFragment>): List<() -> JsIrProgramFragment> {
val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules) val exportModelGenerator = ExportModelGenerator(backendContext, generateNamespacesForPackages = !isEsModules)
val exportData = exportModelGenerator.generateExportWithExternals(files)
val exportData = files.map { it to exportModelGenerator.generateExportWithExternals(it) }
doStaticMembersLowering(allModules) doStaticMembersLowering(allModules)
return exportData.map { (file, exports) -> return exportData.map {
{ generateProgramFragment(file, exports, minimizedMemberNames = false) } { generateProgramFragment(it, minimizedMemberNames = false) }
} }
} }
private fun ExportModelGenerator.generateExportWithExternals(irFile: IrFile): List<ExportedDeclaration> { private fun ExportModelGenerator.generateExportWithExternals(irFiles: Collection<IrFile>): List<IrFileExports> {
val exports = generateExport(irFile) return irFiles.map { irFile ->
val additionalExports = backendContext.externalPackageFragment[irFile.symbol]?.let { generateExport(it) } ?: emptyList() val exports = generateExport(irFile)
return additionalExports + exports val additionalExports = backendContext.externalPackageFragment[irFile.symbol]?.let { generateExport(it) } ?: emptyList()
val allExports = additionalExports + exports
val tsDeclarations = runIf(shouldGenerateTypeScriptDefinitions) {
allExports.ifNotEmpty { toTypeScriptFragment(moduleKind) }
}
IrFileExports(irFile, allExports, tsDeclarations)
}
} }
private fun IrModuleFragment.externalModuleName(): String { private fun IrModuleFragment.externalModuleName(): String {
@@ -213,8 +208,8 @@ class IrModuleToJsTransformer(
JsIrModule( JsIrModule(
data.fragment.safeName, data.fragment.safeName,
data.fragment.externalModuleName(), data.fragment.externalModuleName(),
data.files.map { (file, exports) -> data.files.map {
generateProgramFragment(file, exports, mode.minimizedMemberNames) generateProgramFragment(it, mode.minimizedMemberNames)
} }
) )
} }
@@ -226,11 +221,7 @@ class IrModuleToJsTransformer(
private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH) private val generateFilePaths = backendContext.configuration.getBoolean(JSConfigurationKeys.GENERATE_COMMENTS_WITH_FILE_PATH)
private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP) private val pathPrefixMap = backendContext.configuration.getMap(JSConfigurationKeys.FILE_PATHS_PREFIX_MAP)
private fun generateProgramFragment( private fun generateProgramFragment(fileExports: IrFileExports, minimizedMemberNames: Boolean): JsIrProgramFragment {
file: IrFile,
exports: List<ExportedDeclaration>,
minimizedMemberNames: Boolean
): JsIrProgramFragment {
val nameGenerator = JsNameLinkingNamer(backendContext, minimizedMemberNames) val nameGenerator = JsNameLinkingNamer(backendContext, minimizedMemberNames)
val globalNameScope = NameTable<IrDeclaration>() val globalNameScope = NameTable<IrDeclaration>()
@@ -241,9 +232,9 @@ class IrModuleToJsTransformer(
globalNameScope = globalNameScope globalNameScope = globalNameScope
) )
val result = JsIrProgramFragment(file.fqName.asString()).apply { val result = JsIrProgramFragment(fileExports.file.fqName.asString()).apply {
if (shouldGeneratePolyfills) { if (shouldGeneratePolyfills) {
polyfills.statements += backendContext.polyfills.getAllPolyfillsFor(file) polyfills.statements += backendContext.polyfills.getAllPolyfillsFor(fileExports.file)
} }
} }
@@ -251,20 +242,17 @@ class IrModuleToJsTransformer(
val globalNames = NameTable<String>(globalNameScope) val globalNames = NameTable<String>(globalNameScope)
val exportStatements = val exportStatements =
ExportModelToJsStatements(staticContext, { globalNames.declareFreshName(it, it) }).generateModuleExport( ExportModelToJsStatements(staticContext, { globalNames.declareFreshName(it, it) }).generateModuleExport(
ExportedModule(mainModuleName, moduleKind, exports), ExportedModule(mainModuleName, moduleKind, fileExports.exports),
internalModuleName, internalModuleName,
isEsModules isEsModules
) )
result.exports.statements += exportStatements result.exports.statements += exportStatements
result.dts = fileExports.tsDeclarations
if (shouldGenerateTypeScriptDefinitions && exports.isNotEmpty()) {
result.dts = exports.toTypeScript(moduleKind)
}
val statements = result.declarations.statements val statements = result.declarations.statements
val fileStatements = file.accept(IrFileToJsTransformer(useBareParameterNames = true), staticContext).statements val fileStatements = fileExports.file.accept(IrFileToJsTransformer(useBareParameterNames = true), staticContext).statements
if (fileStatements.isNotEmpty()) { if (fileStatements.isNotEmpty()) {
var startComment = "" var startComment = ""
@@ -273,7 +261,7 @@ class IrModuleToJsTransformer(
} }
if (generateRegionComments || generateFilePaths) { if (generateRegionComments || generateFilePaths) {
val originalPath = file.path val originalPath = fileExports.file.path
val path = pathPrefixMap.entries val path = pathPrefixMap.entries
.find { (k, _) -> originalPath.startsWith(k) } .find { (k, _) -> originalPath.startsWith(k) }
?.let { (k, v) -> v + originalPath.substring(k.length) } ?.let { (k, v) -> v + originalPath.substring(k.length) }
@@ -303,7 +291,7 @@ class IrModuleToJsTransformer(
result.initializers.statements += staticContext.initializerBlock.statements result.initializers.statements += staticContext.initializerBlock.statements
if (mainArguments != null) { if (mainArguments != null) {
JsMainFunctionDetector(backendContext).getMainFunctionOrNull(file)?.let { JsMainFunctionDetector(backendContext).getMainFunctionOrNull(fileExports.file)?.let {
val jsName = staticContext.getNameForStaticFunction(it) val jsName = staticContext.getNameForStaticFunction(it)
val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
val generateContinuation = it.isLoweredSuspendFunction(backendContext) val generateContinuation = it.isLoweredSuspendFunction(backendContext)
@@ -311,14 +299,14 @@ class IrModuleToJsTransformer(
} }
} }
backendContext.testFunsPerFile[file]?.let { backendContext.testFunsPerFile[fileExports.file]?.let {
result.testFunInvocation = JsInvocation(staticContext.getNameForStaticFunction(it).makeRef()).makeStmt() result.testFunInvocation = JsInvocation(staticContext.getNameForStaticFunction(it).makeRef()).makeStmt()
result.suiteFn = staticContext.getNameForStaticFunction(backendContext.suiteFun!!.owner) result.suiteFn = staticContext.getNameForStaticFunction(backendContext.suiteFun!!.owner)
} }
result.importedModules += nameGenerator.importedModules result.importedModules += nameGenerator.importedModules
val definitionSet = file.declarations.toSet() val definitionSet = fileExports.file.declarations.toSet()
fun computeTag(declaration: IrDeclaration): String? { fun computeTag(declaration: IrDeclaration): String? {
val tag = (backendContext.irFactory as IdSignatureRetriever).declarationSignature(declaration)?.toString() val tag = (backendContext.irFactory as IdSignatureRetriever).declarationSignature(declaration)?.toString()
@@ -341,7 +329,7 @@ class IrModuleToJsTransformer(
result.imports[tag] = importExpression result.imports[tag] = importExpression
} }
file.declarations.forEach { fileExports.file.declarations.forEach {
computeTag(it)?.let { tag -> computeTag(it)?.let { tag ->
result.definitions += tag result.definitions += tag
} }
@@ -422,7 +410,7 @@ private fun generateWrappedModuleBody(
} }
} }
return CompilationOutputs(mainModule.jsCode, mainModule.jsProgram, mainModule.sourceMap, dependencies) return mainModule.addDependencies(dependencies)
} else { } else {
return generateSingleWrappedModuleBody( return generateSingleWrappedModuleBody(
mainModuleName, mainModuleName,
@@ -484,6 +472,7 @@ fun generateSingleWrappedModuleBody(
return CompilationOutputs( return CompilationOutputs(
jsCode.toString(), jsCode.toString(),
fragments.mapNotNull { it.dts }.ifNotEmpty { joinTypeScriptFragments() },
program.takeIf { outJsProgram }, program.takeIf { outJsProgram },
sourceMapBuilder?.build() sourceMapBuilder?.build()
) )
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.ir.backend.js.export.TypeScriptFragment
import org.jetbrains.kotlin.ir.backend.js.utils.toJsIdentifier import org.jetbrains.kotlin.ir.backend.js.utils.toJsIdentifier
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import java.io.File import java.io.File
@@ -16,7 +17,7 @@ class JsIrProgramFragment(val packageFqn: String) {
val exports = JsCompositeBlock() val exports = JsCompositeBlock()
val importedModules = mutableListOf<JsImportedModule>() val importedModules = mutableListOf<JsImportedModule>()
val imports = mutableMapOf<String, JsExpression>() val imports = mutableMapOf<String, JsExpression>()
var dts: String? = null var dts: TypeScriptFragment? = null
val classes = mutableMapOf<JsName, JsIrIcClassModel>() val classes = mutableMapOf<JsName, JsIrIcClassModel>()
val initializers = JsCompositeBlock() val initializers = JsCompositeBlock()
var mainFunction: JsStatement? = null var mainFunction: JsStatement? = null
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.utils.serialization package org.jetbrains.kotlin.ir.backend.js.utils.serialization
import org.jetbrains.kotlin.ir.backend.js.export.TypeScriptFragment
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrIcClassModel import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrIcClassModel
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrProgramFragment
import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope import org.jetbrains.kotlin.ir.backend.js.utils.emptyScope
@@ -81,7 +82,9 @@ class JsIrAstDeserializer : JsAstDeserializerBase() {
fragment.suiteFn = deserializeName(proto.suiteFunction) fragment.suiteFn = deserializeName(proto.suiteFunction)
} }
fragment.dts = proto.dts if (proto.hasDts()) {
fragment.dts = TypeScriptFragment(proto.dts)
}
fragment.definitions += proto.definitionsList.map { deserializeString(it) } fragment.definitions += proto.definitionsList.map { deserializeString(it) }
return fragment return fragment
@@ -81,7 +81,7 @@ class JsIrAstSerializer: JsAstSerializerBase() {
} }
fragment.dts?.let { fragment.dts?.let {
fragmentBuilder.dts = it fragmentBuilder.dts = it.raw
} }
fragment.suiteFn?.let { fragment.suiteFn?.let {
@@ -105,8 +105,7 @@ class JsIrBackendFacade(
relativeRequirePath = false relativeRequirePath = false
) )
jsExecutableProducer.buildExecutable(it.perModule, true).compilationOut jsExecutableProducer.buildExecutable(it.perModule, true).compilationOut
}, }
tsDefinitions = null
) )
return BinaryArtifacts.Js.JsIrArtifact( return BinaryArtifacts.Js.JsIrArtifact(
outputFile, compiledModule, testServices.jsIrIncrementalDataProvider.getCacheForModule(module) outputFile, compiledModule, testServices.jsIrIncrementalDataProvider.getCacheForModule(module)
@@ -164,7 +163,8 @@ class JsIrBackendFacade(
val isEsModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives || val isEsModules = JsEnvironmentConfigurationDirectives.ES_MODULES in module.directives ||
module.directives[JsEnvironmentConfigurationDirectives.MODULE_KIND].contains(ModuleKind.ES) module.directives[JsEnvironmentConfigurationDirectives.MODULE_KIND].contains(ModuleKind.ES)
val outputFile = File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL) + module.kind.extension) val outputFile =
File(JsEnvironmentConfigurator.getJsModuleArtifactPath(testServices, module.name, TranslationMode.FULL) + module.kind.extension)
val transformer = IrModuleToJsTransformer( val transformer = IrModuleToJsTransformer(
loweredIr.context, loweredIr.context,
@@ -174,15 +174,15 @@ class JsIrBackendFacade(
isEsModules && granularity != JsGenerationGranularity.WHOLE_PROGRAM isEsModules && granularity != JsGenerationGranularity.WHOLE_PROGRAM
) )
) )
// If runIrDce then include DCE results // If runIrDce then include DCE results
// If perModuleOnly then skip whole program // If perModuleOnly then skip whole program
// (it.dce => runIrDce) && (perModuleOnly => it.perModule) // (it.dce => runIrDce) && (perModuleOnly => it.perModule)
val translationModes = TranslationMode.values() val translationModes = TranslationMode.values()
.filter { (it.dce || !onlyIrDce) && (!it.dce || runIrDce) && (!perModuleOnly || it.perModule) } .filter { (it.dce || !onlyIrDce) && (!it.dce || runIrDce) && (!perModuleOnly || it.perModule) }
.filter { it.dce == it.minimizedMemberNames } .filter { it.dce == it.minimizedMemberNames }
.toSet() .toSet()
val compilationOut = transformer.generateModule(loweredIr.allModules, translationModes, false) val compilationOut = transformer.generateModule(loweredIr.allModules, translationModes, false)
return BinaryArtifacts.Js.JsIrArtifact(outputFile, compilationOut).dump(module) return BinaryArtifacts.Js.JsIrArtifact(outputFile, compilationOut).dump(module)
} }
private fun IrModuleFragment.resolveTestPathes() { private fun IrModuleFragment.resolveTestPathes() {
@@ -195,7 +195,7 @@ class JsIrBackendFacade(
val filesToLoad = module.files.takeIf { !firstTimeCompilation }?.map { "/${it.relativePath}" }?.toSet() val filesToLoad = module.files.takeIf { !firstTimeCompilation }?.map { "/${it.relativePath}" }?.toSet()
val messageLogger = configuration.irMessageLogger val messageLogger = configuration.irMessageLogger
val symbolTable = SymbolTable(IdSignatureDescriptor(JsManglerDesc), IrFactoryImplForJsIC(WholeWorldStageController()),) val symbolTable = SymbolTable(IdSignatureDescriptor(JsManglerDesc), IrFactoryImplForJsIC(WholeWorldStageController()))
val moduleDescriptor = testServices.moduleDescriptorProvider.getModuleDescriptor(module) val moduleDescriptor = testServices.moduleDescriptorProvider.getModuleDescriptor(module)
val mainModuleLib = testServices.jsLibraryProvider.getCompiledLibraryByDescriptor(moduleDescriptor) val mainModuleLib = testServices.jsLibraryProvider.getCompiledLibraryByDescriptor(moduleDescriptor)
@@ -244,9 +244,13 @@ class JsIrBackendFacade(
} }
if (generateDts) { if (generateDts) {
val tsFiles = compilerResult.outputs.entries.associate { it.value.getFullTsDefinition(moduleId, moduleKind) to it.key }
val tsDefinitions = tsFiles.entries.singleOrNull()?.key
?: error("[${tsFiles.values.joinToString { it.name }}] make different TypeScript")
outputFile outputFile
.withReplacedExtensionOrNull("_v5${moduleKind.extension}", ".d.ts")!! .withReplacedExtensionOrNull("_v5${moduleKind.extension}", ".d.ts")!!
.write(compilerResult.tsDefinitions ?: error("No ts definitions")) .write(tsDefinitions)
} }
return this return this