[JS IR BE] Migrate JS BE to common klib utils

This commit is contained in:
Svyatoslav Kuzmich
2019-05-16 17:25:51 +03:00
parent 0c586ef5c1
commit cd651be461
20 changed files with 180 additions and 311 deletions
+2
View File
@@ -225,6 +225,8 @@ extra["compilerModules"] = arrayOf(
":compiler:backend.js",
":compiler:ir.serialization.common",
":compiler:ir.serialization.js",
":kotlin-util-io",
":kotlin-util-klib",
":compiler:backend-common",
":compiler:backend",
":compiler:plugin-api",
@@ -114,6 +114,11 @@ class K2JSCompilerArguments : CommonCompilerArguments() {
)
var irProduceOnly: String? by NullableStringFreezableVar(null)
@Argument(
value = "-Xir-legacy-gradle-plugin-compatibility",
description = "Make KLIB generation compatible with legacy gradle plugin"
)
var irLegacyGradlePluginCompatimbility: Boolean by FreezableVar(false)
@GradleOption(DefaultValues.BooleanTrueDefault::class)
@Argument(value = "-Xtyped-arrays", description = "Translate primitive arrays to JS typed arrays")
@@ -28,14 +28,12 @@ import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
import org.jetbrains.kotlin.incremental.js.IncrementalResultsConsumer
import org.jetbrains.kotlin.ir.backend.js.KlibModuleRef
import org.jetbrains.kotlin.ir.backend.js.compile
import org.jetbrains.kotlin.ir.backend.js.generateKLib
import org.jetbrains.kotlin.ir.backend.js.jsPhases
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.js.config.EcmaVersion
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.config.SourceMapSourceEmbedding
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.serialization.js.ModuleKind
@@ -44,7 +42,6 @@ import org.jetbrains.kotlin.utils.KotlinPaths
import org.jetbrains.kotlin.utils.join
import java.io.File
import java.io.IOException
import java.util.zip.ZipFile
enum class ProduceKind {
DEFAULT, // Determine what to produce based on js-v1 options
@@ -61,68 +58,6 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
return K2JSCompilerArguments()
}
private fun extractKlibFromZip(library: String, messageCollector: MessageCollector): File? {
val zipLib = ZipFile(library)
val tempDir = createTempDir(File(library).name, "klibjar")
tempDir.deleteOnExit()
var extractedKlibDir: File? = null
for (entry in zipLib.entries()) {
if (!entry.isDirectory) {
zipLib.getInputStream(entry).use { input ->
val outputEntryFile = File(tempDir, entry.name)
outputEntryFile.parentFile.mkdirs()
outputEntryFile.outputStream().use { output ->
input.copyTo(output)
}
}
} else {
if (entry.name.endsWith("KLIB/")) {
extractedKlibDir = File(tempDir, entry.name)
messageCollector.report(INFO, "Klib $library is extracted into $extractedKlibDir")
}
}
}
return extractedKlibDir
}
private fun loadIrLibrary(library: String, messageCollector: MessageCollector): KlibModuleRef? {
val libraryFile = File(library)
var klibDir = when {
FileUtil.isJarOrZip(libraryFile) -> {
extractKlibFromZip(library, messageCollector) ?: return null
}
!libraryFile.isDirectory -> {
messageCollector.report(ERROR, "Klib $library must be a directory")
return null
}
else ->
libraryFile
}
var klibFiles = klibDir.listFiles()
if (klibFiles.isEmpty()) {
messageCollector.report(STRONG_WARNING, "Klib $library directory is empty")
return null
}
// Sometines gradle gives us a directory with klib directory inside
val klibDirInsideDir = klibFiles.find { it.name.endsWith("KLIB") }
if (klibDirInsideDir != null) {
klibDir = klibDirInsideDir
klibFiles = klibDir.listFiles()
}
val metadataFile = klibFiles.find { it.extension == "klm" }
if (metadataFile == null) {
messageCollector.report(STRONG_WARNING, "No metadata file (.klm) for klib: $klibDir")
return null
}
return KlibModuleRef(metadataFile.nameWithoutExtension, klibDir.absolutePath)
}
override fun doExecute(
arguments: K2JSCompilerArguments,
configuration: CompilerConfiguration,
@@ -144,7 +79,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
arguments.pluginOptions,
configuration
)
if (pluginLoadResult != ExitCode.OK) return pluginLoadResult
if (pluginLoadResult != OK) return pluginLoadResult
val libraries: List<String> = configureLibraries(arguments.libraries)
val friendLibraries: List<String> = configureLibraries(arguments.friendModules)
@@ -204,11 +139,11 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
val mainCallArguments = if (K2JsArgumentConstants.NO_CALL == arguments.main) null else emptyList<String>()
val loadedLibrariesNames = mutableSetOf<String>()
val dependencies = mutableListOf<KlibModuleRef>()
val friendDependencies = mutableListOf<KlibModuleRef>()
val dependencies = mutableListOf<KotlinLibrary>()
val friendDependencies = mutableListOf<KotlinLibrary>()
for (library in libraries) {
val irLib = loadIrLibrary(library, messageCollector) ?: continue
val irLib = loadKlib(library)
if (irLib.moduleName !in loadedLibrariesNames) {
dependencies.add(irLib)
loadedLibrariesNames.add(irLib.moduleName)
@@ -223,6 +158,24 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
messageCollector.report(ERROR, "Unknown produce kind: ${arguments.irProduceOnly}. Valid values are: js, klib")
}
if (produceKind == ProduceKind.KLIB || (produceKind == ProduceKind.DEFAULT && arguments.metaInfo)) {
val outputKlibPath =
if (arguments.irLegacyGradlePluginCompatimbility)
File(outputFilePath).parent
else
"$outputFilePath.klib"
generateKLib(
project = config.project,
files = sourcesFiles,
configuration = config.configuration,
allDependencies = dependencies,
friendDependencies = friendDependencies,
outputKlibPath = outputKlibPath,
nopack = arguments.irLegacyGradlePluginCompatimbility
)
}
if (produceKind == ProduceKind.JS || produceKind == ProduceKind.DEFAULT) {
val phaseConfig = createPhaseConfig(jsPhases, arguments, messageCollector)
@@ -231,7 +184,6 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
sourcesFiles,
configuration,
phaseConfig,
immediateDependencies = dependencies,
allDependencies = dependencies,
friendDependencies = friendDependencies,
mainArguments = mainCallArguments
@@ -240,19 +192,6 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
outputFile.writeText(compiledModule)
}
if (produceKind == ProduceKind.KLIB || (produceKind == ProduceKind.DEFAULT && arguments.metaInfo)) {
val outputKlibPath = "$outputFilePath.KLIB"
generateKLib(
project = config.project,
files = sourcesFiles,
configuration = config.configuration,
immediateDependencies = dependencies,
allDependencies = dependencies,
friendDependencies = friendDependencies,
outputKlibPath = outputKlibPath
)
}
return OK
}
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransf
import org.jetbrains.kotlin.ir.backend.js.utils.JsMainFunctionDetector
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.psi.KtFile
fun compile(
@@ -20,13 +21,12 @@ fun compile(
files: List<KtFile>,
configuration: CompilerConfiguration,
phaseConfig: PhaseConfig,
immediateDependencies: List<KlibModuleRef>,
allDependencies: List<KlibModuleRef>,
friendDependencies: List<KlibModuleRef>,
allDependencies: List<KotlinLibrary>,
friendDependencies: List<KotlinLibrary>,
mainArguments: List<String>?
): String {
val (moduleFragment, dependencyModules, irBuiltIns, symbolTable, deserializer) =
loadIr(project, files, configuration, immediateDependencies, allDependencies, friendDependencies)
loadIr(project, files, configuration, allDependencies, friendDependencies)
val moduleDescriptor = moduleFragment.descriptor
@@ -7,6 +7,7 @@ dependencies {
// TODO: decouple from backend.common
compile(project(":compiler:ir.backend.common"))
compile(project(":compiler:ir.tree"))
compile(project(":kotlin-util-klib"))
}
sourceSets {
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.ir.ir2string
import org.jetbrains.kotlin.backend.common.library.CombinedIrFileWriter
import org.jetbrains.kotlin.backend.common.library.DeclarationId
import org.jetbrains.kotlin.library.SerializedIr
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassKind.*
import org.jetbrains.kotlin.descriptors.Modality
@@ -1,11 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.common.serialization
class SerializedIr (
val module: ByteArray,
val combinedDeclarationFilePath: String
)
@@ -159,7 +159,7 @@ val generateFullRuntimeKLib by task<NoDebugJavaExec> {
buildKLib(sources = listOf(fullRuntimeSources.outputs.files.singleFile.path),
dependencies = emptyList(),
outPath = "$buildDir/fullRuntime/klib/JS_IR_RUNTIME.klm",
outPath = "$buildDir/fullRuntime/klib",
commonSources = listOf("common", "src", "unsigned").map { "$buildDir/fullRuntime/src/libraries/stdlib/$it" }
)
}
@@ -169,7 +169,7 @@ val generateReducedRuntimeKLib by task<NoDebugJavaExec> {
buildKLib(sources = listOf(reducedRuntimeSources.outputs.files.singleFile.path),
dependencies = emptyList(),
outPath = "$buildDir/reducedRuntime/klib/JS_IR_RUNTIME.klm",
outPath = "$buildDir/reducedRuntime/klib",
commonSources = listOf("common", "src", "unsigned").map { "$buildDir/reducedRuntime/src/libraries/stdlib/$it" }
)
}
@@ -183,8 +183,8 @@ val generateKotlinTestKLib by task<NoDebugJavaExec> {
buildKLib(
sources = listOf("$rootDir/libraries/kotlin.test/js/src/main") + kotlinTestCommonSources,
dependencies = listOf("${generateFullRuntimeKLib.outputs.files.singleFile.path}/JS_IR_RUNTIME.klm"),
outPath = "$buildDir/kotlin.test/klib/kotlin.test.klm",
dependencies = listOf("${generateFullRuntimeKLib.outputs.files.singleFile.path}/klib"),
outPath = "$buildDir/kotlin.test/klib",
commonSources = kotlinTestCommonSources
)
}
@@ -7,25 +7,22 @@ package org.jetbrains.kotlin.ir.backend.js
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable
import org.jetbrains.kotlin.library.impl.buildKoltinLibrary
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.functions.functionInterfacePackageFragmentProvider
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.backend.common.serialization.DescriptorTable
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsDeclarationTable
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.newJsDescriptorUniqId
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataSerializationUtil
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.JsKlibMetadataVersion
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.createJsKlibMetadataPackageFragmentProvider
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata.*
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
@@ -34,6 +31,11 @@ import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
import org.jetbrains.kotlin.konan.KonanVersionImpl
import org.jetbrains.kotlin.konan.MetaVersion
import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.library.*
import org.jetbrains.kotlin.library.impl.createKotlinLibrary
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.psi.KtFile
@@ -44,28 +46,30 @@ import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.CompilerDeserializationConfiguration
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.DFS
import java.io.File
import java.nio.file.Files.move
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import org.jetbrains.kotlin.konan.file.File as KFile
data class KlibModuleRef(
val moduleName: String,
val klibPath: String
)
internal val JS_KLIBRARY_CAPABILITY = ModuleDescriptor.Capability<File>("JS KLIBRARY")
internal val moduleHeaderFileName = "module.kji"
internal val declarationsDirName = "ir/"
val KotlinLibrary.moduleName: String
get() = manifestProperties.getProperty(KLIB_PROPERTY_UNIQUE_NAME)
// Considering library built-ins if it has no dependencies.
// All non-built-ins libraries must have built-ins as a dependency.
val KotlinLibrary.isBuiltIns: Boolean
get() = manifestProperties
.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true)
.isEmpty()
fun loadKlib(klibPath: String) =
createKotlinLibrary(KFile(KFile(klibPath).absolutePath))
internal val JS_KLIBRARY_CAPABILITY = ModuleDescriptor.Capability<KotlinLibrary>("JS KLIBRARY")
private val emptyLoggingContext = object : LoggingContext {
override var inVerbosePhase = false
override fun log(message: () -> String) {}
}
private fun metadataFileName(moduleName: String) = "$moduleName.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}"
private val CompilerConfiguration.metadataVersion
get() = get(CommonConfigurationKeys.METADATA_VERSION) as? JsKlibMetadataVersion ?: JsKlibMetadataVersion.INSTANCE
@@ -73,12 +77,12 @@ fun generateKLib(
project: Project,
files: List<KtFile>,
configuration: CompilerConfiguration,
immediateDependencies: List<KlibModuleRef>,
allDependencies: List<KlibModuleRef>,
friendDependencies: List<KlibModuleRef>,
outputKlibPath: String
): KlibModuleRef {
val depsDescriptors = ModulesStructure(project, files, configuration, immediateDependencies, allDependencies, friendDependencies)
allDependencies: List<KotlinLibrary>,
friendDependencies: List<KotlinLibrary>,
outputKlibPath: String,
nopack: Boolean
) {
val depsDescriptors = ModulesStructure(project, files, configuration, allDependencies, friendDependencies)
val psi2IrContext = runAnalysisAndPreparePsi2Ir(depsDescriptors)
@@ -94,11 +98,10 @@ fun generateKLib(
configuration.languageVersionSettings,
psi2IrContext.bindingContext,
outputKlibPath,
immediateDependencies,
moduleFragment
allDependencies,
moduleFragment,
nopack
)
return KlibModuleRef(moduleName, outputKlibPath)
}
data class IrModuleInfo(
@@ -113,11 +116,10 @@ fun loadIr(
project: Project,
files: List<KtFile>,
configuration: CompilerConfiguration,
immediateDependencies: List<KlibModuleRef>,
allDependencies: List<KlibModuleRef>,
friendDependencies: List<KlibModuleRef>
allDependencies: List<KotlinLibrary>,
friendDependencies: List<KotlinLibrary>
): IrModuleInfo {
val depsDescriptors = ModulesStructure(project, files, configuration, immediateDependencies, allDependencies, friendDependencies)
val depsDescriptors = ModulesStructure(project, files, configuration, allDependencies, friendDependencies)
val psi2IrContext = runAnalysisAndPreparePsi2Ir(depsDescriptors)
@@ -127,7 +129,7 @@ fun loadIr(
val deserializer = JsIrLinker(moduleDescriptor, emptyLoggingContext, irBuiltIns, symbolTable)
val deserializedModuleFragments = depsDescriptors.sortedImmediateDependencies.map {
val deserializedModuleFragments = allDependencies.map {
deserializer.deserializeIrModuleHeader(depsDescriptors.getModuleDescriptor(it))!!
}
@@ -154,16 +156,16 @@ private fun GeneratorContext.generateModuleFragment(files: List<KtFile>, deseria
private fun loadKlibMetadataParts(
moduleId: KlibModuleRef
moduleId: KotlinLibrary
): JsKlibMetadataParts {
val metadataFile = File(moduleId.klibPath, metadataFileName(moduleId.moduleName))
val serializer = JsKlibMetadataSerializationUtil
return serializer.readModuleAsProto(metadataFile.readBytes())
return JsKlibMetadataSerializationUtil.readModuleAsProto(moduleId.moduleHeaderData)
}
val ModuleDescriptor.kotlinLibrary get() = this.getCapability(JS_KLIBRARY_CAPABILITY)!!
private fun loadKlibMetadata(
parts: JsKlibMetadataParts,
moduleId: KlibModuleRef,
moduleId: KotlinLibrary,
isBuiltIn: Boolean,
lookupTracker: LookupTracker,
storageManager: LockBasedStorageManager,
@@ -178,7 +180,7 @@ private fun loadKlibMetadata(
Name.special("<${moduleId.moduleName}>"),
storageManager,
builtIns,
capabilities = mapOf(JS_KLIBRARY_CAPABILITY to File(moduleId.klibPath))
capabilities = mapOf(JS_KLIBRARY_CAPABILITY to moduleId)
)
if (isBuiltIn) builtIns.builtInsModule = md
val currentModuleFragmentProvider = createJsKlibMetadataPackageFragmentProvider(
@@ -203,26 +205,21 @@ private class ModulesStructure(
private val project: Project,
private val files: List<KtFile>,
val compilerConfiguration: CompilerConfiguration,
immediateDependencies: List<KlibModuleRef>,
private val allDependencies: List<KlibModuleRef>,
private val friendDependencies: List<KlibModuleRef>
private val allDependencies: List<KotlinLibrary>,
private val friendDependencies: List<KotlinLibrary>
) {
private val deserializedModuleParts: Map<KlibModuleRef, JsKlibMetadataParts> =
private val deserializedModuleParts: Map<KotlinLibrary, JsKlibMetadataParts> =
allDependencies.associateWith { loadKlibMetadataParts(it) }
fun findModuleByName(name: String): KlibModuleRef =
fun findModuleByName(name: String): KotlinLibrary =
allDependencies.find { it.moduleName == name } ?: error("Module is not found: $name")
val moduleDependencies: Map<KlibModuleRef, List<KlibModuleRef>> =
val moduleDependencies: Map<KotlinLibrary, List<KotlinLibrary>> =
deserializedModuleParts.mapValues { (_, parts) ->
parts.importedModules.map(::findModuleByName)
}
val sortedImmediateDependencies: List<KlibModuleRef> =
DFS.topologicalOrder(immediateDependencies) { moduleDependencies.getValue(it) }
.reversed()
val builtInsDep = sortedImmediateDependencies.firstOrNull()
val builtInsDep = allDependencies.find { it.isBuiltIns }
fun runAnalysis(): JsAnalysisResult {
val analysisResult =
@@ -230,7 +227,7 @@ private class ModulesStructure(
files,
project,
compilerConfiguration,
sortedImmediateDependencies.map { getModuleDescriptor(it) },
allDependencies.map { getModuleDescriptor(it) },
friendModuleDescriptors = friendDependencies.map { getModuleDescriptor(it) },
thisIsBuiltInsModule = builtInModuleDescriptor == null,
customBuiltInsModule = builtInModuleDescriptor
@@ -249,9 +246,9 @@ private class ModulesStructure(
private val storageManager: LockBasedStorageManager = LockBasedStorageManager("ModulesStructure")
private var runtimeModule: ModuleDescriptorImpl? = null
private val descriptors = mutableMapOf<KlibModuleRef, ModuleDescriptorImpl>()
private val descriptors = mutableMapOf<KotlinLibrary, ModuleDescriptorImpl>()
fun getModuleDescriptor(current: KlibModuleRef): ModuleDescriptorImpl = descriptors.getOrPut(current) {
fun getModuleDescriptor(current: KotlinLibrary): ModuleDescriptorImpl = descriptors.getOrPut(current) {
val parts = loadKlibMetadataParts(current)
val isBuiltIns = parts.importedModules.isEmpty()
loadKlibMetadata(
@@ -282,8 +279,9 @@ fun serializeModuleIntoKlib(
languageVersionSettings: LanguageVersionSettings,
bindingContext: BindingContext,
klibPath: String,
dependencies: List<KlibModuleRef>,
moduleFragment: IrModuleFragment
dependencies: List<KotlinLibrary>,
moduleFragment: IrModuleFragment,
nopack: Boolean
) {
val declarationTable = JsDeclarationTable(moduleFragment.irBuiltins, DescriptorTable())
@@ -292,7 +290,7 @@ fun serializeModuleIntoKlib(
val moduleDescription =
JsKlibMetadataModuleDescriptor(moduleName, dependencies.map { it.moduleName }, moduleFragment.descriptor)
val serializedData = serializer.serializeMetadata(
val serializedMetadata = serializer.serializeMetadata(
bindingContext,
moduleDescription,
languageVersionSettings,
@@ -302,19 +300,21 @@ fun serializeModuleIntoKlib(
index?.let { newJsDescriptorUniqId(it) }
}
val klibDir = File(klibPath).also {
it.deleteRecursively()
it.mkdirs()
}
val abiVersion = KotlinAbiVersion.CURRENT
val compilerVersion = KonanVersionImpl(MetaVersion.DEV, 1, 3, 0, -1)
val libraryVersion = "JSIR"
val moduleFile = File(klibDir, moduleHeaderFileName)
moduleFile.writeBytes(serializedIr.module)
val versions = KonanLibraryVersioning(abiVersion = abiVersion, libraryVersion = libraryVersion, compilerVersion = compilerVersion)
val irDeclarationDir = File(klibDir, declarationsDirName).also { it.mkdir() }
val irCombinedFile = File(irDeclarationDir, "irCombined.knd")
move(Paths.get(serializedIr.combinedDeclarationFilePath), Paths.get(irCombinedFile.path), StandardCopyOption.REPLACE_EXISTING)
File(klibDir, "${moduleDescription.name}.${JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION}").also {
it.writeBytes(serializedData.asByteArray())
}
buildKoltinLibrary(
linkDependencies = dependencies,
ir = serializedIr,
metadata = serializedMetadata,
dataFlowGraph = null,
manifestProperties = null,
moduleName = moduleName,
nopack = nopack,
output = klibPath,
versions = versions
)
}
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
import org.jetbrains.kotlin.backend.common.serialization.DescriptorReferenceDeserializer
import org.jetbrains.kotlin.backend.common.serialization.DescriptorUniqIdAware
import org.jetbrains.kotlin.backend.common.serialization.UniqId
import org.jetbrains.kotlin.backend.common.serialization.UniqIdKey
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
@@ -6,29 +6,23 @@
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
import org.jetbrains.kotlin.backend.common.LoggingContext
import org.jetbrains.kotlin.backend.common.library.CombinedIrFileReader
import org.jetbrains.kotlin.backend.common.library.DeclarationId
import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.JS_KLIBRARY_CAPABILITY
import org.jetbrains.kotlin.ir.backend.js.moduleHeaderFileName
import org.jetbrains.kotlin.ir.backend.js.kotlinLibrary
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.util.SymbolTable
import java.io.File
class JsIrLinker(
currentModule: ModuleDescriptor,
logger: LoggingContext,
builtIns: IrBuiltIns,
symbolTable: SymbolTable
) : KotlinIrLinker(logger, builtIns, symbolTable, emptyList<ModuleDescriptor>(), null, 0x1_0000_0000L),
) : KotlinIrLinker(logger, builtIns, symbolTable, emptyList(), null, 0x1_0000_0000L),
DescriptorUniqIdAware by JsDescriptorUniqIdAware {
private val FUNCTION_INDEX_START: Long = indexAfterKnownBuiltins
val moduleToReaderMap = mutableMapOf<ModuleDescriptor, CombinedIrFileReader>()
override fun getPrimitiveTypeOrNull(symbol: IrClassifierSymbol, hasQuestionMark: Boolean) =
builtIns.getPrimitiveTypeOrNullByDescriptor(symbol.descriptor, hasQuestionMark)
@@ -36,15 +30,11 @@ class JsIrLinker(
JsDescriptorReferenceDeserializer(currentModule, builtIns, FUNCTION_INDEX_START)
override fun reader(moduleDescriptor: ModuleDescriptor, uniqId: UniqId): ByteArray {
val irFileReader = moduleToReaderMap.getOrPut(moduleDescriptor) {
val irFile = File(moduleDescriptor.getCapability(JS_KLIBRARY_CAPABILITY)!!, "ir/irCombined.knd")
CombinedIrFileReader(irFile)
}
return irFileReader.declarationBytes(DeclarationId(uniqId.index, uniqId.isLocal))
return moduleDescriptor.kotlinLibrary.irDeclaration(uniqId.index, uniqId.isLocal)
}
override val ModuleDescriptor.irHeader: ByteArray? get() =
this.getCapability(JS_KLIBRARY_CAPABILITY)?.let { File(it, moduleHeaderFileName).readBytes() }
override val ModuleDescriptor.irHeader: ByteArray?
get() = this.kotlinLibrary.irHeader
override fun declareForwardDeclarations() {
// since for `knownBuiltIns` such as FunctionN it is possible to have unbound symbols after deserialization
@@ -5,10 +5,9 @@
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata
import org.jetbrains.kotlin.config.AnalysisFlags
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.library.SerializedMetadata
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.name.FqName
@@ -21,27 +20,25 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.AnnotationSerializer
import org.jetbrains.kotlin.serialization.DescriptorSerializer
import org.jetbrains.kotlin.serialization.StringTableImpl
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
object JsKlibMetadataSerializationUtil {
const val CLASS_METADATA_FILE_EXTENSION: String = "klm"
fun serializeMetadata(
bindingContext: BindingContext,
jsDescriptor: JsKlibMetadataModuleDescriptor<ModuleDescriptor>,
languageVersionSettings: LanguageVersionSettings,
metadataVersion: JsKlibMetadataVersion,
declarationTableHandler: ((DeclarationDescriptor) -> JsKlibMetadataProtoBuf.DescriptorUniqId?)
declarationTableHandler: (DeclarationDescriptor) -> JsKlibMetadataProtoBuf.DescriptorUniqId?
): SerializedMetadata {
val libraryProto = JsKlibMetadataProtoBuf.Library.newBuilder()
jsDescriptor.imported.forEach { libraryProto.addImportedModule(it) }
val serializedFragments = HashMap<FqName, ProtoBuf.PackageFragment>()
val module = jsDescriptor.data
val fragments = mutableListOf<List<ByteArray>>()
val fragmentNames = mutableListOf<String>()
for (fqName in getPackagesFqNames(module).sortedBy { it.asString() }) {
val fragment = serializeDescriptors(
@@ -55,39 +52,14 @@ object JsKlibMetadataSerializationUtil {
}
}
return SerializedMetadata(serializedFragments, jsDescriptor, languageVersionSettings)
}
class SerializedMetadata(
private val serializedFragments: Map<FqName, ProtoBuf.PackageFragment>,
private val jsDescriptor: JsKlibMetadataModuleDescriptor<ModuleDescriptor>,
private val languageVersionSettings: LanguageVersionSettings
) {
fun asByteArray(): ByteArray =
ByteArrayOutputStream().apply {
GZIPOutputStream(this).use { stream ->
serializeHeader(
jsDescriptor.data,
packageFqName = null,
languageVersionSettings = languageVersionSettings
).writeDelimitedTo(stream)
asLibrary().writeTo(stream)
}
}.toByteArray()
private fun asLibrary(): JsKlibMetadataProtoBuf.Library {
jsDescriptor.imported
val builder = JsKlibMetadataProtoBuf.Library.newBuilder()
jsDescriptor.imported.forEach { builder.addImportedModule(it) }
for ((_, fragment) in serializedFragments.entries.sortedBy { (fqName, _) -> fqName.asString() }) {
builder.addPackageFragment(fragment)
}
return builder.build()
for ((fqName, fragment) in serializedFragments.entries.sortedBy { (fqName, _) -> fqName.asString() }) {
libraryProto.addPackageFragment(fragment)
fragments.add(listOf(fragment.toByteArray()))
fragmentNames.add(fqName.asString())
}
val libraryAsByteArray = libraryProto.build().toByteArray()
return SerializedMetadata(libraryAsByteArray, fragments, fragmentNames)
}
fun serializeDescriptors(
@@ -174,38 +146,6 @@ object JsKlibMetadataSerializationUtil {
private fun ProtoBuf.PackageFragment.isEmpty(): Boolean =
class_Count == 0 && `package`.let { it.functionCount == 0 && it.propertyCount == 0 && it.typeAliasCount == 0 }
fun serializeHeader(
module: ModuleDescriptor, packageFqName: FqName?, languageVersionSettings: LanguageVersionSettings
): JsKlibMetadataProtoBuf.Header {
val header = JsKlibMetadataProtoBuf.Header.newBuilder()
if (packageFqName != null) {
header.packageFqName = packageFqName.asString()
}
if (languageVersionSettings.isPreRelease()) {
header.flags = 1
}
val experimentalAnnotationFqNames = languageVersionSettings.getFlag(AnalysisFlags.experimental)
if (experimentalAnnotationFqNames.isNotEmpty()) {
val stringTable = StringTableImpl()
for (fqName in experimentalAnnotationFqNames) {
val descriptor = module.resolveClassByFqName(FqName(fqName), NoLookupLocation.FOR_ALREADY_TRACKED) ?: continue
header.addAnnotation(ProtoBuf.Annotation.newBuilder().apply {
id = stringTable.getFqNameIndex(descriptor)
})
}
val (strings, qualifiedNames) = stringTable.buildProto()
header.strings = strings
header.qualifiedNames = qualifiedNames
}
// TODO: write JS code binary version
return header.build()
}
private fun getPackagesFqNames(module: ModuleDescriptor): Set<FqName> {
return mutableSetOf<FqName>().apply {
getSubPackagesFqNames(module.getPackage(FqName.ROOT), this)
@@ -228,11 +168,8 @@ object JsKlibMetadataSerializationUtil {
@JvmStatic
fun readModuleAsProto(metadata: ByteArray): JsKlibMetadataParts {
val (header, content) = GZIPInputStream(ByteArrayInputStream(metadata)).use { stream ->
JsKlibMetadataProtoBuf.Header.parseDelimitedFrom(stream, JsKlibMetadataSerializerProtocol.extensionRegistry) to
JsKlibMetadataProtoBuf.Library.parseFrom(stream, JsKlibMetadataSerializerProtocol.extensionRegistry)
}
val header = JsKlibMetadataProtoBuf.Header.parseFrom(metadata, JsKlibMetadataSerializerProtocol.extensionRegistry)
val content = JsKlibMetadataProtoBuf.Library.parseFrom(metadata, JsKlibMetadataSerializerProtocol.extensionRegistry)
return JsKlibMetadataParts(header, content.packageFragmentList, content.importedModuleList)
}
}
@@ -5,8 +5,6 @@
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.metadata
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
@@ -24,12 +22,4 @@ object JsKlibMetadataSerializerProtocol : SerializerExtensionProtocol(
JsKlibMetadataProtoBuf.parameterAnnotation,
JsKlibMetadataProtoBuf.typeAnnotation,
JsKlibMetadataProtoBuf.typeParameterAnnotation
) {
fun getKjsmFilePath(packageFqName: FqName): String {
val shortName = if (packageFqName.isRoot) Name.identifier("root-package") else packageFqName.shortName()
return packageFqName.child(shortName).asString().replace('.', '/') +
"." +
JsKlibMetadataSerializationUtil.CLASS_METADATA_FILE_EXTENSION
}
}
)
@@ -13,6 +13,7 @@ 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.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.multiplatform.isCommonSource
import org.jetbrains.kotlin.serialization.js.ModuleKind
@@ -58,10 +59,10 @@ fun buildKLib(
moduleName: String,
sources: List<String>,
outputPath: String,
dependencies: List<KlibModuleRef>,
dependencies: List<KotlinLibrary>,
commonSources: List<String>
): KlibModuleRef {
return generateKLib(
) {
generateKLib(
project = environment.project,
files = sources.map { source ->
val file = createPsiFile(source)
@@ -71,10 +72,10 @@ fun buildKLib(
file
},
configuration = buildConfiguration(environment, moduleName),
immediateDependencies = dependencies,
allDependencies = dependencies,
friendDependencies = emptyList(),
outputKlibPath = outputPath
outputKlibPath = outputPath,
nopack = true
)
}
@@ -114,12 +115,10 @@ fun main(args: Array<String>) {
val name = outputPath.takeLastWhile { it != '/' }
if (!name.endsWith(".klm")) error("invalid output file name")
val dependencyKLibs = dependencies.map {
val file = File(it)
KlibModuleRef(file.name.dropLast(4), file.parent)
loadKlib(file.path)
}
buildKLib(name.dropLast(4), listOfKtFilesFrom(inputFiles), File(outputPath).parent, dependencyKLibs, listOfKtFilesFrom(commonSources))
buildKLib(name.dropLast(4), listOfKtFilesFrom(inputFiles), outputPath, dependencyKLibs, listOfKtFilesFrom(commonSources))
}
+2
View File
@@ -3,6 +3,8 @@ where advanced options include:
-Xfriend-modules=<path> Paths to friend modules
-Xfriend-modules-disabled Disable internal declaration export
-Xir Use IR backend
-Xir-legacy-gradle-plugin-compatibility
Make KLIB generation compatible with legacy gradle plugin
-Xir-produce-only={ klib, js } Type of output to produce. Overrides -meta-info argument.
-Xtyped-arrays Translate primitive arrays to JS typed arrays
-Xallow-kotlin-package Allow compiling code in package 'kotlin' and allow not requiring kotlin.stdlib in module-info
@@ -139,7 +139,7 @@ internal fun zippedKotlinLibraryChecks(klibFile: File) {
check(klibFile.isFile) { "Expected $klibFile to be a regular file." }
val extension = klibFile.extension
check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION) {
check(extension.isEmpty() || extension == KLIB_FILE_EXTENSION || extension == "jar") {
"KLIB path has unexpected extension: $klibFile"
}
}
@@ -85,7 +85,7 @@ class KoltinLibraryWriterImpl(
) : BaseWriter by base, MetadataWriter by metadata, IrWriter by ir, KotlinLibraryWriter
internal fun buildKoltinLibrary(
fun buildKoltinLibrary(
linkDependencies: List<KotlinLibrary>,
metadata: SerializedMetadata,
ir: SerializedIr,
@@ -17,12 +17,14 @@
package org.jetbrains.kotlin.utils
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile
import java.io.*
import java.util.*
import java.util.jar.Attributes
import java.util.jar.JarFile
import java.util.jar.Manifest
import java.util.zip.ZipFile
object LibraryUtils {
private val LOG = Logger.getInstance(LibraryUtils::class.java)
@@ -65,6 +67,26 @@ object LibraryUtils {
return checkAttributeValue(library, TITLE_KOTLIN_JAVASCRIPT_STDLIB, Attributes.Name.IMPLEMENTATION_TITLE)
}
private fun isZippedKlib(candidate: File): Boolean {
if (candidate.extension == "klib") return true
var manifestFound = false
var irFound = false
for (entry in ZipFile(candidate).entries()) {
if (entry.name == "manifest") manifestFound = true
if (entry.name == "ir/") irFound = true
}
return manifestFound && irFound
}
fun isKotlinJavascriptIrLibrary(candidate: File): Boolean {
return when {
FileUtil.isJarOrZip(candidate) -> isZippedKlib(candidate)
!File(candidate, "manifest").isFile -> false
!File(candidate, "ir").isDirectory -> false
else -> true
}
}
private fun getManifestFromJar(library: File): Manifest? {
if (!library.canRead()) return null
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.js.test
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.toPhaseMap
import org.jetbrains.kotlin.ir.backend.js.KlibModuleRef
import org.jetbrains.kotlin.ir.backend.js.loadKlib
import org.jetbrains.kotlin.ir.backend.js.compile
import org.jetbrains.kotlin.ir.backend.js.generateKLib
import org.jetbrains.kotlin.ir.backend.js.jsPhases
@@ -18,9 +18,9 @@ import org.jetbrains.kotlin.js.facade.TranslationUnit
import org.jetbrains.kotlin.test.TargetBackend
import java.io.File
private val fullRuntimeKlib = KlibModuleRef("JS_IR_RUNTIME", "compiler/ir/serialization.js/build/fullRuntime/klib")
private val defaultRuntimeKlib = KlibModuleRef("JS_IR_RUNTIME", "compiler/ir/serialization.js/build/reducedRuntime/klib")
private val kotlinTestKLib = KlibModuleRef("kotlin.test", "compiler/ir/serialization.js/build/kotlin.test/klib")
private val fullRuntimeKlib = loadKlib("compiler/ir/serialization.js/build/fullRuntime/klib")
private val defaultRuntimeKlib = loadKlib("compiler/ir/serialization.js/build/reducedRuntime/klib")
private val kotlinTestKLib = loadKlib("compiler/ir/serialization.js/build/kotlin.test/klib")
abstract class BasicIrBoxTest(
pathToTestDir: String,
@@ -43,7 +43,7 @@ abstract class BasicIrBoxTest(
// TODO Design incremental compilation for IR and add test support
override val incrementalCompilationChecksEnabled = false
private val compilationCache = mutableMapOf<String, KlibModuleRef>()
private val compilationCache = mutableMapOf<String, String>()
override fun doTest(filePath: String, expectedResult: String, mainCallParameters: MainCallParameters, coroutinesPackage: String) {
compilationCache.clear()
@@ -73,16 +73,10 @@ abstract class BasicIrBoxTest(
val runtimeKlibs = if (needsFullIrRuntime) listOf(fullRuntimeKlib, kotlinTestKLib) else listOf(defaultRuntimeKlib)
val libraries = config.configuration[JSConfigurationKeys.LIBRARIES]!!.map { File(it).name }
val transitiveLibraries = config.configuration[JSConfigurationKeys.TRANSITIVE_LIBRARIES]!!.map { File(it).name }
// TODO: Add proper depencencies
val dependencies = runtimeKlibs + libraries.map {
compilationCache[it] ?: error("Can't find compiled module for dependency $it")
}
val allDependencies = runtimeKlibs + transitiveLibraries.map {
compilationCache[it] ?: error("Can't find compiled module for dependency $it")
loadKlib(compilationCache[it] ?: error("Can't find compiled module for dependency $it"))
}
val actualOutputFile = outputFile.absolutePath.let {
@@ -112,7 +106,6 @@ abstract class BasicIrBoxTest(
files = filesToCompile,
configuration = config.configuration,
phaseConfig = phaseConfig,
immediateDependencies = dependencies,
allDependencies = allDependencies,
friendDependencies = emptyList(),
mainArguments = mainCallParameters.run { if (shouldBeGenerated()) arguments() else null }
@@ -122,17 +115,17 @@ abstract class BasicIrBoxTest(
outputFile.write(wrappedCode)
} else {
val module = generateKLib(
generateKLib(
project = config.project,
files = filesToCompile,
configuration = config.configuration,
immediateDependencies = dependencies,
allDependencies = allDependencies,
friendDependencies = emptyList(),
outputKlibPath = actualOutputFile
outputKlibPath = actualOutputFile,
nopack = true
)
compilationCache[outputFile.name.replace(".js", ".meta.js")] = module
compilationCache[outputFile.name.replace(".js", ".meta.js")] = actualOutputFile
}
}
@@ -563,8 +563,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
private val libraryFilter: (File) -> Boolean
get() = if ("-Xir" in kotlinOptions.freeCompilerArgs) {
// TODO: Detect IR libraries
{ true }
LibraryUtils::isKotlinJavascriptIrLibrary
} else {
LibraryUtils::isKotlinJavascriptLibrary
}
@@ -578,6 +577,7 @@ open class Kotlin2JsCompile : AbstractKotlinCompile<K2JSCompilerArguments>(), Ko
if ("-Xir" in args.freeArgs) {
logger.kotlinDebug("Using JS IR backend")
incremental = false
args.freeArgs += "-Xir-legacy-gradle-plugin-compatibility"
}
val dependencies = compileClasspath