[Wasm] Initial K2 support in new test infrastructure (KT-57230)

- Implement FirWasmSessionFactory
- Use new compiler test infra for Wasm K1 and K2
- Delete old Wasm compiler test infra
This commit is contained in:
Svyatoslav Kuzmich
2023-06-12 11:23:31 +02:00
parent 3e564236f9
commit 9b3237fff9
40 changed files with 52444 additions and 4295 deletions
@@ -10,11 +10,13 @@ import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.isJs
import org.jetbrains.kotlin.platform.isWasm
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.resolve.konan.platform.NativePlatformAnalyzerServices
import org.jetbrains.kotlin.wasm.resolve.WasmPlatformAnalyzerServices
import java.io.File
/**
@@ -39,6 +41,7 @@ fun TargetPlatform.getAnalyzerServices(): PlatformDependentAnalyzerServices {
isJs() -> JsPlatformAnalyzerServices
isNative() -> NativePlatformAnalyzerServices
isCommon() -> CommonPlatformAnalyzerServices
isWasm() -> WasmPlatformAnalyzerServices
else -> error("Unknown target platform: $this")
}
}
@@ -52,3 +52,14 @@ abstract class NativeBinaryArtifactHandler(
failureDisablesNextSteps,
doNotRunIfThereWerePreviousFailures
)
abstract class WasmBinaryArtifactHandler(
testServices: TestServices,
failureDisablesNextSteps: Boolean = false,
doNotRunIfThereWerePreviousFailures: Boolean = false
) : BinaryArtifactHandler<BinaryArtifacts.Wasm>(
testServices,
ArtifactKinds.Wasm,
failureDisablesNextSteps,
doNotRunIfThereWerePreviousFailures
)
@@ -97,6 +97,14 @@ class JsIrInterpreterDumpHandler(testServices: TestServices) : IrInterpreterDump
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
class WasmIrInterpreterDumpHandler(testServices: TestServices) : IrInterpreterDumpHandler, WasmBinaryArtifactHandler(testServices) {
override fun processModule(module: TestModule, info: BinaryArtifacts.Wasm) {
processModule(module)
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
class KlibInterpreterDumpHandler(testServices: TestServices) : IrInterpreterDumpHandler, KlibArtifactHandler(testServices) {
override fun processModule(module: TestModule, info: BinaryArtifacts.KLib) {
if (JsEnvironmentConfigurator.isMainModule(module, testServices)) return
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact
// IR backend (JVM, JS, Native)
// IR backend (JVM, JS, Native, Wasm)
sealed class IrBackendInput : ResultingArtifact.BackendInput<IrBackendInput>() {
override val kind: BackendKinds.IrBackend
get() = BackendKinds.IrBackend
@@ -84,6 +84,21 @@ sealed class IrBackendInput : ResultingArtifact.BackendInput<IrBackendInput>() {
val serializeSingleFile: (KtSourceFile, IrActualizedResult?) -> ProtoBuf.PackageFragment,
) : IrBackendInput()
class WasmBackendInput(
override val irModuleFragment: IrModuleFragment,
override val dependentIrModuleFragments: List<IrModuleFragment>,
override val irPluginContext: IrPluginContext,
val sourceFiles: List<KtSourceFile>,
val icData: List<KotlinFileSerializedData>,
val expectDescriptorToSymbol: MutableMap<DeclarationDescriptor, IrSymbol>, // TODO: abstract from descriptors
override val diagnosticReporter: BaseDiagnosticsCollector,
val hasErrors: Boolean,
override val descriptorMangler: KotlinMangler.DescriptorMangler,
override val irMangler: KotlinMangler.IrMangler,
override val firMangler: FirMangler?,
val serializeSingleFile: (KtSourceFile, IrActualizedResult?) -> ProtoBuf.PackageFragment,
) : IrBackendInput()
class JvmIrBackendInput(
val state: GenerationState,
val codegenFactory: JvmIrCodegenFactory,
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.test.builders.CompilerStepsNames.JS_ARTIFACTS_HANDLE
import org.jetbrains.kotlin.test.builders.CompilerStepsNames.JVM_ARTIFACTS_HANDLERS_STEP_NAME
import org.jetbrains.kotlin.test.builders.CompilerStepsNames.KLIB_ARTIFACTS_HANDLERS_STEP_NAME
import org.jetbrains.kotlin.test.builders.CompilerStepsNames.RAW_IR_HANDLERS_STEP_NAME
import org.jetbrains.kotlin.test.builders.CompilerStepsNames.WASM_ARTIFACTS_HANDLERS_STEP_NAME
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2ClassicBackendConverter
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontend2IrConverter
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendFacade
@@ -38,6 +39,7 @@ object CompilerStepsNames {
const val JVM_BACKEND_STEP_NAME = "jvm backend"
const val JVM_ARTIFACTS_HANDLERS_STEP_NAME = "jvm artifacts handlers"
const val JS_ARTIFACTS_HANDLERS_STEP_NAME = "js artifacts handlers"
const val WASM_ARTIFACTS_HANDLERS_STEP_NAME = "wasm artifacts handlers"
const val KLIB_ARTIFACTS_HANDLERS_STEP_NAME = "klib artifacts handlers"
}
@@ -105,6 +107,12 @@ inline fun TestConfigurationBuilder.jsArtifactsHandlersStep(
namedHandlersStep(JS_ARTIFACTS_HANDLERS_STEP_NAME, ArtifactKinds.Js, init)
}
inline fun TestConfigurationBuilder.wasmArtifactsHandlersStep(
init: HandlersStepBuilder<BinaryArtifacts.Wasm>.() -> Unit = {}
) {
namedHandlersStep(WASM_ARTIFACTS_HANDLERS_STEP_NAME, ArtifactKinds.Wasm, init)
}
inline fun TestConfigurationBuilder.klibArtifactsHandlersStep(
init: HandlersStepBuilder<BinaryArtifacts.KLib>.() -> Unit = {}
) {
@@ -142,6 +150,12 @@ inline fun TestConfigurationBuilder.configureJsArtifactsHandlersStep(
configureNamedHandlersStep(JS_ARTIFACTS_HANDLERS_STEP_NAME, ArtifactKinds.Js, init)
}
inline fun TestConfigurationBuilder.configureWasmArtifactsHandlersStep(
init: HandlersStepBuilder<BinaryArtifacts.Wasm>.() -> Unit = {}
) {
configureNamedHandlersStep(WASM_ARTIFACTS_HANDLERS_STEP_NAME, ArtifactKinds.Wasm, init)
}
inline fun TestConfigurationBuilder.configureKlibArtifactsHandlersStep(
init: HandlersStepBuilder<BinaryArtifacts.KLib>.() -> Unit = {}
) {
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2020 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.test.directives
import org.jetbrains.kotlin.test.directives.model.DirectiveApplicability
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
object WasmEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
val RUN_UNIT_TESTS by directive(
description = "Run kotlin.test unit tests (function marked with @Test)",
)
// Next directives are used only inside test system and must not be present in test file
val PATH_TO_TEST_DIR by stringDirective(
description = "Specify the path to directory with test files. " +
"This path is used to copy hierarchy from test file to test dir and use the same hierarchy in output dir.",
applicability = DirectiveApplicability.Global
)
val PATH_TO_ROOT_OUTPUT_DIR by stringDirective(
description = "Specify the path to output directory, where all artifacts will be stored",
applicability = DirectiveApplicability.Global
)
val TEST_GROUP_OUTPUT_DIR_PREFIX by stringDirective(
description = "Specify the prefix directory for output directory that will contains artifacts",
applicability = DirectiveApplicability.Global
)
}
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.KtPsiSourceFile
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForJSIR
import org.jetbrains.kotlin.cli.js.klib.TopDownAnalyzerFacadeForWasm
import org.jetbrains.kotlin.cli.js.klib.generateIrForKlibSerialization
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.CodegenFactory
@@ -33,6 +34,7 @@ import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfigurator
class ClassicFrontend2IrConverter(
testServices: TestServices
@@ -48,6 +50,7 @@ class ClassicFrontend2IrConverter(
return when (module.targetBackend) {
TargetBackend.JVM_IR -> transformToJvmIr(module, inputArtifact)
TargetBackend.JS_IR, TargetBackend.JS_IR_ES6 -> transformToJsIr(module, inputArtifact)
TargetBackend.WASM -> transformToWasmIr(module, inputArtifact)
else -> testServices.assertions.fail { "Target backend ${module.targetBackend} not supported for transformation into IR" }
}
}
@@ -125,4 +128,49 @@ class ClassicFrontend2IrConverter(
metadataSerializer.serializeScope(file, analysisResult.bindingContext, moduleFragment.descriptor)
}
}
private fun transformToWasmIr(module: TestModule, inputArtifact: ClassicFrontendOutputArtifact): IrBackendInput {
val (psiFiles, analysisResult, project, _) = inputArtifact
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val verifySignatures = JsEnvironmentConfigurationDirectives.SKIP_MANGLE_VERIFICATION !in module.directives
val sourceFiles = psiFiles.values.toList()
val icData = configuration.incrementalDataProvider?.getSerializedData(sourceFiles) ?: emptyList()
val expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
val (moduleFragment, pluginContext) = generateIrForKlibSerialization(
project,
sourceFiles,
configuration,
analysisResult,
sortDependencies(WasmEnvironmentConfigurator.getAllDependenciesMappingFor(module, testServices)),
icData,
expectDescriptorToSymbol,
IrFactoryImpl,
verifySignatures
) {
testServices.libraryProvider.getDescriptorByCompiledLibrary(it)
}
val errorPolicy = configuration.get(JSConfigurationKeys.ERROR_TOLERANCE_POLICY) ?: ErrorTolerancePolicy.DEFAULT
val hasErrors = TopDownAnalyzerFacadeForWasm.checkForErrors(sourceFiles, analysisResult.bindingContext, errorPolicy)
val metadataSerializer = KlibMetadataIncrementalSerializer(configuration, project, hasErrors)
return IrBackendInput.WasmBackendInput(
moduleFragment,
dependentIrModuleFragments = emptyList(),
pluginContext,
sourceFiles.map(::KtPsiSourceFile),
icData,
expectDescriptorToSymbol = expectDescriptorToSymbol,
diagnosticReporter = DiagnosticReporterFactory.createReporter(),
hasErrors,
descriptorMangler = (pluginContext.symbolTable as SymbolTable).signaturer.mangler,
irMangler = JsManglerIr,
firMangler = null,
) { file, _ ->
metadataSerializer.serializeScope(file, analysisResult.bindingContext, moduleFragment.descriptor)
}
}
}
@@ -346,12 +346,10 @@ class ClassicFrontendFacade(
dependencyDescriptors: List<ModuleDescriptor>,
friendsDescriptors: List<ModuleDescriptor>,
): AnalysisResult {
val needsKotlinTest = ConfigurationDirectives.WITH_STDLIB in module.directives
val runtimeKlibsNames =
listOfNotNull(
System.getProperty("kotlin.wasm.stdlib.path")!!,
System.getProperty("kotlin.wasm.kotlin.test.path")!!.takeIf { needsKotlinTest }
System.getProperty("kotlin.wasm.kotlin.test.path")!!
).map {
File(it).absolutePath
}
@@ -0,0 +1,228 @@
/*
* Copyright 2010-2020 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.test.frontend.fir
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
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.constant.EvaluatedConstTracker
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.AbstractFirAnalyzerFacade
import org.jetbrains.kotlin.fir.FirAnalyzerFacade
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.backend.js.FirJsKotlinMangler
import org.jetbrains.kotlin.fir.backend.jvm.Fir2IrJvmSpecialAnnotationSymbolProvider
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor
import org.jetbrains.kotlin.fir.serialization.FirKLibSerializerExtension
import org.jetbrains.kotlin.fir.serialization.serializeSingleFirFile
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.backend.js.JsFactories
import org.jetbrains.kotlin.ir.backend.js.getSerializedData
import org.jetbrains.kotlin.ir.backend.js.incrementalDataProvider
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
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.util.KotlinMangler
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
import org.jetbrains.kotlin.library.unresolvedDependencies
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.Frontend2BackendConverter
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
import org.jetbrains.kotlin.test.services.libraryProvider
import org.jetbrains.kotlin.utils.metadataVersion
class Fir2IrWasmResultsConverter(
testServices: TestServices
) : Frontend2BackendConverter<FirOutputArtifact, IrBackendInput>(
testServices,
FrontendKinds.FIR,
BackendKinds.IrBackend
) {
override fun transform(module: TestModule, inputArtifact: FirOutputArtifact): IrBackendInput? =
try {
transformInternal(module, inputArtifact)
} catch (e: Throwable) {
if (CodegenTestDirectives.IGNORE_FIR2IR_EXCEPTIONS_IF_FIR_CONTAINS_ERRORS in module.directives && inputArtifact.hasErrors) {
null
} else {
throw e
}
}
private fun transformInternal(
module: TestModule,
inputArtifact: FirOutputArtifact
): IrBackendInput {
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
lateinit var mainIrPart: IrModuleFragment
val dependentIrParts = mutableListOf<IrModuleFragment>()
val sourceFiles = mutableListOf<KtSourceFile>()
val firFilesAndComponentsBySourceFile = mutableMapOf<KtSourceFile, Pair<FirFile, Fir2IrComponents>>()
lateinit var mainPluginContext: IrPluginContext
var irBuiltIns: IrBuiltInsOverFir? = null
val commonMemberStorage = Fir2IrCommonMemberStorage(IdSignatureDescriptor(JsManglerDesc), FirJsKotlinMangler())
val irMangler = JsManglerIr
for ((index, part) in inputArtifact.partsForDependsOnModules.withIndex()) {
val (irModuleFragment, components, pluginContext) =
part.firAnalyzerFacade.convertToWasmIr(
part.firFiles.values,
fir2IrExtensions = Fir2IrExtensions.Default,
module,
configuration,
testServices,
commonMemberStorage,
irBuiltIns,
irMangler,
generateSignatures = true,
)
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 metadataVersion = configuration.metadataVersion(module.languageVersionSettings.languageVersion)
var actualizedExpectDeclarations: Set<FirDeclaration>? = null
return IrBackendInput.WasmBackendInput(
mainIrPart,
dependentIrParts,
mainPluginContext,
sourceFiles,
configuration.incrementalDataProvider?.getSerializedData(sourceFiles) ?: emptyList(),
expectDescriptorToSymbol = mutableMapOf(),
diagnosticReporter = DiagnosticReporterFactory.createReporter(),
hasErrors = inputArtifact.hasErrors,
descriptorMangler = commonMemberStorage.symbolTable.signaturer.mangler,
irMangler = irMangler,
firMangler = commonMemberStorage.firSignatureComposer.mangler,
) { file, irActualizedResult ->
val (firFile, components) = firFilesAndComponentsBySourceFile[file]
?: error("cannot find FIR file by source file ${file.name} (${file.path})")
if (actualizedExpectDeclarations == null && irActualizedResult != null) {
actualizedExpectDeclarations = irActualizedResult.extractFirDeclarations()
}
serializeSingleFirFile(
firFile,
components.session,
components.scopeSession,
actualizedExpectDeclarations,
FirKLibSerializerExtension(
components.session, metadataVersion,
ConstValueProviderImpl(components),
allowErrorTypes = false, exportKDoc = false,
additionalAnnotationsProvider = null,
),
configuration.languageVersionSettings,
)
}
}
}
fun AbstractFirAnalyzerFacade.convertToWasmIr(
firFiles: Collection<FirFile>,
fir2IrExtensions: Fir2IrExtensions,
module: TestModule,
configuration: CompilerConfiguration,
testServices: TestServices,
commonMemberStorage: Fir2IrCommonMemberStorage,
irBuiltIns: IrBuiltInsOverFir?,
irMangler: KotlinMangler.IrMangler,
generateSignatures: Boolean
): Fir2IrResult {
this as FirAnalyzerFacade
// TODO: consider avoiding repeated libraries resolution
val libraries = resolveWasmLibraries(module, testServices, configuration)
val (dependencies, builtIns) = loadResolvedLibraries(libraries, configuration.languageVersionSettings, testServices)
val fir2IrConfiguration = Fir2IrConfiguration(
languageVersionSettings = configuration.languageVersionSettings,
linkViaSignatures = generateSignatures,
evaluatedConstTracker = configuration
.putIfAbsent(CommonConfigurationKeys.EVALUATED_CONST_TRACKER, EvaluatedConstTracker.create()),
inlineConstTracker = null,
)
return Fir2IrConverter.createModuleFragmentWithSignaturesIfNeeded(
session, scopeSession, firFiles.toList(),
fir2IrExtensions,
fir2IrConfiguration,
irMangler, IrFactoryImpl,
Fir2IrVisibilityConverter.Default,
Fir2IrJvmSpecialAnnotationSymbolProvider(), // TODO: replace with appropriate (probably empty) implementation
irGeneratorExtensions,
kotlinBuiltIns = builtIns ?: DefaultBuiltIns.Instance, // TODO: consider passing externally,
commonMemberStorage = commonMemberStorage,
initializedIrBuiltIns = irBuiltIns
).also {
(it.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = dependencies }
}
}
private fun loadResolvedLibraries(
resolvedLibraries: List<KotlinResolvedLibrary>,
languageVersionSettings: LanguageVersionSettings,
testServices: TestServices
): Pair<List<ModuleDescriptor>, KotlinBuiltIns?> {
var builtInsModule: KotlinBuiltIns? = null
val dependencies = mutableListOf<ModuleDescriptorImpl>()
return resolvedLibraries.map { resolvedLibrary ->
// resolvedLibrary.library.libraryName in fact resolves to (modified) file path, which is confising and maybe should be refactored
testServices.libraryProvider.getOrCreateStdlibByPath(resolvedLibrary.library.libraryName) {
// TODO: check safety of the approach of creating a separate storage manager per library
val storageManager = LockBasedStorageManager("ModulesStructure")
val moduleDescriptor = JsFactories.DefaultDeserializedDescriptorFactory.createDescriptorOptionalBuiltIns(
resolvedLibrary.library,
languageVersionSettings,
storageManager,
builtInsModule,
packageAccessHandler = null,
lookupTracker = LookupTracker.DO_NOTHING
)
dependencies += moduleDescriptor
moduleDescriptor.setDependencies(ArrayList(dependencies))
Pair(moduleDescriptor, resolvedLibrary.library)
}.also {
val isBuiltIns = resolvedLibrary.library.unresolvedDependencies.isEmpty()
if (isBuiltIns) builtInsModule = it.builtIns
}
} to builtInsModule
}
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.isJs
import org.jetbrains.kotlin.platform.isWasm
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtFile
@@ -55,6 +56,7 @@ import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.runners.lightTreeSyntaxDiagnosticsReporterHolder
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfigurator
import java.nio.file.Paths
open class FirFrontendFacade(
@@ -240,6 +242,20 @@ open class FirFrontendFacade(
registerExtraComponents = ::registerExtraComponents,
)
}
module.targetPlatform.isWasm() -> {
projectEnvironment = null
TestFirWasmSessionFactory.createLibrarySession(
moduleName,
sessionProvider,
moduleDataProvider,
module,
testServices,
configuration,
extensionRegistrars,
languageVersionSettings,
registerExtraComponents = ::registerExtraComponents,
)
}
else -> error("Unsupported")
}
return projectEnvironment
@@ -380,6 +396,17 @@ open class FirFrontendFacade(
init = sessionConfigurator
)
}
targetPlatform.isWasm() -> {
TestFirWasmSessionFactory.createModuleBasedSession(
moduleData,
sessionProvider,
extensionRegistrars,
languageVersionSettings,
null,
registerExtraComponents = ::registerExtraComponents,
sessionConfigurator,
)
}
else -> error("Unsupported")
}
}
@@ -413,6 +440,13 @@ open class FirFrontendFacade(
dependencies(transitiveLibraries.map { it.toPath().toAbsolutePath() })
friendDependencies(friendLibraries.map { it.toPath().toAbsolutePath() })
}
targetPlatform.isWasm() -> {
val runtimeKlibsPaths = WasmEnvironmentConfigurator.getRuntimePathsForModule()
val (transitiveLibraries, friendLibraries) = getTransitivesAndFriends(mainModule, testServices)
dependencies(runtimeKlibsPaths.map { Paths.get(it).toAbsolutePath() })
dependencies(transitiveLibraries.map { it.toPath().toAbsolutePath() })
friendDependencies(friendLibraries.map { it.toPath().toAbsolutePath() })
}
else -> error("Unsupported")
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2021 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.test.frontend.fir
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.deserialization.ModuleDataProvider
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.session.FirSessionConfigurator
import org.jetbrains.kotlin.fir.session.FirWasmSessionFactory
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.backend.js.resolverLogger
import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.test.model.DependencyRelation
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.configuration.WasmEnvironmentConfigurator
import java.io.File
object TestFirWasmSessionFactory {
fun createLibrarySession(
mainModuleName: Name,
sessionProvider: FirProjectSessionProvider,
moduleDataProvider: ModuleDataProvider,
module: TestModule,
testServices: TestServices,
configuration: CompilerConfiguration,
extensionRegistrars: List<FirExtensionRegistrar>,
languageVersionSettings: LanguageVersionSettings,
registerExtraComponents: ((FirSession) -> Unit),
): FirSession {
val resolvedLibraries = resolveLibraries(configuration, getAllWasmDependenciesPaths(module, testServices))
return FirWasmSessionFactory.createLibrarySession(
mainModuleName,
resolvedLibraries.map { it.library },
sessionProvider,
moduleDataProvider,
extensionRegistrars,
languageVersionSettings,
registerExtraComponents,
)
}
fun createModuleBasedSession(
mainModuleData: FirModuleData, sessionProvider: FirProjectSessionProvider, extensionRegistrars: List<FirExtensionRegistrar>,
languageVersionSettings: LanguageVersionSettings, lookupTracker: LookupTracker?,
registerExtraComponents: ((FirSession) -> Unit),
sessionConfigurator: FirSessionConfigurator.() -> Unit,
): FirSession =
FirWasmSessionFactory.createModuleBasedSession(
mainModuleData,
sessionProvider,
extensionRegistrars,
languageVersionSettings,
lookupTracker,
icData = null,
registerExtraComponents,
sessionConfigurator
)
}
fun resolveWasmLibraries(
module: TestModule,
testServices: TestServices,
configuration: CompilerConfiguration
): List<KotlinResolvedLibrary> {
return resolveLibraries(configuration, getAllWasmDependenciesPaths(module, testServices))
}
fun getAllWasmDependenciesPaths(module: TestModule, testServices: TestServices): List<String> {
val (runtimeKlibsPaths, transitiveLibraries, friendLibraries) = getWasmDependencies(module, testServices)
return runtimeKlibsPaths + transitiveLibraries.map { it.path } + friendLibraries.map { it.path }
}
fun getWasmDependencies(module: TestModule, testServices: TestServices): Triple<List<String>, List<File>, List<File>> {
val runtimeKlibsPaths = WasmEnvironmentConfigurator.getRuntimePathsForModule()
val transitiveLibraries = WasmEnvironmentConfigurator.getKlibDependencies(module, testServices, DependencyRelation.RegularDependency)
val friendLibraries = WasmEnvironmentConfigurator.getKlibDependencies(module, testServices, DependencyRelation.FriendDependency)
return Triple(runtimeKlibsPaths, transitiveLibraries, friendLibraries)
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.test.model
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.backend.wasm.WasmCompilerResult
import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo
import org.jetbrains.kotlin.ir.backend.js.CompilerResult
@@ -49,6 +50,14 @@ object BinaryArtifacts {
get() = ArtifactKinds.Native
}
class Wasm(
val compilerResult: WasmCompilerResult,
val compilerResultWithDCE: WasmCompilerResult,
) : ResultingArtifact.Binary<Wasm>() {
override val kind: BinaryKind<Wasm>
get() = ArtifactKinds.Wasm
}
class KLib(val outputFile: File) : ResultingArtifact.Binary<KLib>() {
override val kind: BinaryKind<KLib>
get() = ArtifactKinds.KLib
@@ -50,6 +50,7 @@ object ArtifactKinds {
object Jvm : BinaryKind<BinaryArtifacts.Jvm>("JVM")
object Js : BinaryKind<BinaryArtifacts.Js>("JS")
object Native : BinaryKind<BinaryArtifacts.Native>("Native")
object Wasm : BinaryKind<BinaryArtifacts.Wasm>("Wasm")
object KLib : BinaryKind<BinaryArtifacts.KLib>("KLib")
fun fromString(string: String): BinaryKind<*>? {
@@ -57,6 +58,7 @@ object ArtifactKinds {
"Jvm" -> Jvm
"Js" -> Js
"Native" -> Native
"Wasm" -> Wasm
"KLib" -> KLib
else -> null
}
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.test.model.DependencyRelation
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.dependencyProvider
import org.jetbrains.kotlin.test.services.jsLibraryProvider
import org.jetbrains.kotlin.test.services.libraryProvider
import java.io.File
fun getKlibDependencies(module: TestModule, testServices: TestServices, kind: DependencyRelation): List<File> {
@@ -39,5 +39,5 @@ fun getKlibDependencies(module: TestModule, testServices: TestServices, kind: De
fun getDependencies(module: TestModule, testServices: TestServices, kind: DependencyRelation): List<ModuleDescriptor> {
return getKlibDependencies(module, testServices, kind)
.map { testServices.jsLibraryProvider.getDescriptorByPath(it.absolutePath) }
.map { testServices.libraryProvider.getDescriptorByPath(it.absolutePath) }
}
@@ -11,16 +11,19 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.SourceMapSourceEmbedding
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.INFER_MAIN_MODULE
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.EXPECT_ACTUAL_LINKER
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.NO_INLINE
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.PROPERTY_LAZY_INITIALIZATION
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives.SOURCE_MAP_EMBED_SOURCES
import org.jetbrains.kotlin.test.directives.WasmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.frontend.classic.moduleDescriptorProvider
import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.DependencyRelation
@@ -30,7 +33,98 @@ import java.io.File
class WasmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
override val directiveContainers: List<DirectivesContainer>
get() = listOf(JsEnvironmentConfigurationDirectives)
get() = listOf(WasmEnvironmentConfigurationDirectives)
companion object {
private const val OUTPUT_KLIB_DIR_NAME = "outputKlibDir"
fun getRuntimePathsForModule(): List<String> {
return listOf(System.getProperty("kotlin.wasm.stdlib.path")!!, System.getProperty("kotlin.wasm.kotlin.test.path")!!)
}
fun getKlibDependencies(module: TestModule, testServices: TestServices, kind: DependencyRelation): List<File> {
val visited = mutableSetOf<TestModule>()
fun getRecursive(module: TestModule, relation: DependencyRelation) {
val dependencies = if (relation == DependencyRelation.FriendDependency) {
module.friendDependencies
} else {
module.regularDependencies
}
dependencies
.filter { it.kind != DependencyKind.Source }
.map { testServices.dependencyProvider.getTestModule(it.moduleName) }.forEach {
if (it !in visited) {
visited += it
getRecursive(it, relation)
}
}
}
getRecursive(module, kind)
return visited.map { testServices.dependencyProvider.getArtifact(it, ArtifactKinds.KLib).outputFile }
}
fun getDependencies(module: TestModule, testServices: TestServices, kind: DependencyRelation): List<ModuleDescriptor> {
return getKlibDependencies(module, testServices, kind)
.map { testServices.libraryProvider.getDescriptorByPath(it.absolutePath) }
}
fun getWasmKlibArtifactPath(testServices: TestServices, moduleName: String): String {
return getWasmKlibOutputDir(testServices).absolutePath + File.separator + JsEnvironmentConfigurator.getJsArtifactSimpleName(
testServices,
moduleName
)
}
fun getWasmKlibOutputDir(testServices: TestServices): File {
return testServices.temporaryDirectoryManager.getOrCreateTempDirectory(OUTPUT_KLIB_DIR_NAME)
}
fun getAllRecursiveDependenciesFor(module: TestModule, testServices: TestServices): Set<ModuleDescriptorImpl> {
val visited = mutableSetOf<ModuleDescriptorImpl>()
fun getRecursive(descriptor: ModuleDescriptor) {
descriptor.allDependencyModules.forEach {
if (it is ModuleDescriptorImpl && it !in visited) {
visited += it
getRecursive(it)
}
}
}
getRecursive(testServices.moduleDescriptorProvider.getModuleDescriptor(module))
return visited
}
fun getAllRecursiveLibrariesFor(module: TestModule, testServices: TestServices): Map<KotlinLibrary, ModuleDescriptorImpl> {
val dependencies = getAllRecursiveDependenciesFor(module, testServices)
return dependencies.associateBy { testServices.libraryProvider.getCompiledLibraryByDescriptor(it) }
}
fun getAllDependenciesMappingFor(module: TestModule, testServices: TestServices): Map<KotlinLibrary, List<KotlinLibrary>> {
val allRecursiveLibraries: Map<KotlinLibrary, ModuleDescriptor> =
getAllRecursiveLibrariesFor(module, testServices)
val m2l = allRecursiveLibraries.map { it.value to it.key }.toMap()
return allRecursiveLibraries.keys.associateWith { m ->
val descriptor = allRecursiveLibraries[m] ?: error("No descriptor found for library ${m.libraryName}")
descriptor.allDependencyModules.filter { it != descriptor }.map { m2l.getValue(it) }
}
}
fun getMainModule(testServices: TestServices): TestModule {
val modules = testServices.moduleStructure.modules
val inferMainModule = INFER_MAIN_MODULE in testServices.moduleStructure.allDirectives
return when {
inferMainModule -> modules.last()
else -> modules.singleOrNull { it.name == ModuleStructureExtractor.DEFAULT_MODULE_NAME } ?: modules.last()
}
}
fun isMainModule(module: TestModule, testServices: TestServices): Boolean {
return module == getMainModule(testServices)
}
}
override fun provideAdditionalAnalysisFlags(
directives: RegisteredDirectives,
@@ -48,11 +142,11 @@ class WasmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfi
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule) {
val registeredDirectives = module.directives
configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.ES)
val noInline = registeredDirectives.contains(NO_INLINE)
configuration.put(CommonConfigurationKeys.DISABLE_INLINE, noInline)
configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name)
configuration.put(JSConfigurationKeys.WASM_ENABLE_ASSERTS, true)
configuration.put(JSConfigurationKeys.WASM_ENABLE_ARRAY_RANGE_CHECKS, true)
val sourceDirs = module.files.map { it.originalFile.parent }.distinct()
configuration.put(JSConfigurationKeys.SOURCE_MAP_SOURCE_ROOTS, sourceDirs)
configuration.put(JSConfigurationKeys.SOURCE_MAP, true)