[K/JS] Add warning for ES-modules on the klibgen stage on the uniqueness of the exported names from the module
This commit is contained in:
@@ -136,8 +136,8 @@ open class IncrementalJsCache(
|
||||
}
|
||||
|
||||
for ((srcFile, irData) in incrementalResults.irFileData) {
|
||||
val (fileData, types, signatures, strings, declarations, bodies, fqn, debugInfos) = irData
|
||||
irTranslationResults.put(srcFile, fileData, types, signatures, strings, declarations, bodies, fqn, debugInfos)
|
||||
val (fileData, types, signatures, strings, declarations, bodies, fqn, fileMetadata, debugInfos) = irData
|
||||
irTranslationResults.put(srcFile, fileData, types, signatures, strings, declarations, bodies, fqn, fileMetadata, debugInfos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +268,7 @@ private object IrTranslationResultValueExternalizer : DataExternalizer<IrTransla
|
||||
output.writeArray(value.declarations)
|
||||
output.writeArray(value.bodies)
|
||||
output.writeArray(value.fqn)
|
||||
output.writeArray(value.fileMetadata)
|
||||
value.debugInfo?.let { output.writeArray(it) }
|
||||
}
|
||||
|
||||
@@ -302,9 +303,10 @@ private object IrTranslationResultValueExternalizer : DataExternalizer<IrTransla
|
||||
val declarations = input.readArray()
|
||||
val bodies = input.readArray()
|
||||
val fqn = input.readArray()
|
||||
val fileMetadata = input.readArray()
|
||||
val debugInfos = input.readArrayOrNull()
|
||||
|
||||
return IrTranslationResultValue(fileData, types, signatures, strings, declarations, bodies, fqn, debugInfos)
|
||||
return IrTranslationResultValue(fileData, types, signatures, strings, declarations, bodies, fqn, fileMetadata, debugInfos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,10 +332,11 @@ private class IrTranslationResultMap(
|
||||
newDeclarations: ByteArray,
|
||||
newBodies: ByteArray,
|
||||
fqn: ByteArray,
|
||||
debugInfos: ByteArray?
|
||||
newFileMetadata: ByteArray,
|
||||
debugInfos: ByteArray?,
|
||||
) {
|
||||
storage[pathConverter.toPath(sourceFile)] =
|
||||
IrTranslationResultValue(newFiledata, newTypes, newSignatures, newStrings, newDeclarations, newBodies, fqn, debugInfos)
|
||||
IrTranslationResultValue(newFiledata, newTypes, newSignatures, newStrings, newDeclarations, newBodies, fqn, newFileMetadata, debugInfos)
|
||||
}
|
||||
|
||||
operator fun get(sourceFile: File): IrTranslationResultValue? =
|
||||
|
||||
@@ -26,7 +26,8 @@ class RemoteIncrementalResultsConsumer(
|
||||
declarations: ByteArray,
|
||||
bodies: ByteArray,
|
||||
fqn: ByteArray,
|
||||
debugInfo: ByteArray?
|
||||
fileMetadata: ByteArray,
|
||||
debugInfo: ByteArray?,
|
||||
) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
+15
-9
@@ -147,6 +147,10 @@ open class IrFileSerializer(
|
||||
|
||||
protected val protoDebugInfoArray = arrayListOf<String>()
|
||||
|
||||
interface FileBackendSpecificMetadata {
|
||||
fun toByteArray(): ByteArray
|
||||
}
|
||||
|
||||
sealed class XStatementOrExpression {
|
||||
abstract fun toByteArray(): ByteArray
|
||||
|
||||
@@ -1353,6 +1357,7 @@ open class IrFileSerializer(
|
||||
open fun backendSpecificExplicitRootExclusion(node: IrAnnotationContainer): Boolean = false
|
||||
open fun keepOrderOfProperties(property: IrProperty): Boolean = !property.isConst
|
||||
open fun backendSpecificSerializeAllMembers(irClass: IrClass) = false
|
||||
open fun backendSpecificMetadata(irFile: IrFile): FileBackendSpecificMetadata? = null
|
||||
|
||||
open fun memberNeedsSerialization(member: IrDeclaration): Boolean {
|
||||
val parent = member.parent
|
||||
@@ -1454,15 +1459,16 @@ open class IrFileSerializer(
|
||||
serializeExpectActualSubstitutionTable(proto)
|
||||
|
||||
return SerializedIrFile(
|
||||
proto.build().toByteArray(),
|
||||
file.packageFqName.asString(),
|
||||
file.path,
|
||||
IrMemoryArrayWriter(protoTypeArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||
IrMemoryArrayWriter(protoIdSignatureArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||
IrMemoryStringWriter(protoStringArray).writeIntoMemory(),
|
||||
IrMemoryArrayWriter(protoBodyArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||
IrMemoryDeclarationWriter(topLevelDeclarations).writeIntoMemory(),
|
||||
IrMemoryStringWriter(protoDebugInfoArray).writeIntoMemory()
|
||||
fileData = proto.build().toByteArray(),
|
||||
fqName = file.packageFqName.asString(),
|
||||
path = file.path,
|
||||
types = IrMemoryArrayWriter(protoTypeArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||
signatures = IrMemoryArrayWriter(protoIdSignatureArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||
strings = IrMemoryStringWriter(protoStringArray).writeIntoMemory(),
|
||||
bodies = IrMemoryArrayWriter(protoBodyArray.map { it.toByteArray() }).writeIntoMemory(),
|
||||
declarations = IrMemoryDeclarationWriter(topLevelDeclarations).writeIntoMemory(),
|
||||
debugInfo = IrMemoryStringWriter(protoDebugInfoArray).writeIntoMemory(),
|
||||
backendSpecificMetadata = backendSpecificMetadata(file)?.toByteArray(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ 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 java.io.File
|
||||
@@ -101,6 +102,9 @@ private val CompilerConfiguration.metadataVersion
|
||||
private val CompilerConfiguration.expectActualLinker: Boolean
|
||||
get() = get(CommonConfigurationKeys.EXPECT_ACTUAL_LINKER) ?: false
|
||||
|
||||
private val SerializedIrFile.fileMetadata: ByteArray
|
||||
get() = backendSpecificMetadata ?: error("Expect file caches to have backendSpecificMetadata, but '$path' doesn't")
|
||||
|
||||
val CompilerConfiguration.resolverLogger: Logger
|
||||
get() = when (val messageLogger = this[IrMessageLogger.IR_MESSAGE_LOGGER]) {
|
||||
null -> DummyLogger
|
||||
@@ -627,6 +631,29 @@ 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,
|
||||
@@ -674,7 +701,18 @@ fun serializeModuleIntoKlib(
|
||||
incrementalResultsConsumer?.run {
|
||||
processPackagePart(ioFile, compiledFile.metadata, empty, empty)
|
||||
with(compiledFile.irData) {
|
||||
processIrFile(ioFile, fileData, types, signatures, strings, declarations, bodies, fqName.toByteArray(), debugInfo)
|
||||
processIrFile(
|
||||
ioFile,
|
||||
fileData,
|
||||
types,
|
||||
signatures,
|
||||
strings,
|
||||
declarations,
|
||||
bodies,
|
||||
fqName.toByteArray(),
|
||||
fileMetadata,
|
||||
debugInfo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -699,7 +737,11 @@ fun serializeModuleIntoKlib(
|
||||
processCompiledFileData(ioFile!!, compiledKotlinFile)
|
||||
}
|
||||
|
||||
val compiledKotlinFiles = (cleanFiles + additionalFiles)
|
||||
val compiledKotlinFiles = (cleanFiles + additionalFiles).also {
|
||||
if (builtInsPlatform == BuiltInsPlatform.JS) {
|
||||
configuration.assertNoExportedNamesClashes(moduleName, it)
|
||||
}
|
||||
}
|
||||
|
||||
val header = serializeKlibHeader(
|
||||
configuration.languageVersionSettings, moduleDescriptor,
|
||||
@@ -845,7 +887,8 @@ fun IncrementalDataProvider.getSerializedData(newSources: List<KtSourceFile>): L
|
||||
strings,
|
||||
bodies,
|
||||
declarations,
|
||||
debugInfo
|
||||
debugInfo,
|
||||
fileMetadata,
|
||||
)
|
||||
}
|
||||
storage.add(KotlinFileSerializedData(metaFile.metadata, irFile))
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.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
|
||||
|
||||
class JsIrFileMetadata(val exportedNames: List<String>) : IrFileSerializer.FileBackendSpecificMetadata {
|
||||
override fun toByteArray(): ByteArray {
|
||||
return IrMemoryStringWriter(exportedNames).writeIntoMemory()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromByteArray(data: ByteArray): JsIrFileMetadata {
|
||||
return JsIrFileMetadata(
|
||||
exportedNames = IrArrayMemoryReader(data).toArray().map(WobblyTF8::decode)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-4
@@ -5,15 +5,22 @@
|
||||
|
||||
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.descriptors.DeclarationDescriptor
|
||||
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.symbols.IrSymbol
|
||||
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
|
||||
|
||||
class JsIrFileSerializer(
|
||||
@@ -38,15 +45,38 @@ class JsIrFileSerializer(
|
||||
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")
|
||||
}
|
||||
|
||||
override fun backendSpecificExplicitRoot(node: IrAnnotationContainer): Boolean {
|
||||
return node.annotations.hasAnnotation(JS_EXPORT_FQN) && !backendSpecificExplicitRootExclusion(node)
|
||||
private fun IrAnnotationContainer.isExportedDeclaration(): Boolean {
|
||||
return annotations.hasAnnotation(JS_EXPORT_FQN) && !isExportIgnoreDeclaration()
|
||||
}
|
||||
|
||||
override fun backendSpecificExplicitRootExclusion(node: IrAnnotationContainer): Boolean {
|
||||
return node.annotations.hasAnnotation(JS_EXPORT_IGNORE_FQN)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@ class SerializedIrFile(
|
||||
val strings: ByteArray,
|
||||
val bodies: ByteArray,
|
||||
val declarations: ByteArray,
|
||||
val debugInfo: ByteArray?
|
||||
val debugInfo: ByteArray?,
|
||||
val backendSpecificMetadata: ByteArray?,
|
||||
)
|
||||
|
||||
class SerializedIrModule(val files: Collection<SerializedIrFile>)
|
||||
@@ -50,7 +50,8 @@ interface IncrementalResultsConsumer {
|
||||
declarations: ByteArray,
|
||||
bodies: ByteArray,
|
||||
fqn: ByteArray,
|
||||
debugInfo: ByteArray?
|
||||
fileMetadata: ByteArray,
|
||||
debugInfo: ByteArray?,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -136,9 +137,10 @@ open class IncrementalResultsConsumerImpl : IncrementalResultsConsumer {
|
||||
declarations: ByteArray,
|
||||
bodies: ByteArray,
|
||||
fqn: ByteArray,
|
||||
debugInfo: ByteArray?
|
||||
fileMetadata: ByteArray,
|
||||
debugInfo: ByteArray?,
|
||||
) {
|
||||
_irFileData[sourceFile] = IrTranslationResultValue(fileData, types, signatures, strings, declarations, bodies, fqn, debugInfo)
|
||||
_irFileData[sourceFile] = IrTranslationResultValue(fileData, types, signatures, strings, declarations, bodies, fqn, fileMetadata, debugInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.incremental.js
|
||||
|
||||
data class TranslationResultValue(val metadata: ByteArray, val binaryAst: ByteArray, val inlineData: ByteArray)
|
||||
|
||||
|
||||
data class IrTranslationResultValue(
|
||||
val fileData: ByteArray,
|
||||
val types: ByteArray,
|
||||
@@ -27,5 +26,6 @@ data class IrTranslationResultValue(
|
||||
val declarations: ByteArray,
|
||||
val bodies: ByteArray,
|
||||
val fqn: ByteArray,
|
||||
val debugInfo: ByteArray?
|
||||
)
|
||||
val fileMetadata: ByteArray,
|
||||
val debugInfo: ByteArray?,
|
||||
)
|
||||
+13
@@ -356,6 +356,19 @@ class Kotlin2JsIrGradlePluginIT : KGPBaseTest() {
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("klib compilation with the declarations name clash")
|
||||
@GradleTest
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DisplayName("per-file with the declarations validation")
|
||||
@GradleTest
|
||||
fun testPerFileProjectWithResultFilesClash(gradleVersion: GradleVersion) {
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
kotlin("js")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js(IR) {
|
||||
useEsModules()
|
||||
binaries.executable()
|
||||
nodejs()
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
kotlin.tests.individualTaskReports=true
|
||||
kotlin.incremental=false
|
||||
kotlin.incremental.js=false
|
||||
kotlin.incremental.js.ir=false
|
||||
kotlin.incremental.js.klib=false
|
||||
kotlin.incremental.multiplatform=false
|
||||
kotlin.incremental.useClasspathSnapshot=false
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* 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 first
|
||||
|
||||
@JsExport
|
||||
fun best(): Int {
|
||||
return 42
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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 second
|
||||
|
||||
@JsExport
|
||||
@JsName("best")
|
||||
public val foo = 44
|
||||
Reference in New Issue
Block a user