[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:
committed by
Space Team
parent
03a43e0dc0
commit
0a35d84193
@@ -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,
|
||||||
|
|||||||
+17
-10
@@ -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 {
|
||||||
|
|||||||
+1
-1
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -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 ->
|
||||||
|
|||||||
+35
-46
@@ -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()
|
||||||
)
|
)
|
||||||
|
|||||||
+2
-1
@@ -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
|
||||||
|
|||||||
+4
-1
@@ -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
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user