[IR BE] Add runtime compilation benchmarks for IR JS compiler

This commit is contained in:
Roman Artemev
2019-09-11 17:53:30 +03:00
committed by romanart
parent e6c855111e
commit 44dfae53f0
3 changed files with 610 additions and 6 deletions
@@ -8,18 +8,16 @@ package org.jetbrains.kotlin.library.impl
import org.jetbrains.kotlin.library.*
abstract class IrWriterImpl(val irLayout: IrKotlinLibraryLayout) : IrWriter {
init {
irLayout.irDir.mkdirs()
}
override fun addDataFlowGraph(dataFlowGraph: ByteArray) {
irLayout.dataFlowGraphFile.writeBytes(dataFlowGraph)
}
}
class IrMonoliticWriterImpl(irLayout: IrKotlinLibraryLayout) : IrWriterImpl(irLayout) {
class IrMonoliticWriterImpl(_irLayout: IrKotlinLibraryLayout) : IrWriterImpl(_irLayout) {
override fun addIr(ir: SerializedIrModule) {
irLayout.irDir.mkdirs()
with(ir.files.sortedBy { it.path }) {
IrArrayWriter(map { it.fileData }).writeIntoFile(irLayout.irFiles.absolutePath)
IrArrayWriter(map { it.declarations }).writeIntoFile(irLayout.irDeclarations.absolutePath)
@@ -31,8 +29,10 @@ class IrMonoliticWriterImpl(irLayout: IrKotlinLibraryLayout) : IrWriterImpl(irLa
}
}
class IrPerFileWriterImpl(irLayout: IrKotlinLibraryLayout) : IrWriterImpl(irLayout) {
class IrPerFileWriterImpl(_irLayout: IrKotlinLibraryLayout) : IrWriterImpl(_irLayout) {
override fun addIr(ir: SerializedIrModule) {
irLayout.irDir.mkdirs()
ir.files.forEach {
serializeFile(it)
}
+1
View File
@@ -43,6 +43,7 @@ dependencies {
testCompile(project(":js:js.serializer"))
testCompile(project(":js:js.dce"))
testCompile(project(":js:js.engines"))
testCompile(project(":compiler:incremental-compilation-impl"))
testCompile(commonDep("junit:junit"))
testCompile(projectTests(":kotlin-build-common"))
testCompile(projectTests(":generators:test-generator"))
@@ -0,0 +1,603 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. 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.benchmarks
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel
import org.jetbrains.kotlin.backend.common.serialization.KlibIrVersion
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.descriptors.konan.kotlinLibrary
import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.incremental.EmptyICReporter
import org.jetbrains.kotlin.incremental.IncrementalJsCompilerRunner
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
import org.jetbrains.kotlin.incremental.withJsIC
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrModuleSerializer
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.library.KotlinLibraryVersioning
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.SerializedIrModule
import org.jetbrains.kotlin.library.impl.BuiltInsPlatform
import org.jetbrains.kotlin.library.impl.KotlinLibraryOnlyIrWriter
import org.jetbrains.kotlin.library.impl.createKotlinLibrary
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.psi2ir.generators.createGeneratorContext
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import java.io.File
import org.jetbrains.kotlin.konan.file.File as KonanFile
@Ignore
class GenerateIrRuntime {
private val lookupTracker: LookupTracker = LookupTracker.DO_NOTHING
private val logger = object : LoggingContext {
override var inVerbosePhase = false
override fun log(message: () -> String) {}
}
fun loadKlib(klibPath: String, isPacked: Boolean) = createKotlinLibrary(KonanFile("$klibPath${if (isPacked) ".klib" else ""}"))
private fun buildConfiguration(environment: KotlinCoreEnvironment): CompilerConfiguration {
val runtimeConfiguration = environment.configuration.copy()
runtimeConfiguration.put(CommonConfigurationKeys.MODULE_NAME, "JS_IR_RUNTIME")
runtimeConfiguration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.UMD)
runtimeConfiguration.languageVersionSettings = LanguageVersionSettingsImpl(
LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE,
specificFeatures = mapOf(
LanguageFeature.AllowContractsForCustomFunctions to LanguageFeature.State.ENABLED,
LanguageFeature.MultiPlatformProjects to LanguageFeature.State.ENABLED
),
analysisFlags = mapOf(
AnalysisFlags.useExperimental to listOf("kotlin.contracts.ExperimentalContracts", "kotlin.Experimental", "kotlin.ExperimentalMultiplatform"),
AnalysisFlags.allowResultReturnType to true
)
)
return runtimeConfiguration
}
private val CompilerConfiguration.metadataVersion
get() = get(CommonConfigurationKeys.METADATA_VERSION) as? KlibMetadataVersion ?: KlibMetadataVersion.INSTANCE
private val environment =
KotlinCoreEnvironment.createForTests(Disposable { }, CompilerConfiguration(), EnvironmentConfigFiles.JS_CONFIG_FILES)
private val configuration = buildConfiguration(environment)
private val project = environment.project
private val phaseConfig = PhaseConfig(jsPhases)
private val metadataVersion = configuration.metadataVersion
private val languageVersionSettings = configuration.languageVersionSettings
private val moduleName = configuration[CommonConfigurationKeys.MODULE_NAME]!!
fun createPsiFile(fileName: String, isCommon: Boolean): KtFile? {
val psiManager = PsiManager.getInstance(environment.project)
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val file = fileSystem.findFileByPath(fileName) ?: error("File not found: $fileName")
val psiFile = psiManager.findFile(file)
return (psiFile as? KtFile)?.apply { isCommonSource = isCommon }
}
private fun File.listAllFiles(): List<File> {
return if (isDirectory) listFiles().flatMap { it.listAllFiles() }
else listOf(this)
}
private fun createPsiFileFromDir(path: String, vararg extraDirs: String): List<KtFile> {
val dir = File(path)
val buildPath = File(dir, "build")
val commonPath = File(buildPath, "commonMainSources")
val extraPaths = extraDirs.map { File(dir, it) }
val jsPaths = listOf(File(buildPath, "jsMainSources")) + extraPaths
val commonPsis = commonPath.listAllFiles().mapNotNull { createPsiFile(it.path, true) }
val jsPsis = jsPaths.flatMap { d -> d.listAllFiles().mapNotNull { createPsiFile(it.path, false) } }
return commonPsis + jsPsis
}
private val fullRuntimeSourceSet = createPsiFileFromDir("libraries/stdlib/js-ir", "builtins", "runtime", "src")
private val reducedRuntimeSourceSet = createPsiFileFromDir("libraries/stdlib/js-ir-minimal-for-test", "src")
private lateinit var workingDir: File
@Before
fun setUp() {
workingDir = FileUtil.createTempDirectory("irTest", null, false)
}
@After
fun tearDown() {
workingDir.deleteRecursively()
}
@Test
fun runFullPipeline() {
runBenchWithWarmup("Full pipeline", 5, 2, MeasureUnits.MICROSECONDS, pre = System::gc) {
compile(fullRuntimeSourceSet)
}
}
@Test
fun runWithoutFrontEnd() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
runBenchWithWarmup("Pipeline withput FrontEnd", 40, 10, MeasureUnits.MICROSECONDS, pre = System::gc) {
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
val (module, symbolTable, irBuiltIns, linker) = doDeserializeModule(modulePath)
val jsProgram = doBackEnd(module, symbolTable, irBuiltIns, linker)
}
}
@Test
fun runPsi2Ir() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
runBenchWithWarmup("Psi2Ir phase", 40, 10, MeasureUnits.MICROSECONDS, pre = System::gc) {
doPsi2Ir(files, analysisResult)
}
}
@Test
fun runSerialization() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
runBenchWithWarmup("Ir Serialization", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
}
}
enum class MeasureUnits(val delimeter: Long, private val suffix: String) {
NANOSECONDS(1L, "ns"),
MICROSECONDS(1000L, "mcs"),
MILLISECONDS(1000L * 1000L, "ms");
fun convert(nanos: Long): String = "${(nanos / delimeter)}$suffix"
}
@Test
fun runIrDeserializationMonolitic() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files, false)
val moduleRef = loadKlib(modulePath, isPacked = false)
val moduleDescriptor = doDeserializeModuleMetadata(moduleRef)
runBenchWithWarmup("Ir Deserialization Monolithic", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
doDeserializeIrModule(moduleDescriptor)
}
}
@Test
fun runIrDeserializationPerFile() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files, true)
val moduleRef = loadKlib(modulePath, isPacked = false)
val moduleDescriptor = doDeserializeModuleMetadata(moduleRef)
runBenchWithWarmup("Ir Deserialization Per-File", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
doDeserializeIrModule(moduleDescriptor)
}
}
@Test
fun runIrSerialization() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
runBenchWithWarmup("Ir Serialization", 40, 10, MeasureUnits.MILLISECONDS, pre = System::gc) {
doSerializeIrModule(rawModuleFragment)
}
}
@Test
fun runMonoliticDiskWriting() {
val libraryVersion = "JSIR"
val compilerVersion = KotlinCompilerVersion.getVersion()
val abiVersion = KotlinAbiVersion.CURRENT
val metadataVersion = KlibMetadataVersion.INSTANCE.toString()
val irVersion = KlibIrVersion.INSTANCE.toString()
val versions = KotlinLibraryVersioning(libraryVersion, compilerVersion, abiVersion, metadataVersion, irVersion)
val file = createTempFile(directory = workingDir)
val writer = KotlinLibraryOnlyIrWriter(file.absolutePath, "", versions, BuiltInsPlatform.JS, emptyList(), false)
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val fileCount = rawModuleFragment.files.size
val serializedIr = doSerializeIrModule(rawModuleFragment)
runBenchWithWarmup("Monolitic Disk Writing of $fileCount files", 10, 30, MeasureUnits.MILLISECONDS, pre = writer::invalidate) {
doWriteIrModuleToStorage(serializedIr, writer)
}
}
@Test
fun runPerfileDiskWriting() {
val libraryVersion = "JSIR"
val compilerVersion = KotlinCompilerVersion.getVersion()
val abiVersion = KotlinAbiVersion.CURRENT
val metadataVersion = KlibMetadataVersion.INSTANCE.toString()
val irVersion = KlibIrVersion.INSTANCE.toString()
val versions = KotlinLibraryVersioning(libraryVersion, compilerVersion, abiVersion, metadataVersion, irVersion)
val file = createTempFile(directory = workingDir)
val writer = KotlinLibraryOnlyIrWriter(file.absolutePath, "", versions, BuiltInsPlatform.JS, emptyList(), true)
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val fileCount = rawModuleFragment.files.size
val serializedIr = doSerializeIrModule(rawModuleFragment)
runBenchWithWarmup("Per-file Disk Writing of $fileCount files", 10, 30, MeasureUnits.MILLISECONDS, pre = writer::invalidate) {
doWriteIrModuleToStorage(serializedIr, writer)
}
}
@Test
fun runIncrementalKlibGeneratation() {
val klibDirectory = workingDir.resolve("output/klib")
val filesToCompile = fullRuntimeSourceSet
// val filesToCompile = reducedRuntimeSourceSet
val args = K2JSCompilerArguments().apply {
libraries = ""
outputFile = klibDirectory.path
sourceMap = false
irProduceKlibDir = true
irOnly = true
irModuleName = "kotlin"
allowKotlinPackage = true
useExperimental = arrayOf("kotlin.contracts.ExperimentalContracts", "kotlin.Experimental", "kotlin.ExperimentalMultiplatform")
allowResultReturnType = true
multiPlatform = true
languageVersion = "1.4"
commonSources = filesToCompile.filter { it.isCommonSource == true }.map { it.virtualFilePath }.toTypedArray()
}
val cachesDir = workingDir.resolve("caches")
val allFiles = filesToCompile.map { VfsUtilCore.virtualToIoFile(it.virtualFile) }
val dirtyFiles = allFiles.filter { it.name.contains("coreRuntime") }
val cleanBuildStart = System.nanoTime()
withJsIC {
val buildHistoryFile = File(cachesDir, "build-history.bin")
val compiler = IncrementalJsCompilerRunner(
cachesDir, EmptyICReporter,
buildHistoryFile = buildHistoryFile,
modulesApiHistory = EmptyModulesApiHistory
)
compiler.compile(allFiles, args, MessageCollector.NONE, providedChangedFiles = null)
}
val cleanBuildTime = System.nanoTime() - cleanBuildStart
println("[Cold] Clean build of ${allFiles.size} takes ${MeasureUnits.MILLISECONDS.convert(cleanBuildTime)}")
var index = -1
val wmpDone = { index = 0 }
val elist = emptyList<File>()
var changedFiles = ChangedFiles.Known(dirtyFiles, elist)
val update = {
changedFiles = if (index < 0) changedFiles else ChangedFiles.Known(listOf(allFiles[index++]), elist)
System.gc()
}
class CompileTimeResult(val file: String, val time: Long)
var maxResult = CompileTimeResult("", -1)
var minResult = CompileTimeResult("", Long.MAX_VALUE)
val done = { t: Long ->
if (maxResult.time < t) maxResult = CompileTimeResult(changedFiles.modified[0].path, t)
if (minResult.time > t) minResult = CompileTimeResult(changedFiles.modified[0].path, t)
}
runBenchWithWarmup(
"Incremental recompilation of ${dirtyFiles.count()} files",
200,
allFiles.size,
MeasureUnits.MILLISECONDS,
wmpDone,
done,
update
) {
withJsIC {
val buildHistoryFile = File(cachesDir, "build-history.bin")
val compiler = IncrementalJsCompilerRunner(
cachesDir, EmptyICReporter,
buildHistoryFile = buildHistoryFile,
modulesApiHistory = EmptyModulesApiHistory
)
compiler.compile(allFiles, args, MessageCollector.NONE, changedFiles)
}
}
println("Longest re-compilation takes ${MeasureUnits.MILLISECONDS.convert(maxResult.time)} (${maxResult.file})")
println("Fastest re-compilation takes ${MeasureUnits.MILLISECONDS.convert(minResult.time)} (${minResult.file})")
}
private fun runBenchWithWarmup(name: String, W: Int, N: Int, measurer: MeasureUnits, wmpDone: () -> Unit = {}, bnhDone: (Long) -> Unit = {}, pre: () -> Unit = {}, bench: () -> Unit) {
println("Run $name benchmark")
println("Warmup: $W times...")
repeat(W) {
println("W: ${it + 1} out of $W")
pre()
bench()
}
var total = 0L
wmpDone()
println("Run bench: $N times...")
repeat(N) {
print("R: ${it + 1} out of $N ")
pre()
val start = System.nanoTime()
bench()
val iter = System.nanoTime() - start
println("takes ${measurer.convert(iter)}")
bnhDone(iter)
total += iter
}
println("$name takes ${measurer.convert(total / N)}")
}
@Test
fun runDeserialization() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
repeat(20) {
doDeserializeModule(modulePath)
}
}
@Test
fun runDeserializationAndBackend() {
val files = fullRuntimeSourceSet
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
runBenchWithWarmup("Deserializtion and Backend", 40, 10, MeasureUnits.MICROSECONDS, pre = System::gc) {
val (module, symbolTable, irBuiltIns, linker) = doDeserializeModule(modulePath)
doBackEnd(module, symbolTable, irBuiltIns, linker)
}
}
private fun doFrontEnd(files: List<KtFile>): AnalysisResult {
val analysisResult =
TopDownAnalyzerFacadeForJS.analyzeFiles(
files,
project,
configuration,
emptyList(),
friendModuleDescriptors = emptyList(),
thisIsBuiltInsModule = true,
customBuiltInsModule = null
)
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
TopDownAnalyzerFacadeForJS.checkForErrors(files, analysisResult.bindingContext)
return analysisResult
}
private fun doPsi2Ir(files: List<KtFile>, analysisResult: AnalysisResult): IrModuleFragment {
val psi2IrConfiguration = Psi2IrConfiguration()
val mangler = JsManglerDesc
val signaturer = IdSignatureDescriptor(mangler)
val symbolTable = SymbolTable(signaturer)
val generatorExtensions = GeneratorExtensions()
val psi2IrContext = createGeneratorContext(
psi2IrConfiguration,
analysisResult.moduleDescriptor,
analysisResult.bindingContext,
languageVersionSettings,
symbolTable,
generatorExtensions
)
val irBuiltIns = psi2IrContext.irBuiltIns
val functionFactory = IrFunctionFactory(irBuiltIns, psi2IrContext.symbolTable)
irBuiltIns.functionFactory = functionFactory
val irLinker = JsIrLinker(
psi2IrContext.moduleDescriptor,
emptyLoggingContext,
psi2IrContext.irBuiltIns,
psi2IrContext.symbolTable,
functionFactory,
null
)
val irProviders = listOf(irLinker)
val psi2IrTranslator = Psi2IrTranslator(languageVersionSettings, psi2IrContext.configuration, signaturer)
return psi2IrTranslator.generateModuleFragment(psi2IrContext, files, irProviders, null)
}
private fun doSerializeModule(moduleFragment: IrModuleFragment, bindingContext: BindingContext, files: List<KtFile>, perFile: Boolean = false): String {
val tmpKlibDir = createTempDir().also { it.deleteOnExit() }
serializeModuleIntoKlib(
moduleName,
configuration,
bindingContext,
files,
tmpKlibDir.path,
emptyList(),
moduleFragment,
mutableMapOf(),
emptyList(),
true,
perFile
)
return tmpKlibDir.path
}
private fun doDeserializeModuleMetadata(moduleRef: KotlinLibrary): ModuleDescriptorImpl {
return getModuleDescriptorByLibrary(moduleRef, emptyMap())
}
private data class DeserializedModuleInfo(val module: IrModuleFragment, val symbolTable: SymbolTable, val irBuiltIns: IrBuiltIns, val linker: JsIrLinker)
private fun doSerializeIrModule(module: IrModuleFragment): SerializedIrModule {
val serializedIr = JsIrModuleSerializer(logger, module.irBuiltins, mutableMapOf(), true).serializedIrModule(module)
return serializedIr
}
private fun doWriteIrModuleToStorage(serializedIrModule: SerializedIrModule, writer: KotlinLibraryOnlyIrWriter) {
writer.writeIr(serializedIrModule)
}
private fun doDeserializeIrModule(moduleDescriptor: ModuleDescriptorImpl): DeserializedModuleInfo {
val mangler = JsManglerDesc
val signaturer = IdSignatureDescriptor(mangler)
val symbolTable = SymbolTable(signaturer)
val typeTranslator = TypeTranslator(symbolTable, languageVersionSettings, moduleDescriptor.builtIns).also {
it.constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable)
}
val irBuiltIns = IrBuiltIns(moduleDescriptor.builtIns, typeTranslator, symbolTable)
val functionFactory = IrFunctionFactory(irBuiltIns, symbolTable)
irBuiltIns.functionFactory = functionFactory
val jsLinker = JsIrLinker(moduleDescriptor, logger, irBuiltIns, symbolTable, functionFactory, null)
val moduleFragment = jsLinker.deserializeFullModule(moduleDescriptor, moduleDescriptor.kotlinLibrary)
jsLinker.init(null)
// Create stubs
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker), languageVersionSettings)
.generateUnboundSymbolsAsDependencies()
jsLinker.postProcess()
moduleFragment.patchDeclarationParents()
return DeserializedModuleInfo(moduleFragment, symbolTable, irBuiltIns, jsLinker)
}
private fun doDeserializeModule(modulePath: String): DeserializedModuleInfo {
val moduleRef = loadKlib(modulePath, false)
val moduleDescriptor = doDeserializeModuleMetadata(moduleRef)
val mangler = JsManglerDesc
val signaturer = IdSignatureDescriptor(mangler)
val symbolTable = SymbolTable(signaturer)
val typeTranslator = TypeTranslator(symbolTable, languageVersionSettings, moduleDescriptor.builtIns).also {
it.constantValueGenerator = ConstantValueGenerator(moduleDescriptor, symbolTable)
}
val irBuiltIns = IrBuiltIns(moduleDescriptor.builtIns, typeTranslator, symbolTable)
val functionFactory = IrFunctionFactory(irBuiltIns, symbolTable)
irBuiltIns.functionFactory = functionFactory
val jsLinker = JsIrLinker(moduleDescriptor, logger, irBuiltIns, symbolTable, functionFactory, null)
val moduleFragment = jsLinker.deserializeFullModule(moduleDescriptor, moduleDescriptor.kotlinLibrary)
// Create stubs
jsLinker.init(null)
// Create stubs
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker), languageVersionSettings)
.generateUnboundSymbolsAsDependencies()
jsLinker.postProcess()
moduleFragment.patchDeclarationParents()
return DeserializedModuleInfo(moduleFragment, symbolTable, irBuiltIns, jsLinker)
}
private fun doBackEnd(module: IrModuleFragment, symbolTable: SymbolTable, irBuiltIns: IrBuiltIns, jsLinker: JsIrLinker): CompilerResult {
val context = JsIrBackendContext(module.descriptor, irBuiltIns, symbolTable, module, emptySet(), configuration)
ExternalDependenciesGenerator(symbolTable, listOf(jsLinker), languageVersionSettings).generateUnboundSymbolsAsDependencies()
jsPhases.invokeToplevel(phaseConfig, context, module)
val transformer = IrModuleToJsTransformer(context, null, null)
return transformer.generateModule(module)
}
fun compile(files: List<KtFile>): String {
val analysisResult = doFrontEnd(files)
val rawModuleFragment = doPsi2Ir(files, analysisResult)
val modulePath = doSerializeModule(rawModuleFragment, analysisResult.bindingContext, files)
val (module, symbolTable, irBuiltIns, linker) = doDeserializeModule(modulePath)
val jsProgram = doBackEnd(module, symbolTable, irBuiltIns, linker)
return jsProgram.toString()
}
}