[FIR & IR] Implement JS MPP test infrastructure

^KT-55295 Fixed
This commit is contained in:
Ivan Kochurkin
2023-01-21 15:38:51 +01:00
committed by Space Team
parent e42efe1ee6
commit d401ff7b09
13 changed files with 141 additions and 52 deletions
@@ -35,6 +35,6 @@ object IrActualizer {
} }
private fun mergeIrFragments(mainFragment: IrModuleFragment, dependentFragments: List<IrModuleFragment>) { private fun mergeIrFragments(mainFragment: IrModuleFragment, dependentFragments: List<IrModuleFragment>) {
mainFragment.files.addAll(dependentFragments.flatMap { it.files }) mainFragment.files.addAll(0, dependentFragments.flatMap { it.files })
} }
} }
@@ -1,7 +1,7 @@
// !LANGUAGE: +MultiPlatformProjects // !LANGUAGE: +MultiPlatformProjects
// WITH_STDLIB // WITH_STDLIB
// IGNORE_BACKEND_K1: ANY // IGNORE_BACKEND_K1: ANY
// IGNORE_BACKEND_K2: JS_IR, NATIVE // IGNORE_BACKEND_K2: NATIVE
// MODULE: common // MODULE: common
// FILE: common.kt // FILE: common.kt
@@ -0,0 +1,28 @@
// KJS_WITH_FULL_RUNTIME
// IGNORE_BACKEND_K1: JS, JS_IR, JS_IR_ES6
// TARGET_BACKEND: JS_IR
// !LANGUAGE: +MultiPlatformProjects
// MODULE: common
// TARGET_PLATFORM: Common
// FILE: common.kt
expect fun func(): String
expect var prop: String
fun test(): String {
prop = "K"
return func() + prop
}
// MODULE: js()()(common)
// TARGET_PLATFORM: JS
// FILE: main.kt
actual fun func(): String = "O"
actual var prop: String = "!"
fun box() = test()
@@ -30,14 +30,18 @@ sealed class IrBackendInput : ResultingArtifact.BackendInput<IrBackendInput>() {
abstract val irPluginContext: IrPluginContext abstract val irPluginContext: IrPluginContext
data class JsIrBackendInput( data class JsIrBackendInput(
override val irModuleFragment: IrModuleFragment, val mainModuleFragment: IrModuleFragment,
val dependentModuleFragments: List<IrModuleFragment>,
override val irPluginContext: IrPluginContext, override val irPluginContext: IrPluginContext,
val sourceFiles: List<KtSourceFile>, val sourceFiles: List<KtSourceFile>,
val icData: List<KotlinFileSerializedData>, val icData: List<KotlinFileSerializedData>,
val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>, // TODO: abstract from descriptors val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>, // TODO: abstract from descriptors
val hasErrors: Boolean, val hasErrors: Boolean,
val serializeSingleFile: (KtSourceFile) -> ProtoBuf.PackageFragment val serializeSingleFile: (KtSourceFile) -> ProtoBuf.PackageFragment
) : IrBackendInput() ) : IrBackendInput() {
override val irModuleFragment: IrModuleFragment
get() = mainModuleFragment
}
data class JvmIrBackendInput( data class JvmIrBackendInput(
val state: GenerationState, val state: GenerationState,
@@ -105,6 +105,7 @@ class ClassicFrontend2IrConverter(
return IrBackendInput.JsIrBackendInput( return IrBackendInput.JsIrBackendInput(
moduleFragment, moduleFragment,
dependentModuleFragments = emptyList(),
pluginContext, pluginContext,
sourceFiles.map(::KtPsiSourceFile), sourceFiles.map(::KtPsiSourceFile),
icData, icData,
@@ -5,26 +5,24 @@
package org.jetbrains.kotlin.test.frontend.fir package org.jetbrains.kotlin.test.frontend.fir
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.fir.AbstractFirAnalyzerFacade import org.jetbrains.kotlin.fir.AbstractFirAnalyzerFacade
import org.jetbrains.kotlin.fir.FirAnalyzerFacade import org.jetbrains.kotlin.fir.FirAnalyzerFacade
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.backend.jvm.Fir2IrJvmSpecialAnnotationSymbolProvider import org.jetbrains.kotlin.fir.backend.jvm.Fir2IrJvmSpecialAnnotationSymbolProvider
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler import org.jetbrains.kotlin.fir.backend.jvm.FirJvmKotlinMangler
import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor
import org.jetbrains.kotlin.fir.moduleData
import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.impl.FirProviderImpl
import org.jetbrains.kotlin.fir.serialization.FirElementAwareSerializableStringTable import org.jetbrains.kotlin.fir.serialization.FirElementAwareSerializableStringTable
import org.jetbrains.kotlin.fir.serialization.FirKLibSerializerExtension import org.jetbrains.kotlin.fir.serialization.FirKLibSerializerExtension
import org.jetbrains.kotlin.fir.serialization.serializeSingleFirFile import org.jetbrains.kotlin.fir.serialization.serializeSingleFirFile
@@ -33,8 +31,8 @@ import org.jetbrains.kotlin.ir.backend.js.JsFactories
import org.jetbrains.kotlin.ir.backend.js.getSerializedData import org.jetbrains.kotlin.ir.backend.js.getSerializedData
import org.jetbrains.kotlin.ir.backend.js.incrementalDataProvider import org.jetbrains.kotlin.ir.backend.js.incrementalDataProvider
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
import org.jetbrains.kotlin.library.unresolvedDependencies import org.jetbrains.kotlin.library.unresolvedDependencies
import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.storage.LockBasedStorageManager
@@ -59,34 +57,67 @@ class Fir2IrJsResultsConverter(
module: TestModule, module: TestModule,
inputArtifact: FirOutputArtifact inputArtifact: FirOutputArtifact
): IrBackendInput { ): IrBackendInput {
val compilerConfigurationProvider = testServices.compilerConfigurationProvider val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val configuration = compilerConfigurationProvider.getCompilerConfiguration(module)
val fir2IrExtensions = Fir2IrExtensions.Default lateinit var mainIrPart: IrModuleFragment
val firFiles = inputArtifact.allFirFiles.values val dependentIrParts = mutableListOf<IrModuleFragment>()
val (irModuleFragment, components, pluginContext) = val sourceFiles = mutableListOf<KtSourceFile>()
inputArtifact.partsForDependsOnModules.last().firAnalyzerFacade.convertToJsIr(firFiles, fir2IrExtensions, module, configuration, testServices) val firFilesAndComponentsBySourceFile = mutableMapOf<KtSourceFile, Pair<FirFile, Fir2IrComponents>>()
lateinit var mainPluginContext: IrPluginContext
var irBuiltIns: IrBuiltInsOverFir? = null
val sourceFiles = firFiles.mapNotNull { it.sourceFile } val commonMemberStorage = Fir2IrCommonMemberStorage(
val firFilesBySourceFile = firFiles.associateBy { it.sourceFile } generateSignatures = false,
signatureComposerCreator = null,
manglerCreator = { FirJvmKotlinMangler() } // TODO: replace with potentially simpler JS version
)
for ((index, part) in inputArtifact.partsForDependsOnModules.withIndex()) {
val (irModuleFragment, components, pluginContext) =
part.firAnalyzerFacade.convertToJsIr(
part.firFiles.values,
fir2IrExtensions = Fir2IrExtensions.Default,
module,
configuration,
testServices,
commonMemberStorage,
irBuiltIns
)
irBuiltIns = components.irBuiltIns
mainPluginContext = pluginContext
if (index < inputArtifact.partsForDependsOnModules.size - 1) {
dependentIrParts.add(irModuleFragment)
} else {
mainIrPart = irModuleFragment
}
sourceFiles.addAll(part.firFiles.mapNotNull { it.value.sourceFile })
for (firFile in part.firFiles.values) {
firFilesAndComponentsBySourceFile[firFile.sourceFile!!] = firFile to components
}
}
val icData = configuration.incrementalDataProvider?.getSerializedData(sourceFiles) ?: emptyList()
val expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
val metadataVersion = configuration.metadataVersion(module.languageVersionSettings.languageVersion) val metadataVersion = configuration.metadataVersion(module.languageVersionSettings.languageVersion)
// At this point, checkers will already have been run by a previous test step. `runCheckers` returns the cached diagnostics map. // At this point, checkers will already have been run by a previous test step. `runCheckers` returns the cached diagnostics map.
val diagnosticsMap = inputArtifact.partsForDependsOnModules.last().firAnalyzerFacade.runCheckers() val diagnosticsMap = inputArtifact.partsForDependsOnModules.fold(mutableMapOf<FirFile, List<KtDiagnostic>>()) { result, part ->
result.also { it.putAll(part.firAnalyzerFacade.runCheckers()) }
}
val hasErrors = diagnosticsMap.any { entry -> entry.value.any { it.severity == Severity.ERROR } } val hasErrors = diagnosticsMap.any { entry -> entry.value.any { it.severity == Severity.ERROR } }
return IrBackendInput.JsIrBackendInput( return IrBackendInput.JsIrBackendInput(
irModuleFragment, mainIrPart,
pluginContext, dependentIrParts,
mainPluginContext,
sourceFiles, sourceFiles,
icData, configuration.incrementalDataProvider?.getSerializedData(sourceFiles) ?: emptyList(),
expectDescriptorToSymbol, expectDescriptorToSymbol = mutableMapOf(),
hasErrors, hasErrors = hasErrors
) { file -> ) { file ->
val firFile = firFilesBySourceFile[file] ?: error("cannot find FIR file by source file ${file.name} (${file.path})") val (firFile, components) = firFilesAndComponentsBySourceFile[file]
?: error("cannot find FIR file by source file ${file.name} (${file.path})")
serializeSingleFirFile( serializeSingleFirFile(
firFile, firFile,
components.session, components.session,
@@ -103,26 +134,17 @@ fun AbstractFirAnalyzerFacade.convertToJsIr(
fir2IrExtensions: Fir2IrExtensions, fir2IrExtensions: Fir2IrExtensions,
module: TestModule, module: TestModule,
configuration: CompilerConfiguration, configuration: CompilerConfiguration,
testServices: TestServices testServices: TestServices,
commonMemberStorage: Fir2IrCommonMemberStorage,
irBuiltIns: IrBuiltInsOverFir?
): Fir2IrResult { ): Fir2IrResult {
this as FirAnalyzerFacade this as FirAnalyzerFacade
val commonFirFiles = session.moduleData.dependsOnDependencies
.map { it.session }
.filter { it.kind == FirSession.Kind.Source }
.flatMap { (it.firProvider as FirProviderImpl).getAllFirFiles() }
// TODO: consider avoiding repeated libraries resolution // TODO: consider avoiding repeated libraries resolution
val libraries = resolveJsLibraries(module, testServices, configuration) val libraries = resolveJsLibraries(module, testServices, configuration)
val (dependencies, builtIns) = loadResolvedLibraries(libraries, configuration.languageVersionSettings, testServices) val (dependencies, builtIns) = loadResolvedLibraries(libraries, configuration.languageVersionSettings, testServices)
val commonMemberStorage = Fir2IrCommonMemberStorage(
generateSignatures = false,
signatureComposerCreator = null,
manglerCreator = { FirJvmKotlinMangler() } // TODO: replace with potentially simpler JS version
)
return Fir2IrConverter.createModuleFragmentWithSignaturesIfNeeded( return Fir2IrConverter.createModuleFragmentWithSignaturesIfNeeded(
session, scopeSession, firFiles + commonFirFiles, session, scopeSession, firFiles.toList(),
languageVersionSettings, languageVersionSettings,
fir2IrExtensions, fir2IrExtensions,
JsManglerIr, IrFactoryImpl, JsManglerIr, IrFactoryImpl,
@@ -132,7 +154,7 @@ fun AbstractFirAnalyzerFacade.convertToJsIr(
generateSignatures = false, generateSignatures = false,
kotlinBuiltIns = builtIns ?: DefaultBuiltIns.Instance, // TODO: consider passing externally, kotlinBuiltIns = builtIns ?: DefaultBuiltIns.Instance, // TODO: consider passing externally,
commonMemberStorage = commonMemberStorage, commonMemberStorage = commonMemberStorage,
initializedIrBuiltIns = null initializedIrBuiltIns = irBuiltIns
).also { ).also {
(it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = dependencies } (it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = dependencies }
} }
@@ -150,8 +150,8 @@ open class FirFrontendFacade(
regularModules, regularModules,
dependsOnModules, dependsOnModules,
friendModules, friendModules,
module.targetPlatform, mainModule.targetPlatform,
module.targetPlatform.getAnalyzerServices() mainModule.targetPlatform.getAnalyzerServices()
) )
moduleInfoProvider.registerModuleData(module, moduleData) moduleInfoProvider.registerModuleData(module, moduleData)
@@ -135,7 +135,7 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu
val inferMainModule = JsEnvironmentConfigurationDirectives.INFER_MAIN_MODULE in testServices.moduleStructure.allDirectives val inferMainModule = JsEnvironmentConfigurationDirectives.INFER_MAIN_MODULE in testServices.moduleStructure.allDirectives
return when { return when {
inferMainModule -> modules.last() inferMainModule -> modules.last()
else -> modules.singleOrNull { it.name == ModuleStructureExtractor.DEFAULT_MODULE_NAME } ?: modules.single() else -> modules.singleOrNull { it.name == ModuleStructureExtractor.DEFAULT_MODULE_NAME } ?: modules.last()
} }
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.test.services.impl package org.jetbrains.kotlin.test.services.impl
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.platform.CommonPlatforms import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.js.JsPlatforms import org.jetbrains.kotlin.platform.js.JsPlatforms
@@ -351,13 +352,18 @@ class ModuleStructureExtractorImpl(
languageVersionSettings = currentModuleLanguageVersionSettingsBuilder.build() languageVersionSettings = currentModuleLanguageVersionSettingsBuilder.build()
) )
modules += testModule modules += testModule
additionalSourceProviders.flatMapTo(filesOfCurrentModule) { additionalSourceProvider -> if (testModule.frontendKind != FrontendKinds.FIR ||
additionalSourceProvider.produceAdditionalFiles( !testModule.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) ||
globalDirectives ?: RegisteredDirectives.Empty, testModule.dependsOnDependencies.isEmpty()
testModule ) {
).also { additionalFiles -> additionalSourceProviders.flatMapTo(filesOfCurrentModule) { additionalSourceProvider ->
require(additionalFiles.all { it.isAdditional }) { additionalSourceProvider.produceAdditionalFiles(
"Files produced by ${additionalSourceProvider::class.qualifiedName} should have flag `isAdditional = true`" globalDirectives ?: RegisteredDirectives.Empty,
testModule
).also { additionalFiles ->
require(additionalFiles.all { it.isAdditional }) {
"Files produced by ${additionalSourceProvider::class.qualifiedName} should have flag `isAdditional = true`"
}
} }
} }
} }
@@ -5,11 +5,16 @@
package org.jetbrains.kotlin.js.test.converters package org.jetbrains.kotlin.js.test.converters
import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer
import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.backend.js.* import org.jetbrains.kotlin.ir.backend.js.JsFactories
import org.jetbrains.kotlin.ir.backend.js.jsResolveLibraries
import org.jetbrains.kotlin.ir.backend.js.resolverLogger
import org.jetbrains.kotlin.ir.backend.js.serializeModuleIntoKlib
import org.jetbrains.kotlin.ir.util.IrMessageLogger import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.js.test.utils.JsIrIncrementalDataProvider import org.jetbrains.kotlin.js.test.utils.JsIrIncrementalDataProvider
import org.jetbrains.kotlin.js.test.utils.jsIrIncrementalDataProvider import org.jetbrains.kotlin.js.test.utils.jsIrIncrementalDataProvider
@@ -23,6 +28,7 @@ import org.jetbrains.kotlin.test.frontend.fir.getAllJsDependenciesPaths
import org.jetbrains.kotlin.test.frontend.fir.resolveJsLibraries import org.jetbrains.kotlin.test.frontend.fir.resolveJsLibraries
import org.jetbrains.kotlin.test.model.ArtifactKinds import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.BinaryArtifacts import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.* import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
@@ -55,6 +61,10 @@ class FirJsKlibBackendFacade(
val libraries = resolveJsLibraries(module, testServices, configuration) val libraries = resolveJsLibraries(module, testServices, configuration)
if (firstTimeCompilation) { if (firstTimeCompilation) {
if (module.frontendKind == FrontendKinds.FIR && module.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)) {
IrActualizer.actualize(inputArtifact.mainModuleFragment, inputArtifact.dependentModuleFragments)
}
serializeModuleIntoKlib( serializeModuleIntoKlib(
configuration[CommonConfigurationKeys.MODULE_NAME]!!, configuration[CommonConfigurationKeys.MODULE_NAME]!!,
configuration, configuration,
@@ -23040,6 +23040,12 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt"); runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt");
} }
@Test
@TestMetadata("expectActualSimpleJs.kt")
public void testExpectActualSimpleJs() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimpleJs.kt");
}
@Test @Test
@TestMetadata("expectActualTypealiasLink.kt") @TestMetadata("expectActualTypealiasLink.kt")
public void testExpectActualTypealiasLink() throws Exception { public void testExpectActualTypealiasLink() throws Exception {
@@ -23040,6 +23040,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt"); runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt");
} }
@Test
@TestMetadata("expectActualSimpleJs.kt")
public void testExpectActualSimpleJs() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimpleJs.kt");
}
@Test @Test
@TestMetadata("expectActualTypealiasLink.kt") @TestMetadata("expectActualTypealiasLink.kt")
public void testExpectActualTypealiasLink() throws Exception { public void testExpectActualTypealiasLink() throws Exception {
@@ -23040,6 +23040,12 @@ public class IrJsES6CodegenBoxTestGenerated extends AbstractIrJsES6CodegenBoxTes
runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt"); runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualMemberLink.kt");
} }
@Test
@TestMetadata("expectActualSimpleJs.kt")
public void testExpectActualSimpleJs() throws Exception {
runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimpleJs.kt");
}
@Test @Test
@TestMetadata("expectActualTypealiasLink.kt") @TestMetadata("expectActualTypealiasLink.kt")
public void testExpectActualTypealiasLink() throws Exception { public void testExpectActualTypealiasLink() throws Exception {