[JS IR] Run diagnostics by IR before the klib serialization
Implement an infrastructure for checking IR before JS klib serialization. Implement the EXPORTING_JS_NAME_CLASH and EXPORTING_JS_NAME_CLASH_ES checks. ^KT-61710 Fixed
This commit is contained in:
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.RUNTIME_DIAGNOSTIC_EXCEPTION
|
||||
import org.jetbrains.kotlin.cli.common.arguments.K2JsArgumentConstants.RUNTIME_DIAGNOSTIC_LOG
|
||||
import org.jetbrains.kotlin.cli.common.config.addKotlinSourceRoot
|
||||
import org.jetbrains.kotlin.cli.common.fir.reportToMessageCollector
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
|
||||
@@ -461,6 +462,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
sourceModule.jsFrontEndResult.hasErrors
|
||||
)
|
||||
|
||||
val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter()
|
||||
generateKLib(
|
||||
sourceModule,
|
||||
outputKlibPath,
|
||||
@@ -468,10 +470,17 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
jsOutputName = arguments.irPerModuleOutputName,
|
||||
icData = icData,
|
||||
moduleFragment = moduleFragment,
|
||||
diagnosticReporter = diagnosticsReporter,
|
||||
builtInsPlatform = if (arguments.wasm) BuiltInsPlatform.WASM else BuiltInsPlatform.JS
|
||||
) { file ->
|
||||
metadataSerializer.serializeScope(file, sourceModule.jsFrontEndResult.bindingContext, moduleFragment.descriptor)
|
||||
}
|
||||
|
||||
val messageCollector = environmentForJS.configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
reportCollectedDiagnostics(environmentForJS.configuration, diagnosticsReporter, messageCollector)
|
||||
if (diagnosticsReporter.hasErrors) {
|
||||
throw CompilationErrorException()
|
||||
}
|
||||
}
|
||||
return sourceModule
|
||||
}
|
||||
@@ -556,6 +565,11 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
|
||||
jsOutputName = arguments.irPerModuleOutputName,
|
||||
useWasmPlatform = arguments.wasm
|
||||
)
|
||||
|
||||
reportCollectedDiagnostics(moduleStructure.compilerConfiguration, diagnosticsReporter, messageCollector)
|
||||
if (diagnosticsReporter.hasErrors) {
|
||||
throw CompilationErrorException()
|
||||
}
|
||||
}
|
||||
|
||||
return moduleStructure
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.cli.common.fir.FirDiagnosticsCompilerResultsReporter
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.constant.EvaluatedConstTracker
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
@@ -116,6 +117,15 @@ inline fun <F> compileModuleToAnalyzedFir(
|
||||
return outputs
|
||||
}
|
||||
|
||||
internal fun reportCollectedDiagnostics(
|
||||
compilerConfiguration: CompilerConfiguration,
|
||||
diagnosticsReporter: BaseDiagnosticsCollector,
|
||||
messageCollector: MessageCollector
|
||||
) {
|
||||
val renderName = compilerConfiguration.getBoolean(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME)
|
||||
FirDiagnosticsCompilerResultsReporter.reportToMessageCollector(diagnosticsReporter, messageCollector, renderName)
|
||||
}
|
||||
|
||||
open class AnalyzedFirOutput(val output: List<ModuleCompilerAnalyzedOutput>) {
|
||||
protected open fun checkSyntaxErrors(messageCollector: MessageCollector) = false
|
||||
|
||||
@@ -125,8 +135,7 @@ open class AnalyzedFirOutput(val output: List<ModuleCompilerAnalyzedOutput>) {
|
||||
messageCollector: MessageCollector,
|
||||
): Boolean {
|
||||
if (checkSyntaxErrors(messageCollector) || diagnosticsReporter.hasErrors) {
|
||||
val renderName = moduleStructure.compilerConfiguration.getBoolean(CLIConfigurationKeys.RENDER_DIAGNOSTIC_INTERNAL_NAME)
|
||||
FirDiagnosticsCompilerResultsReporter.reportToMessageCollector(diagnosticsReporter, messageCollector, renderName)
|
||||
reportCollectedDiagnostics(moduleStructure.compilerConfiguration, diagnosticsReporter, messageCollector)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -318,6 +327,7 @@ fun serializeFirKlib(
|
||||
moduleStructure.compilerConfiguration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
moduleStructure.compilerConfiguration,
|
||||
moduleStructure.compilerConfiguration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None,
|
||||
diagnosticsReporter,
|
||||
fir2KlibSerializer.sourceFiles,
|
||||
klibPath = outputKlibPath,
|
||||
moduleStructure.allDependencies,
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.checkers
|
||||
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.declarations.JsKlibEsModuleExportsChecker
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.declarations.JsKlibOtherModuleExportsChecker
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.library.SerializedIrFile
|
||||
|
||||
object JsKlibCheckers {
|
||||
private val exportedDeclarationsCheckers = listOf(
|
||||
JsKlibEsModuleExportsChecker,
|
||||
JsKlibOtherModuleExportsChecker
|
||||
)
|
||||
|
||||
fun check(
|
||||
cleanFiles: List<SerializedIrFile>,
|
||||
dirtyFiles: List<IrFile>,
|
||||
exportedNames: Map<IrFile, Map<IrDeclarationWithName, String>>,
|
||||
diagnosticReporter: DiagnosticReporter,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
val reporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, configuration.languageVersionSettings)
|
||||
val exportedDeclarations = JsKlibExportingDeclaration.collectDeclarations(cleanFiles, dirtyFiles, exportedNames)
|
||||
for (checker in exportedDeclarationsCheckers) {
|
||||
checker.check(exportedDeclarations, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.checkers
|
||||
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
|
||||
interface JsKlibDeclarationsChecker<D> {
|
||||
fun check(declarations: List<D>, reporter: KtDiagnosticReporterWithImplicitIrBasedContext)
|
||||
}
|
||||
|
||||
typealias JsKlibExportedDeclarationsChecker = JsKlibDeclarationsChecker<JsKlibExportingDeclaration>
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.error2
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.*
|
||||
import org.jetbrains.kotlin.diagnostics.warning2
|
||||
|
||||
object JsKlibErrors {
|
||||
val EXPORTING_JS_NAME_CLASH by error2<PsiElement, String, List<JsKlibExport>>()
|
||||
val EXPORTING_JS_NAME_CLASH_ES by warning2<PsiElement, String, List<JsKlibExport>>()
|
||||
|
||||
init {
|
||||
RootDiagnosticRendererFactory.registerFactory(KtDefaultJsKlibErrorMessages)
|
||||
}
|
||||
}
|
||||
|
||||
private object KtDefaultJsKlibErrorMessages : BaseDiagnosticRendererFactory() {
|
||||
@JvmField
|
||||
val JS_KLIB_EXPORTS = Renderer<List<JsKlibExport>> { exports ->
|
||||
if (exports.size == 1) {
|
||||
exports.single().render()
|
||||
} else {
|
||||
exports.sortedBy { it.containingFile }.joinToString("\n", "\n", limit = 10) { " ${it.render()}" }
|
||||
}
|
||||
}
|
||||
|
||||
override val MAP = KtDiagnosticFactoryToRendererMap("KT").also { map ->
|
||||
map.put(
|
||||
JsKlibErrors.EXPORTING_JS_NAME_CLASH,
|
||||
"Exporting name ''{0}'' clashes with {1}",
|
||||
CommonRenderers.STRING,
|
||||
JS_KLIB_EXPORTS
|
||||
)
|
||||
map.put(
|
||||
JsKlibErrors.EXPORTING_JS_NAME_CLASH_ES,
|
||||
"Exporting name ''{0}'' in ES modules may clash with {1}",
|
||||
CommonRenderers.STRING,
|
||||
JS_KLIB_EXPORTS,
|
||||
)
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.checkers
|
||||
|
||||
import org.jetbrains.kotlin.ir.backend.js.fileMetadata
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrFileMetadata
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.library.SerializedIrFile
|
||||
|
||||
abstract class JsKlibExport(val containingFile: String) {
|
||||
abstract val fqName: String
|
||||
abstract fun render(): String
|
||||
}
|
||||
|
||||
class JsKlibExportingPackage(containingFile: String, override val fqName: String) : JsKlibExport(containingFile) {
|
||||
override fun render() = "package '$fqName' from file '$containingFile'"
|
||||
}
|
||||
|
||||
class JsKlibExportingDeclaration(
|
||||
val exportingName: String,
|
||||
containingFile: String,
|
||||
packageFqName: String,
|
||||
val declaration: IrDeclaration?,
|
||||
) : JsKlibExport(containingFile) {
|
||||
constructor(name: String, file: SerializedIrFile) : this(name, file.path, file.fqName, null)
|
||||
constructor(name: String, file: IrFile, decl: IrDeclaration) : this(name, file.fileEntry.name, file.packageFqName.toString(), decl)
|
||||
|
||||
val containingPackageFqName = packageFqName.takeIf { it != "<root>" } ?: ""
|
||||
override val fqName = "$containingPackageFqName${".".takeIf { containingPackageFqName.isNotEmpty() } ?: ""}$exportingName"
|
||||
override fun render() = "exporting name '$exportingName' from file '$containingFile'"
|
||||
|
||||
companion object {
|
||||
fun collectDeclarations(
|
||||
cleanFiles: List<SerializedIrFile>,
|
||||
dirtyFiles: List<IrFile>,
|
||||
exportedNames: Map<IrFile, Map<IrDeclarationWithName, String>>,
|
||||
) = buildList {
|
||||
for (serializedFile in cleanFiles) {
|
||||
val fileMetadata = JsIrFileMetadata.fromByteArray(serializedFile.fileMetadata)
|
||||
for (exportedName in fileMetadata.exportedNames) {
|
||||
add(JsKlibExportingDeclaration(exportedName, serializedFile))
|
||||
}
|
||||
}
|
||||
for (dirtyFile in dirtyFiles) {
|
||||
val exportedDeclarations = exportedNames[dirtyFile] ?: continue
|
||||
for ((declaration, exportedName) in exportedDeclarations) {
|
||||
add(JsKlibExportingDeclaration(exportedName, dirtyFile, declaration))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.checkers.declarations
|
||||
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibExportingDeclaration
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibExportedDeclarationsChecker
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibErrors
|
||||
|
||||
object JsKlibEsModuleExportsChecker : JsKlibExportedDeclarationsChecker {
|
||||
override fun check(
|
||||
declarations: List<JsKlibExportingDeclaration>,
|
||||
reporter: KtDiagnosticReporterWithImplicitIrBasedContext,
|
||||
) {
|
||||
val allExportedNameClashes = declarations.groupBy { it.exportingName }.filterValues { it.size > 1 }
|
||||
|
||||
for (exportedDeclarationClashes in allExportedNameClashes.values) {
|
||||
for ((index, exportedDeclaration) in exportedDeclarationClashes.withIndex()) {
|
||||
val declaration = exportedDeclaration.declaration ?: continue
|
||||
val clashedWith = exportedDeclarationClashes.filterIndexed { i, _ -> i != index }
|
||||
reporter.at(declaration).report(JsKlibErrors.EXPORTING_JS_NAME_CLASH_ES, exportedDeclaration.exportingName, clashedWith)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.checkers.declarations
|
||||
|
||||
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.*
|
||||
|
||||
object JsKlibOtherModuleExportsChecker : JsKlibExportedDeclarationsChecker {
|
||||
private fun <T> MutableMap<T, MutableList<JsKlibExport>>.addExport(key: T, export: JsKlibExport) {
|
||||
getOrPut(key) { mutableListOf() }.add(export)
|
||||
}
|
||||
|
||||
private fun collectClashesByFqNames(declarations: List<JsKlibExportingDeclaration>): Map<String, List<JsKlibExport>> {
|
||||
return buildMap<String, MutableList<JsKlibExport>> {
|
||||
for (declaration in declarations) {
|
||||
addExport(declaration.fqName, declaration)
|
||||
|
||||
var packageFqName = declaration.containingPackageFqName
|
||||
while (packageFqName.isNotEmpty()) {
|
||||
addExport(packageFqName, JsKlibExportingPackage(declaration.containingFile, packageFqName))
|
||||
|
||||
packageFqName = packageFqName.substringBeforeLast(".", "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectClashes(declarations: List<JsKlibExportingDeclaration>): Map<JsKlibExportingDeclaration, List<JsKlibExport>> {
|
||||
val clashesByFqNames = collectClashesByFqNames(declarations)
|
||||
return buildMap {
|
||||
for (clashingExports in clashesByFqNames.values) {
|
||||
for ((index, export) in clashingExports.withIndex()) {
|
||||
if (export is JsKlibExportingDeclaration) {
|
||||
val clashedWith = clashingExports.filterIndexed { i, _ -> i != index }
|
||||
if (clashedWith.isNotEmpty()) {
|
||||
put(export, clashedWith)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun check(declarations: List<JsKlibExportingDeclaration>, reporter: KtDiagnosticReporterWithImplicitIrBasedContext) {
|
||||
val clashes = collectClashes(declarations)
|
||||
for ((declaration, clashedWith) in clashes) {
|
||||
if (declaration.declaration != null) {
|
||||
reporter.at(declaration.declaration).report(JsKlibErrors.EXPORTING_JS_NAME_CLASH, declaration.exportingName, clashedWith)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,10 +32,12 @@ import org.jetbrains.kotlin.config.*
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.incremental.components.LookupTracker
|
||||
import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.backend.js.checkers.JsKlibCheckers
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
@@ -71,9 +73,9 @@ import org.jetbrains.kotlin.storage.StorageManager
|
||||
import org.jetbrains.kotlin.util.DummyLogger
|
||||
import org.jetbrains.kotlin.util.Logger
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.flatGroupBy
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.ifTrue
|
||||
import org.jetbrains.kotlin.utils.memoryOptimizedFilter
|
||||
import org.jetbrains.kotlin.utils.toSmartList
|
||||
import java.io.File
|
||||
|
||||
val KotlinLibrary.moduleName: String
|
||||
@@ -98,7 +100,7 @@ val KotlinLibrary.serializedKlibFingerprint: SerializedKlibFingerprint?
|
||||
private val CompilerConfiguration.metadataVersion
|
||||
get() = get(CommonConfigurationKeys.METADATA_VERSION) as? KlibMetadataVersion ?: KlibMetadataVersion.INSTANCE
|
||||
|
||||
private val SerializedIrFile.fileMetadata: ByteArray
|
||||
internal val SerializedIrFile.fileMetadata: ByteArray
|
||||
get() = backendSpecificMetadata ?: error("Expect file caches to have backendSpecificMetadata, but '$path' doesn't")
|
||||
|
||||
val CompilerConfiguration.resolverLogger: Logger
|
||||
@@ -126,6 +128,7 @@ fun generateKLib(
|
||||
jsOutputName: String?,
|
||||
icData: List<KotlinFileSerializedData>,
|
||||
moduleFragment: IrModuleFragment,
|
||||
diagnosticReporter: DiagnosticReporter,
|
||||
builtInsPlatform: BuiltInsPlatform = BuiltInsPlatform.JS,
|
||||
serializeSingleFile: (KtSourceFile) -> ProtoBuf.PackageFragment
|
||||
) {
|
||||
@@ -138,6 +141,7 @@ fun generateKLib(
|
||||
configuration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
configuration,
|
||||
messageLogger,
|
||||
diagnosticReporter,
|
||||
files,
|
||||
outputKlibPath,
|
||||
allDependencies,
|
||||
@@ -623,33 +627,11 @@ private fun String.parseSerializedIrFileFingerprints(): List<SerializedIrFileFin
|
||||
return split(FILE_FINGERPRINTS_SEPARATOR).mapNotNull(SerializedIrFileFingerprint::fromString)
|
||||
}
|
||||
|
||||
private fun CompilerConfiguration.assertNoExportedNamesClashes(moduleName: String, files: List<KotlinFileSerializedData>) {
|
||||
val allExportedNameClashes = files
|
||||
.flatGroupBy { JsIrFileMetadata.fromByteArray(it.irData.fileMetadata).exportedNames }
|
||||
.filterValues { it.size > 1 }
|
||||
|
||||
if (allExportedNameClashes.isEmpty()) return
|
||||
|
||||
val nameClashesString = buildString {
|
||||
allExportedNameClashes.forEach { (name, files) ->
|
||||
appendLine(" * Next files contain declarations with @JsExport and name '$name'")
|
||||
files.forEach { appendLine(" - ${it.irData.path}") }
|
||||
}
|
||||
}
|
||||
|
||||
val message = """
|
||||
|There are clashes of declaration names that annotated with @JsExport in module '$moduleName'.
|
||||
|${nameClashesString}
|
||||
|Note, that this clash could affect the generated JS code in case of ES module kind usage
|
||||
""".trimMargin()
|
||||
|
||||
irMessageLogger.report(IrMessageLogger.Severity.WARNING, message, null)
|
||||
}
|
||||
|
||||
fun serializeModuleIntoKlib(
|
||||
moduleName: String,
|
||||
configuration: CompilerConfiguration,
|
||||
messageLogger: IrMessageLogger,
|
||||
diagnosticReporter: DiagnosticReporter,
|
||||
files: List<KtSourceFile>,
|
||||
klibPath: String,
|
||||
dependencies: List<KotlinLibrary>,
|
||||
@@ -670,6 +652,13 @@ fun serializeModuleIntoKlib(
|
||||
val absolutePathNormalization = configuration[CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH] ?: false
|
||||
val signatureClashChecks = configuration[CommonConfigurationKeys.PRODUCE_KLIB_SIGNATURES_CLASH_CHECKS] ?: false
|
||||
|
||||
val moduleExportedNames = moduleFragment.collectExportedNames()
|
||||
|
||||
if (builtInsPlatform == BuiltInsPlatform.JS) {
|
||||
val cleanFilesIrData = cleanFiles.map { it.irData }
|
||||
JsKlibCheckers.check(cleanFilesIrData, moduleFragment.files, moduleExportedNames, diagnosticReporter, configuration)
|
||||
}
|
||||
|
||||
val serializedIr =
|
||||
JsIrModuleSerializer(
|
||||
messageLogger,
|
||||
@@ -678,7 +667,8 @@ fun serializeModuleIntoKlib(
|
||||
normalizeAbsolutePaths = absolutePathNormalization,
|
||||
sourceBaseDirs = sourceBaseDirs,
|
||||
configuration.languageVersionSettings,
|
||||
signatureClashChecks
|
||||
signatureClashChecks,
|
||||
jsIrFileMetadataFactory = { JsIrFileMetadata(moduleExportedNames[it]?.values?.toSmartList() ?: emptyList()) }
|
||||
).serializedIrModule(moduleFragment)
|
||||
|
||||
val moduleDescriptor = moduleFragment.descriptor
|
||||
@@ -726,11 +716,7 @@ fun serializeModuleIntoKlib(
|
||||
processCompiledFileData(ioFile!!, compiledKotlinFile)
|
||||
}
|
||||
|
||||
val compiledKotlinFiles = (cleanFiles + additionalFiles).also {
|
||||
if (builtInsPlatform == BuiltInsPlatform.JS) {
|
||||
configuration.assertNoExportedNamesClashes(moduleName, it)
|
||||
}
|
||||
}
|
||||
val compiledKotlinFiles = cleanFiles + additionalFiles
|
||||
|
||||
val header = serializeKlibHeader(
|
||||
configuration.languageVersionSettings, moduleDescriptor,
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.ir.backend.js.lower.serialization.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isExpect
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.name.JsStandardClassIds
|
||||
|
||||
internal fun IrAnnotationContainer.isExportedDeclaration(): Boolean {
|
||||
return annotations.hasAnnotation(JsStandardClassIds.Annotations.JsExport.asSingleFqName()) && !isExportIgnoreDeclaration()
|
||||
}
|
||||
|
||||
internal fun IrAnnotationContainer.isExportIgnoreDeclaration(): Boolean {
|
||||
return annotations.hasAnnotation(JsStandardClassIds.Annotations.JsExportIgnore.asSingleFqName())
|
||||
}
|
||||
|
||||
private val IrDeclarationWithName.exportedName: String
|
||||
get() = getAnnotation(JsStandardClassIds.Annotations.JsName.asSingleFqName())?.getSingleConstStringArgument() ?: name.toString()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun IrConstructorCall.getSingleConstStringArgument() =
|
||||
(getValueArgument(0) as IrConst<String>).value
|
||||
|
||||
fun IrModuleFragment.collectExportedNames(): Map<IrFile, Map<IrDeclarationWithName, String>> {
|
||||
return files.associateWith { irFile ->
|
||||
val isFileExported = irFile.annotations.hasAnnotation(JsStandardClassIds.Annotations.JsExport.asSingleFqName())
|
||||
|
||||
val exportedDeclarations = irFile.declarations.asSequence()
|
||||
.filterIsInstance<IrDeclarationWithName>()
|
||||
.filter { if (isFileExported) !it.isExportIgnoreDeclaration() else it.isExportedDeclaration() }
|
||||
.filter { !it.isEffectivelyExternal() && !it.isExpect }
|
||||
.map {
|
||||
it to it.exportedName
|
||||
}.toMap()
|
||||
exportedDeclarations
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
|
||||
import org.jetbrains.kotlin.backend.common.serialization.IrFileSerializer
|
||||
import org.jetbrains.kotlin.library.impl.toArray
|
||||
import org.jetbrains.kotlin.library.encodings.WobblyTF8
|
||||
import org.jetbrains.kotlin.library.impl.IrMemoryArrayWriter
|
||||
import org.jetbrains.kotlin.library.impl.IrMemoryStringWriter
|
||||
import org.jetbrains.kotlin.library.impl.IrArrayMemoryReader
|
||||
|
||||
@@ -24,4 +23,4 @@ class JsIrFileMetadata(val exportedNames: List<String>) : IrFileSerializer.FileB
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-42
@@ -5,21 +5,21 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isExpect
|
||||
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
|
||||
import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
|
||||
import org.jetbrains.kotlin.backend.common.serialization.IrFileSerializer
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.util.IrMessageLogger
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
fun interface JsIrFileMetadataFactory {
|
||||
fun createJsIrFileMetadata(irFile: IrFile): JsIrFileMetadata
|
||||
}
|
||||
|
||||
object JsIrFileEmptyMetadataFactory : JsIrFileMetadataFactory {
|
||||
override fun createJsIrFileMetadata(irFile: IrFile) = JsIrFileMetadata(emptyList())
|
||||
}
|
||||
|
||||
class JsIrFileSerializer(
|
||||
messageLogger: IrMessageLogger,
|
||||
@@ -28,7 +28,8 @@ class JsIrFileSerializer(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
bodiesOnlyForInlines: Boolean = false,
|
||||
normalizeAbsolutePaths: Boolean,
|
||||
sourceBaseDirs: Collection<String>
|
||||
sourceBaseDirs: Collection<String>,
|
||||
private val jsIrFileMetadataFactory: JsIrFileMetadataFactory
|
||||
) : IrFileSerializer(
|
||||
messageLogger,
|
||||
declarationTable,
|
||||
@@ -38,39 +39,7 @@ class JsIrFileSerializer(
|
||||
normalizeAbsolutePaths = normalizeAbsolutePaths,
|
||||
sourceBaseDirs = sourceBaseDirs
|
||||
) {
|
||||
companion object {
|
||||
private val JS_NAME_FQN = FqName("kotlin.js.JsName")
|
||||
private val JS_EXPORT_FQN = FqName("kotlin.js.JsExport")
|
||||
private val JS_EXPORT_IGNORE_FQN = FqName("kotlin.js.JsExport.Ignore")
|
||||
}
|
||||
|
||||
private fun IrAnnotationContainer.isExportedDeclaration(): Boolean {
|
||||
return annotations.hasAnnotation(JS_EXPORT_FQN) && !isExportIgnoreDeclaration()
|
||||
}
|
||||
|
||||
private fun IrAnnotationContainer.isExportIgnoreDeclaration(): Boolean {
|
||||
return annotations.hasAnnotation(JS_EXPORT_IGNORE_FQN)
|
||||
}
|
||||
|
||||
private val IrDeclarationWithName.exportedName: String
|
||||
get() = getAnnotation(JS_NAME_FQN)?.getSingleConstStringArgument() ?: name.toString()
|
||||
|
||||
override fun backendSpecificExplicitRoot(node: IrAnnotationContainer) = node.isExportedDeclaration()
|
||||
override fun backendSpecificExplicitRootExclusion(node: IrAnnotationContainer) = node.isExportIgnoreDeclaration()
|
||||
override fun backendSpecificMetadata(irFile: IrFile): FileBackendSpecificMetadata {
|
||||
val isFileExported = irFile.annotations.hasAnnotation(JS_EXPORT_FQN)
|
||||
|
||||
val exportedNames = irFile.declarations.asSequence()
|
||||
.filterIsInstance<IrDeclarationWithName>()
|
||||
.filter { if (isFileExported) !it.isExportIgnoreDeclaration() else it.isExportedDeclaration() }
|
||||
.filter { !it.isEffectivelyExternal() && !it.isExpect }
|
||||
.map { it.exportedName }
|
||||
.toList()
|
||||
|
||||
return JsIrFileMetadata(exportedNames)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun IrConstructorCall.getSingleConstStringArgument() =
|
||||
(getValueArgument(0) as IrConst<String>).value
|
||||
override fun backendSpecificMetadata(irFile: IrFile) = jsIrFileMetadataFactory.createJsIrFileMetadata(irFile)
|
||||
}
|
||||
|
||||
+4
-2
@@ -21,7 +21,8 @@ class JsIrModuleSerializer(
|
||||
normalizeAbsolutePaths: Boolean,
|
||||
sourceBaseDirs: Collection<String>,
|
||||
private val languageVersionSettings: LanguageVersionSettings,
|
||||
shouldCheckSignaturesOnUniqueness: Boolean = true
|
||||
shouldCheckSignaturesOnUniqueness: Boolean = true,
|
||||
private val jsIrFileMetadataFactory: JsIrFileMetadataFactory = JsIrFileEmptyMetadataFactory
|
||||
) : IrModuleSerializer<JsIrFileSerializer>(messageLogger, compatibilityMode, normalizeAbsolutePaths, sourceBaseDirs) {
|
||||
|
||||
private val globalDeclarationTable = JsGlobalDeclarationTable(
|
||||
@@ -37,5 +38,6 @@ class JsIrModuleSerializer(
|
||||
normalizeAbsolutePaths = normalizeAbsolutePaths,
|
||||
sourceBaseDirs = sourceBaseDirs,
|
||||
languageVersionSettings = languageVersionSettings,
|
||||
jsIrFileMetadataFactory = jsIrFileMetadataFactory
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,9 @@ Output:
|
||||
-- JS --
|
||||
Exit code: OK
|
||||
Output:
|
||||
compiler/testData/multiplatform/jsNameClash/common.kt:1:1: warning: 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta. You can use -Xexpect-actual-classes flag to suppress this warning. Also see: https://youtrack.jetbrains.com/issue/KT-61573
|
||||
expect class ClassWithImplByExtension
|
||||
^
|
||||
compiler/testData/multiplatform/jsNameClash/js.kt:1:1: warning: 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta. You can use -Xexpect-actual-classes flag to suppress this warning. Also see: https://youtrack.jetbrains.com/issue/KT-61573
|
||||
actual class ClassWithImplByExtension
|
||||
^
|
||||
|
||||
@@ -15,3 +15,6 @@ actual annotation class A
|
||||
-- JS --
|
||||
Exit code: OK
|
||||
Output:
|
||||
compiler/testData/multiplatform/optionalExpectation/common.kt:5:1: warning: 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta. You can use -Xexpect-actual-classes flag to suppress this warning. Also see: https://youtrack.jetbrains.com/issue/KT-61573
|
||||
expect annotation class A()
|
||||
^
|
||||
|
||||
@@ -15,3 +15,9 @@ actual class Printer {
|
||||
-- JS --
|
||||
Exit code: OK
|
||||
Output:
|
||||
compiler/testData/multiplatform/simple/common.kt:1:1: warning: 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta. You can use -Xexpect-actual-classes flag to suppress this warning. Also see: https://youtrack.jetbrains.com/issue/KT-61573
|
||||
expect class Printer() {
|
||||
^
|
||||
compiler/testData/multiplatform/simple/js.kt:1:1: warning: 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta. You can use -Xexpect-actual-classes flag to suppress this warning. Also see: https://youtrack.jetbrains.com/issue/KT-61573
|
||||
actual class Printer {
|
||||
^
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
|
||||
import org.jetbrains.kotlin.incremental.md5
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
@@ -76,13 +77,15 @@ class FilePathsInKlibTest : CodegenTestCase() {
|
||||
val metadataSerializer =
|
||||
KlibMetadataIncrementalSerializer(module.compilerConfiguration, module.project, module.jsFrontEndResult.hasErrors)
|
||||
|
||||
val diagnosticReporter = DiagnosticReporterFactory.createPendingReporter()
|
||||
generateKLib(
|
||||
module,
|
||||
outputKlibPath = destination.path,
|
||||
nopack = false,
|
||||
jsOutputName = MODULE_NAME,
|
||||
icData = icData,
|
||||
moduleFragment = moduleFragment
|
||||
moduleFragment = moduleFragment,
|
||||
diagnosticReporter = diagnosticReporter
|
||||
) { file ->
|
||||
metadataSerializer.serializeScope(file, module.jsFrontEndResult.bindingContext, moduleFragment.descriptor)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,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.descriptors.impl.ModuleDescriptorImpl
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
|
||||
import org.jetbrains.kotlin.incremental.ChangedFiles
|
||||
import org.jetbrains.kotlin.incremental.IncrementalJsCompilerRunner
|
||||
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
|
||||
@@ -36,6 +37,7 @@ 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.lower.serialization.ir.collectExportedNames
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
@@ -490,12 +492,14 @@ class GenerateIrRuntime {
|
||||
files: List<KtFile>,
|
||||
perFile: Boolean = false
|
||||
): String {
|
||||
val diagnosticReporter = DiagnosticReporterFactory.createPendingReporter()
|
||||
val tmpKlibDir = createTempDirectory().also { it.toFile().deleteOnExit() }.toString()
|
||||
val metadataSerializer = KlibMetadataIncrementalSerializer(configuration, project, false)
|
||||
serializeModuleIntoKlib(
|
||||
moduleName,
|
||||
configuration,
|
||||
IrMessageLogger.None,
|
||||
diagnosticReporter,
|
||||
files.map(::KtPsiSourceFile),
|
||||
tmpKlibDir,
|
||||
emptyList(),
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.incremental
|
||||
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
|
||||
import org.jetbrains.kotlin.cli.js.klib.generateIrForKlibSerialization
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
|
||||
import org.jetbrains.kotlin.ir.backend.js.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsGenerationGranularity
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
@@ -108,13 +109,15 @@ abstract class IrAbstractInvalidationTest(
|
||||
val metadataSerializer =
|
||||
KlibMetadataIncrementalSerializer(configuration, sourceModule.project, sourceModule.jsFrontEndResult.hasErrors)
|
||||
|
||||
val diagnosticReporter = DiagnosticReporterFactory.createPendingReporter()
|
||||
generateKLib(
|
||||
sourceModule,
|
||||
outputKlibFile.canonicalPath,
|
||||
nopack = false,
|
||||
jsOutputName = moduleName,
|
||||
icData = icData,
|
||||
moduleFragment = moduleFragment
|
||||
moduleFragment = moduleFragment,
|
||||
diagnosticReporter = diagnosticReporter
|
||||
) { file ->
|
||||
metadataSerializer.serializeScope(file, sourceModule.jsFrontEndResult.bindingContext, moduleFragment.descriptor)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class FirJsKlibBackendFacade(
|
||||
configuration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
configuration,
|
||||
configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None,
|
||||
inputArtifact.diagnosticReporter,
|
||||
inputArtifact.sourceFiles,
|
||||
klibPath = outputFile,
|
||||
libraries.map { it.library },
|
||||
|
||||
@@ -51,6 +51,7 @@ class JsKlibBackendFacade(
|
||||
configuration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
configuration,
|
||||
configuration.irMessageLogger,
|
||||
inputArtifact.diagnosticReporter,
|
||||
inputArtifact.sourceFiles,
|
||||
klibPath = outputFile,
|
||||
JsEnvironmentConfigurator.getAllRecursiveLibrariesFor(module, testServices).keys.toList(),
|
||||
|
||||
+9
-2
@@ -112,12 +112,19 @@ abstract class AbstractJsPartialLinkageTestCase(private val compilerType: Compil
|
||||
"-Xir-produce-klib-file",
|
||||
"-ir-output-dir", klibFile.parentFile.absolutePath,
|
||||
"-ir-output-name", moduleName,
|
||||
"-Werror" // Halt on any unexpected warning.
|
||||
// Halt on any unexpected warning.
|
||||
"-Werror",
|
||||
// Tests suppress the INVISIBLE_REFERENCE check.
|
||||
// However, JS doesn't produce the INVISIBLE_REFERENCE error;
|
||||
// As result, it triggers a suppression error warning about the redundant suppression.
|
||||
// This flag is used to disable the warning.
|
||||
"-Xdont-warn-on-error-suppression"
|
||||
),
|
||||
dependencies.toCompilerArgs(),
|
||||
listOf(
|
||||
"-language-version", "2.0",
|
||||
"-Xsuppress-version-warnings" // Don't fail on language version warnings.
|
||||
// Don't fail on language version warnings.
|
||||
"-Xsuppress-version-warnings"
|
||||
).takeIf { compilerType.useFir },
|
||||
kotlinSourceFilePaths
|
||||
)
|
||||
|
||||
+1
-4
@@ -364,10 +364,7 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
fun testProjectWithExportedNamesClash(gradleVersion: GradleVersion) {
|
||||
project("kotlin-js-invalid-project-with-exported-clash", gradleVersion) {
|
||||
build("compileKotlinJs") {
|
||||
assertOutputContains("""
|
||||
|There are clashes of declaration names that annotated with @JsExport in module 'kotlin-js-invalid-project-with-exported-clash'.
|
||||
| * Next files contain declarations with @JsExport and name 'best'
|
||||
""".trimMargin())
|
||||
assertOutputContains("Exporting name 'best' in ES modules may clash")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -62,6 +62,7 @@ class FirWasmKlibBackendFacade(
|
||||
configuration[CommonConfigurationKeys.MODULE_NAME]!!,
|
||||
configuration,
|
||||
configuration.get(IrMessageLogger.IR_MESSAGE_LOGGER) ?: IrMessageLogger.None,
|
||||
inputArtifact.diagnosticReporter,
|
||||
inputArtifact.sourceFiles,
|
||||
klibPath = outputFile,
|
||||
libraries.map { it.library },
|
||||
|
||||
Reference in New Issue
Block a user