[JS Test] Introduce tests for loading metadata produced by JS compiler

This commit is contained in:
Dmitriy Novozhilov
2023-05-31 12:43:51 +03:00
committed by Space Team
parent 2290a096d1
commit 7eca772ec2
13 changed files with 3957 additions and 120 deletions
@@ -7,9 +7,13 @@ package org.jetbrains.kotlin.test
import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.VirtualFileManager
import org.jetbrains.kotlin.backend.common.CommonKLibResolver
import org.jetbrains.kotlin.cli.common.SessionWithSources
import org.jetbrains.kotlin.cli.common.prepareJsSessions
import org.jetbrains.kotlin.cli.common.prepareJvmSessions import org.jetbrains.kotlin.cli.common.prepareJvmSessions
import org.jetbrains.kotlin.cli.jvm.compiler.VfsBasedProjectEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.VfsBasedProjectEnvironment
import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.fir.BinaryModuleData import org.jetbrains.kotlin.fir.BinaryModuleData
import org.jetbrains.kotlin.fir.DependencyListForCliModule import org.jetbrains.kotlin.fir.DependencyListForCliModule
@@ -19,31 +23,119 @@ import org.jetbrains.kotlin.fir.declarations.utils.nameOrSpecialName
import org.jetbrains.kotlin.fir.renderer.FirRenderer import org.jetbrains.kotlin.fir.renderer.FirRenderer
import org.jetbrains.kotlin.fir.resolve.providers.firProvider import org.jetbrains.kotlin.fir.resolve.providers.firProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.isJs
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices
import org.jetbrains.kotlin.test.backend.handlers.JvmBinaryArtifactHandler import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.PLATFORM_DEPENDANT_METADATA
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.LANGUAGE_VERSION import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.LANGUAGE_VERSION
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.frontend.fir.getAllJsDependenciesPaths
import org.jetbrains.kotlin.test.model.* import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.* import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndRemoveRedundantEmptyLinesAtTheEnd import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndRemoveRedundantEmptyLinesAtTheEnd
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumper import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumper
import org.jetbrains.kotlin.test.utils.withExtension import org.jetbrains.kotlin.test.utils.withExtension
import org.jetbrains.kotlin.util.DummyLogger
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
import java.io.File import java.io.File
class LoadedMetadataDumpHandler(testServices: TestServices) : JvmBinaryArtifactHandler(testServices) { class JvmLoadedMetadataDumpHandler(testServices: TestServices) : AbstractLoadedMetadataDumpHandler<BinaryArtifacts.Jvm>(
testServices,
ArtifactKinds.Jvm
) {
override val targetPlatform: TargetPlatform
get() = JvmPlatforms.defaultJvmPlatform
override val platformAnalyzerServices: PlatformDependentAnalyzerServices
get() = JvmPlatformAnalyzerServices
override val dependencyKind: DependencyKind
get() = DependencyKind.Binary
override fun prepareSessions(
module: TestModule,
configuration: CompilerConfiguration,
environment: VfsBasedProjectEnvironment,
moduleName: Name,
libraryList: DependencyListForCliModule,
): List<SessionWithSources<KtFile>> {
return prepareJvmSessions(
files = emptyList(),
configuration, environment, moduleName,
extensionRegistrars = emptyList(),
environment.getSearchScopeForProjectLibraries(),
libraryList,
isCommonSource = { false },
fileBelongsToModule = { _, _ -> false },
createProviderAndScopeForIncrementalCompilation = { null }
)
}
}
class KlibLoadedMetadataDumpHandler(testServices: TestServices) : AbstractLoadedMetadataDumpHandler<BinaryArtifacts.KLib>(
testServices,
ArtifactKinds.KLib
) {
override val targetPlatform: TargetPlatform
get() = JsPlatforms.defaultJsPlatform
override val platformAnalyzerServices: PlatformDependentAnalyzerServices
get() = JsPlatformAnalyzerServices
override val dependencyKind: DependencyKind
get() = DependencyKind.KLib
override fun prepareSessions(
module: TestModule,
configuration: CompilerConfiguration,
environment: VfsBasedProjectEnvironment,
moduleName: Name,
libraryList: DependencyListForCliModule,
): List<SessionWithSources<KtFile>> {
val libraries = getAllJsDependenciesPaths(module, testServices)
val resolvedLibraries = CommonKLibResolver.resolve(libraries, DummyLogger).getFullResolvedList()
return prepareJsSessions(
files = emptyList(),
configuration,
moduleName,
resolvedLibraries.map { it.library },
libraryList,
extensionRegistrars = emptyList(),
isCommonSource = { false },
fileBelongsToModule = { _, _ -> false },
lookupTracker = null,
icData = null
)
}
}
abstract class AbstractLoadedMetadataDumpHandler<A : ResultingArtifact.Binary<A>>(
testServices: TestServices,
override val artifactKind: BinaryKind<A>
) : BinaryArtifactHandler<A>(
testServices,
artifactKind,
failureDisablesNextSteps = false,
doNotRunIfThereWerePreviousFailures = false
) {
private val dumper: MultiModuleInfoDumper = MultiModuleInfoDumper() private val dumper: MultiModuleInfoDumper = MultiModuleInfoDumper()
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) { override val directiveContainers: List<DirectivesContainer>
get() = listOf(FirDiagnosticsDirectives)
override fun processModule(module: TestModule, info: A) {
if (testServices.loadedMetadataSuppressionDirective in module.directives) return if (testServices.loadedMetadataSuppressionDirective in module.directives) return
val languageVersion = module.directives.singleOrZeroValue(LANGUAGE_VERSION) val languageVersion = module.directives.singleOrZeroValue(LANGUAGE_VERSION)
val languageVersionSettings = if (languageVersion != null) { val languageVersionSettings = if (languageVersion != null) {
@@ -51,10 +143,11 @@ class LoadedMetadataDumpHandler(testServices: TestServices) : JvmBinaryArtifactH
} else { } else {
LanguageVersionSettingsImpl.DEFAULT LanguageVersionSettingsImpl.DEFAULT
} }
val emptyModule = TestModule( val emptyModule = TestModule(
name = "empty", JvmPlatforms.defaultJvmPlatform, TargetBackend.JVM_IR, FrontendKinds.FIR, name = "empty", module.targetPlatform, module.targetBackend, FrontendKinds.FIR,
BackendKinds.IrBackend, ArtifactKinds.Jvm, files = emptyList(), BackendKinds.IrBackend, module.binaryKind, files = emptyList(),
allDependencies = listOf(DependencyDescription(module.name, DependencyKind.Binary, DependencyRelation.RegularDependency)), allDependencies = listOf(DependencyDescription(module.name, dependencyKind, DependencyRelation.RegularDependency)),
RegisteredDirectives.Empty, languageVersionSettings RegisteredDirectives.Empty, languageVersionSettings
) )
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(emptyModule) val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(emptyModule)
@@ -63,20 +156,22 @@ class LoadedMetadataDumpHandler(testServices: TestServices) : JvmBinaryArtifactH
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL), VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL),
testServices.compilerConfigurationProvider.getPackagePartProviderFactory(emptyModule) testServices.compilerConfigurationProvider.getPackagePartProviderFactory(emptyModule)
) )
val moduleName = Name.identifier(emptyModule.name)
val binaryModuleData = BinaryModuleData.initialize( val binaryModuleData = BinaryModuleData.initialize(
Name.identifier(emptyModule.name), moduleName,
JvmPlatforms.defaultJvmPlatform, targetPlatform,
JvmPlatformAnalyzerServices platformAnalyzerServices
) )
val session = prepareJvmSessions( val libraryList = FirFrontendFacade.initializeLibraryList(
files = emptyList<KtFile>(), emptyModule, binaryModuleData, targetPlatform, configuration, testServices
configuration, environment, Name.identifier(emptyModule.name), )
extensionRegistrars = emptyList(),
environment.getSearchScopeForProjectLibraries(), val session = prepareSessions(
DependencyListForCliModule.build(binaryModuleData), emptyModule,
isCommonSource = { false }, configuration,
fileBelongsToModule = { _, _ -> false }, environment,
createProviderAndScopeForIncrementalCompilation = { null } moduleName,
libraryList
).single().session ).single().session
val packageFqName = FqName("test") val packageFqName = FqName("test")
@@ -84,42 +179,104 @@ class LoadedMetadataDumpHandler(testServices: TestServices) : JvmBinaryArtifactH
.append(collectPackageContent(session, packageFqName, extractNames(module, packageFqName))) .append(collectPackageContent(session, packageFqName, extractNames(module, packageFqName)))
} }
@Suppress("warnings") protected abstract val targetPlatform: TargetPlatform
protected abstract val platformAnalyzerServices: PlatformDependentAnalyzerServices
protected abstract val dependencyKind: DependencyKind
protected abstract fun prepareSessions(
module: TestModule,
configuration: CompilerConfiguration,
environment: VfsBasedProjectEnvironment,
moduleName: Name,
libraryList: DependencyListForCliModule,
): List<SessionWithSources<KtFile>>
override fun processAfterAllModules(someAssertionWasFailed: Boolean) { override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (dumper.isEmpty()) return if (dumper.isEmpty()) return
val testDataFile = testServices.moduleStructure.originalTestDataFiles.first() val testDataFile = testServices.moduleStructure.originalTestDataFiles.first()
val frontendKind = testServices.defaultsProvider.defaultFrontend
val commonExtension = ".fir.txt" val commonExtension = ".fir.txt"
val (specificExtension, otherSpecificExtension) = when (testServices.defaultsProvider.defaultFrontend) { val (specificExtension, otherSpecificExtension) = when (frontendKind) {
FrontendKinds.ClassicFrontend -> ".fir.k1.txt" to ".fir.k2.txt" FrontendKinds.ClassicFrontend -> ".fir.k1.txt" to ".fir.k2.txt"
FrontendKinds.FIR -> ".fir.k2.txt" to ".fir.k1.txt" FrontendKinds.FIR -> ".fir.k2.txt" to ".fir.k1.txt"
else -> shouldNotBeCalled() else -> shouldNotBeCalled()
} }
val commonFirDump = testDataFile.withExtension(commonExtension)
val specificFirDump = testDataFile.withExtension(specificExtension)
val expectedFile = when { val targetPlatform = testServices.defaultsProvider.defaultPlatform
commonFirDump.exists() -> commonFirDump if (PLATFORM_DEPENDANT_METADATA in testServices.moduleStructure.allDirectives) {
else -> specificFirDump val platformExtension = specificExtension.replace(".txt", "${targetPlatform.suffix}.txt")
} val otherPlatformExtension = specificExtension.replace(".txt", "${targetPlatform.oppositeSuffix}.txt")
val actualText = dumper.generateResultingDump() val expectedFile = testDataFile.withExtension(platformExtension)
assertions.assertEqualsToFile(expectedFile, actualText, message = { "Content is not equal" }) val actualText = dumper.generateResultingDump()
assertions.assertEqualsToFile(expectedFile, actualText, message = { "Content is not equal" })
if (commonFirDump.exists() && specificFirDump.exists()) { val checks = listOf(commonExtension, specificExtension, otherSpecificExtension).map { extension ->
assertions.fail { {
""" val baseFile = testDataFile.withExtension(extension)
assertions.assertFalse(baseFile.exists()) {
"Base file $baseFile exists in presence of $PLATFORM_DEPENDANT_METADATA directive. Please remove file or directive"
}
}
}
assertions.assertAll(checks)
val secondFile = testDataFile.withExtension(otherPlatformExtension)
val common = testDataFile.withExtension(specificExtension)
checkDumpsIdentity(
testDataFile, expectedFile, secondFile, common,
postProcessTestData = { it.replace("// $PLATFORM_DEPENDANT_METADATA\n", "") }
)
} else {
val commonFirDump = testDataFile.withExtension(commonExtension)
val specificFirDump = testDataFile.withExtension(specificExtension)
val expectedFile = when {
commonFirDump.exists() -> commonFirDump
else -> specificFirDump
}
val actualText = dumper.generateResultingDump()
assertions.assertEqualsToFile(expectedFile, actualText, message = { "Content is not equal" })
if (commonFirDump.exists() && specificFirDump.exists()) {
assertions.fail {
"""
Common dump ${commonFirDump.name} and specific ${specificFirDump.name} exist at the same time Common dump ${commonFirDump.name} and specific ${specificFirDump.name} exist at the same time
Please remove ${specificFirDump.name} Please remove ${specificFirDump.name}
""".trimIndent() """.trimIndent()
}
}
if (!commonFirDump.exists()) {
val otherFirDump = testDataFile.withExtension(otherSpecificExtension)
checkDumpsIdentity(testDataFile, specificFirDump, otherFirDump, commonFirDump)
} }
}
if (!commonFirDump.exists()) {
val otherFirDump = testDataFile.withExtension(otherSpecificExtension)
checkK1AndK2DumpsIdentity(specificFirDump, otherFirDump, commonFirDump)
} }
} }
private fun checkK1AndK2DumpsIdentity(file1: File, file2: File, commonFile: File) { private val TargetPlatform.suffix: String
get() = when {
isJvm() -> ".jvm"
isJs() -> ".klib"
else -> error("Unsupported platform: $this")
}
private val TargetPlatform.oppositeSuffix: String
get() = when {
isJvm() -> ".klib"
isJs() -> ".jvm"
else -> error("Unsupported platform: $this")
}
private fun checkDumpsIdentity(
testDataFile: File,
file1: File,
file2: File,
commonFile: File,
postProcessTestData: ((String) -> String)? = null
) {
if (!file1.exists() || !file2.exists()) return if (!file1.exists() || !file2.exists()) return
val dump1 = file1.readText().trimTrailingWhitespacesAndRemoveRedundantEmptyLinesAtTheEnd() val dump1 = file1.readText().trimTrailingWhitespacesAndRemoveRedundantEmptyLinesAtTheEnd()
val dump2 = file2.readText().trimTrailingWhitespacesAndRemoveRedundantEmptyLinesAtTheEnd() val dump2 = file2.readText().trimTrailingWhitespacesAndRemoveRedundantEmptyLinesAtTheEnd()
@@ -127,6 +284,9 @@ class LoadedMetadataDumpHandler(testServices: TestServices) : JvmBinaryArtifactH
commonFile.writeText(dump1) commonFile.writeText(dump1)
file1.delete() file1.delete()
file2.delete() file2.delete()
if (postProcessTestData != null) {
testDataFile.writeText(postProcessTestData(testDataFile.readText()))
}
assertions.fail { assertions.fail {
""" """
Files ${file1.name} and ${file2.name} are identical Files ${file1.name} and ${file2.name} are identical
@@ -37,7 +37,7 @@ abstract class AbstractFirLoadCompiledJvmKotlinTestBase<F : ResultingArtifact.Fr
) )
configureJvmArtifactsHandlersStep { configureJvmArtifactsHandlersStep {
useHandlers(::LoadedMetadataDumpHandler) useHandlers(::JvmLoadedMetadataDumpHandler)
} }
forTestsMatching("compiler/testData/loadJava/compiledKotlinWithStdlib/*") { forTestsMatching("compiler/testData/loadJava/compiledKotlinWithStdlib/*") {
@@ -81,6 +81,13 @@ object FirDiagnosticsDirectives : SimpleDirectivesContainer() {
Directive must contain description of ignoring in argument Directive must contain description of ignoring in argument
""".trimIndent() """.trimIndent()
) )
val PLATFORM_DEPENDANT_METADATA by directive(
description = """
Generate separate dumps for JVM and JS load compiled kotlin tests
See AbstractLoadedMetadataDumpHandler
"""
)
} }
fun TestConfigurationBuilder.configureFirParser(parser: FirParser) { fun TestConfigurationBuilder.configureFirParser(parser: FirParser) {
@@ -136,7 +136,7 @@ open class FirFrontendFacade(
val compilerConfigurationProvider = testServices.compilerConfigurationProvider val compilerConfigurationProvider = testServices.compilerConfigurationProvider
val configuration = compilerConfigurationProvider.getCompilerConfiguration(mainModule) val configuration = compilerConfigurationProvider.getCompilerConfiguration(mainModule)
val libraryList = initializeLibraryList(mainModule, binaryModuleData, targetPlatform, configuration) val libraryList = initializeLibraryList(mainModule, binaryModuleData, targetPlatform, configuration, testServices)
val moduleInfoProvider = testServices.firModuleInfoProvider val moduleInfoProvider = testServices.firModuleInfoProvider
val moduleDataMap = mutableMapOf<TestModule, FirModuleData>() val moduleDataMap = mutableMapOf<TestModule, FirModuleData>()
@@ -163,38 +163,6 @@ open class FirFrontendFacade(
return moduleDataMap to libraryList.moduleDataProvider return moduleDataMap to libraryList.moduleDataProvider
} }
private fun initializeLibraryList(
mainModule: TestModule,
binaryModuleData: BinaryModuleData,
targetPlatform: TargetPlatform,
configuration: CompilerConfiguration,
): DependencyListForCliModule {
return DependencyListForCliModule.build(binaryModuleData) {
when {
targetPlatform.isCommon() || targetPlatform.isJvm() -> {
dependencies(configuration.jvmModularRoots.map { it.toPath() })
dependencies(configuration.jvmClasspathRoots.map { it.toPath() })
friendDependencies(configuration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList())
}
targetPlatform.isJs() -> {
val runtimeKlibsPaths = JsEnvironmentConfigurator.getRuntimePathsForModule(mainModule, testServices)
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() })
}
targetPlatform.isNative() -> {
val runtimeKlibsPaths = NativeEnvironmentConfigurator.getRuntimePathsForModule(mainModule, testServices)
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")
}
}
}
private fun createLibrarySession( private fun createLibrarySession(
module: TestModule, module: TestModule,
project: Project, project: Project,
@@ -414,4 +382,39 @@ open class FirFrontendFacade(
else -> error("Unsupported") else -> error("Unsupported")
} }
} }
companion object {
fun initializeLibraryList(
mainModule: TestModule,
binaryModuleData: BinaryModuleData,
targetPlatform: TargetPlatform,
configuration: CompilerConfiguration,
testServices: TestServices
): DependencyListForCliModule {
return DependencyListForCliModule.build(binaryModuleData) {
when {
targetPlatform.isCommon() || targetPlatform.isJvm() -> {
dependencies(configuration.jvmModularRoots.map { it.toPath() })
dependencies(configuration.jvmClasspathRoots.map { it.toPath() })
friendDependencies(configuration[JVMConfigurationKeys.FRIEND_PATHS] ?: emptyList())
}
targetPlatform.isJs() -> {
val runtimeKlibsPaths = JsEnvironmentConfigurator.getRuntimePathsForModule(mainModule, testServices)
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() })
}
targetPlatform.isNative() -> {
val runtimeKlibsPaths = NativeEnvironmentConfigurator.getRuntimePathsForModule(mainModule, testServices)
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")
}
}
}
}
} }
@@ -24,6 +24,9 @@ object FrontendKinds {
} }
} }
val FrontendKind<*>.isFir: Boolean
get() = this == FrontendKinds.FIR
object BackendKinds { object BackendKinds {
object ClassicBackend : BackendKind<ClassicBackendInput>("ClassicBackend") object ClassicBackend : BackendKind<ClassicBackendInput>("ClassicBackend")
object IrBackend : BackendKind<IrBackendInput>("IrBackend") object IrBackend : BackendKind<IrBackendInput>("IrBackend")
@@ -72,6 +72,21 @@ abstract class KotlinStandardLibrariesPathProvider : TestService {
*/ */
abstract fun getAnnotationsJar(): File abstract fun getAnnotationsJar(): File
/**
* kotlin-stdlib-js.jar
*/
abstract fun fullJsStdlib(): File
/**
* Jar with minimal version of kotlin stdlib JS (may be same as fullJsStdlib)
*/
abstract fun defaultJsStdlib(): File
/**
* kotlin-test-js.jar
*/
abstract fun kotlinTestJsKLib(): File
fun getRuntimeJarClassLoader(): ClassLoader = synchronized(this) { fun getRuntimeJarClassLoader(): ClassLoader = synchronized(this) {
var loader = runtimeJarClassLoader.get() var loader = runtimeJarClassLoader.get()
if (loader == null) { if (loader == null) {
@@ -108,13 +123,28 @@ object StandardLibrariesPathProviderForKotlinProject : KotlinStandardLibrariesPa
override fun scriptRuntimeJarForTests(): File = ForTestCompileRuntime.scriptRuntimeJarForTests() override fun scriptRuntimeJarForTests(): File = ForTestCompileRuntime.scriptRuntimeJarForTests()
override fun jvmAnnotationsForTests(): File = ForTestCompileRuntime.jvmAnnotationsForTests() override fun jvmAnnotationsForTests(): File = ForTestCompileRuntime.jvmAnnotationsForTests()
override fun getAnnotationsJar(): File = KtTestUtil.getAnnotationsJar() override fun getAnnotationsJar(): File = KtTestUtil.getAnnotationsJar()
override fun fullJsStdlib(): File = extractFromPropertyFirst("kotlin.js.full.stdlib.path") { "kotlin-stdlib-js.jar".dist() }
override fun defaultJsStdlib(): File = extractFromPropertyFirst("kotlin.js.reduced.stdlib.path") { "kotlin-stdlib-js.jar".dist() }
override fun kotlinTestJsKLib(): File = extractFromPropertyFirst("kotlin.js.kotlin.test.path") { "kotlin-test-js.jar".dist() }
private inline fun extractFromPropertyFirst(prop: String, onMissingProperty: () -> String): File {
val path = System.getProperty(prop, null) ?: onMissingProperty()
return File(path)
}
private fun String.dist(): String {
return "dist/kotlinc/lib/$this"
}
} }
object EnvironmentBasedStandardLibrariesPathProvider : KotlinStandardLibrariesPathProvider() { object EnvironmentBasedStandardLibrariesPathProvider : KotlinStandardLibrariesPathProvider() {
const val KOTLIN_STDLIB_PROP = "org.jetbrains.kotlin.test.kotlin-stdlib" const val KOTLIN_STDLIB_PROP = "org.jetbrains.kotlin.test.kotlin-stdlib"
const val KOTLIN_STDLIB_JS_PROP = "org.jetbrains.kotlin.test.kotlin-stdlib-js"
const val KOTLIN_STDLIB_JDK8_PROP = "org.jetbrains.kotlin.test.kotlin-stdlib-jdk8" const val KOTLIN_STDLIB_JDK8_PROP = "org.jetbrains.kotlin.test.kotlin-stdlib-jdk8"
const val KOTLIN_REFLECT_PROP = "org.jetbrains.kotlin.test.kotlin-reflect" const val KOTLIN_REFLECT_PROP = "org.jetbrains.kotlin.test.kotlin-reflect"
const val KOTLIN_TEST_PROP = "org.jetbrains.kotlin.test.kotlin-test" const val KOTLIN_TEST_PROP = "org.jetbrains.kotlin.test.kotlin-test"
const val KOTLIN_TEST_JS_PROP = "org.jetbrains.kotlin.test.kotlin-test-js"
const val KOTLIN_SCRIPT_RUNTIME_PROP = "org.jetbrains.kotlin.test.kotlin-script-runtime" const val KOTLIN_SCRIPT_RUNTIME_PROP = "org.jetbrains.kotlin.test.kotlin-script-runtime"
const val KOTLIN_ANNOTATIONS_JVM_PROP = "org.jetbrains.kotlin.test.kotlin-annotations-jvm" const val KOTLIN_ANNOTATIONS_JVM_PROP = "org.jetbrains.kotlin.test.kotlin-annotations-jvm"
@@ -133,6 +163,9 @@ object EnvironmentBasedStandardLibrariesPathProvider : KotlinStandardLibrariesPa
override fun scriptRuntimeJarForTests(): File = getFile(KOTLIN_SCRIPT_RUNTIME_PROP) override fun scriptRuntimeJarForTests(): File = getFile(KOTLIN_SCRIPT_RUNTIME_PROP)
override fun jvmAnnotationsForTests(): File = getFile(KOTLIN_ANNOTATIONS_JVM_PROP) override fun jvmAnnotationsForTests(): File = getFile(KOTLIN_ANNOTATIONS_JVM_PROP)
override fun getAnnotationsJar(): File = getFile(KOTLIN_ANNOTATIONS_JVM_PROP) override fun getAnnotationsJar(): File = getFile(KOTLIN_ANNOTATIONS_JVM_PROP)
override fun fullJsStdlib(): File = getFile(KOTLIN_STDLIB_JS_PROP)
override fun defaultJsStdlib(): File = getFile(KOTLIN_STDLIB_JS_PROP)
override fun kotlinTestJsKLib(): File = getFile(KOTLIN_TEST_JS_PROP)
} }
val TestServices.standardLibrariesPathProvider: KotlinStandardLibrariesPathProvider by TestServices.testServiceAccessor() val TestServices.standardLibrariesPathProvider: KotlinStandardLibrariesPathProvider by TestServices.testServiceAccessor()
@@ -154,8 +154,13 @@ class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigu
val needsFullIrRuntime = JsEnvironmentConfigurationDirectives.KJS_WITH_FULL_RUNTIME in module.directives || val needsFullIrRuntime = JsEnvironmentConfigurationDirectives.KJS_WITH_FULL_RUNTIME in module.directives ||
ConfigurationDirectives.WITH_STDLIB in module.directives ConfigurationDirectives.WITH_STDLIB in module.directives
val names = if (needsFullIrRuntime) listOf("full.stdlib", "kotlin.test") else listOf("reduced.stdlib") val pathProvider = testServices.standardLibrariesPathProvider
names.mapNotNullTo(result) { System.getProperty("kotlin.js.$it.path")?.let { File(it).absolutePath } } if (needsFullIrRuntime) {
result += pathProvider.fullJsStdlib().absolutePath
result += pathProvider.kotlinTestJsKLib().absolutePath
} else {
result += pathProvider.defaultJsStdlib().absolutePath
}
val runtimeClasspaths = testServices.runtimeClasspathProviders.flatMap { it.runtimeClassPaths(module) } val runtimeClasspaths = testServices.runtimeClasspathProviders.flatMap { it.runtimeClassPaths(module) }
runtimeClasspaths.mapTo(result) { it.absolutePath } runtimeClasspaths.mapTo(result) { it.absolutePath }
return result return result
+1
View File
@@ -37,6 +37,7 @@ dependencies {
testApi(projectTests(":compiler:test-infrastructure-utils")) testApi(projectTests(":compiler:test-infrastructure-utils"))
testApi(projectTests(":compiler:tests-compiler-utils")) testApi(projectTests(":compiler:tests-compiler-utils"))
testApi(projectTests(":compiler:tests-common-new")) testApi(projectTests(":compiler:tests-common-new"))
testImplementation(projectTests(":compiler:fir:analysis-tests"))
testCompileOnly(project(":compiler:frontend")) testCompileOnly(project(":compiler:frontend"))
testCompileOnly(project(":compiler:cli")) testCompileOnly(project(":compiler:cli"))
@@ -230,6 +230,11 @@ fun main(args: Array<String>) {
// testClass<AbstractFirJsSteppingTest> { // testClass<AbstractFirJsSteppingTest> {
// model("debug/stepping") // model("debug/stepping")
// } // }
testClass<AbstractFirLoadK2CompiledJsKotlinTest> {
model("loadJava/compiledKotlin", extension = "kt")
model("loadJava/compiledKotlinWithStdlib", extension = "kt")
}
} }
} }
} }
@@ -0,0 +1,58 @@
/*
* 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.js.test.fir
import org.jetbrains.kotlin.js.test.converters.FirJsKlibBackendFacade
import org.jetbrains.kotlin.js.test.ir.commonConfigurationForJsCodegenTest
import org.jetbrains.kotlin.test.*
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.builders.configureKlibArtifactsHandlersStep
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.FIR_PARSER
import org.jetbrains.kotlin.test.frontend.fir.Fir2IrResultsConverter
import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest
abstract class AbstractFirLoadCompiledJsKotlinTestBase<F : ResultingArtifact.FrontendOutput<F>> :
AbstractKotlinCompilerWithTargetBackendTest(TargetBackend.JS_IR)
{
protected abstract val frontendKind: FrontendKind<F>
protected abstract val frontendFacade: Constructor<FrontendFacade<F>>
protected abstract val frontendToBackendConverter: Constructor<Frontend2BackendConverter<F, IrBackendInput>>
override fun TestConfigurationBuilder.configuration() {
commonConfigurationForJsCodegenTest(
frontendKind,
frontendFacade,
frontendToBackendConverter,
::FirJsKlibBackendFacade,
)
configureKlibArtifactsHandlersStep {
useHandlers(::KlibLoadedMetadataDumpHandler)
}
useAfterAnalysisCheckers(::FirMetadataLoadingTestSuppressor)
}
}
open class AbstractFirLoadK2CompiledJsKotlinTest : AbstractFirLoadCompiledJsKotlinTestBase<FirOutputArtifact>() {
override val frontendKind: FrontendKind<FirOutputArtifact>
get() = FrontendKinds.FIR
override val frontendFacade: Constructor<FrontendFacade<FirOutputArtifact>>
get() = ::FirFrontendFacade
override val frontendToBackendConverter: Constructor<Frontend2BackendConverter<FirOutputArtifact, IrBackendInput>>
get() = ::Fir2IrResultsConverter
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
builder.defaultDirectives {
FIR_PARSER with FirParser.LightTree
}
}
}
@@ -56,15 +56,10 @@ abstract class AbstractJsBlackBoxCodegenTestBase<R : ResultingArtifact.FrontendO
} }
protected fun TestConfigurationBuilder.commonConfigurationForJsBlackBoxCodegenTest() { protected fun TestConfigurationBuilder.commonConfigurationForJsBlackBoxCodegenTest() {
globalDefaults { commonConfigurationForJsCodegenTest(targetFrontend, frontendFacade, frontendToBackendConverter, backendFacade)
frontend = targetFrontend
targetPlatform = JsPlatforms.defaultJsPlatform
dependencyKind = DependencyKind.Binary
}
val pathToRootOutputDir = System.getProperty("kotlin.js.test.root.out.dir") ?: error("'kotlin.js.test.root.out.dir' is not set") val pathToRootOutputDir = System.getProperty("kotlin.js.test.root.out.dir") ?: error("'kotlin.js.test.root.out.dir' is not set")
defaultDirectives { defaultDirectives {
+DiagnosticsDirectives.REPORT_ONLY_EXPLICITLY_DEFINED_DEBUG_INFO
JsEnvironmentConfigurationDirectives.PATH_TO_ROOT_OUTPUT_DIR with pathToRootOutputDir JsEnvironmentConfigurationDirectives.PATH_TO_ROOT_OUTPUT_DIR with pathToRootOutputDir
JsEnvironmentConfigurationDirectives.PATH_TO_TEST_DIR with pathToTestDir JsEnvironmentConfigurationDirectives.PATH_TO_TEST_DIR with pathToTestDir
JsEnvironmentConfigurationDirectives.TEST_GROUP_OUTPUT_DIR_PREFIX with testGroupOutputDirPrefix JsEnvironmentConfigurationDirectives.TEST_GROUP_OUTPUT_DIR_PREFIX with testGroupOutputDirPrefix
@@ -74,6 +69,11 @@ abstract class AbstractJsBlackBoxCodegenTestBase<R : ResultingArtifact.FrontendO
if (getBoolean("kotlin.js.ir.skipRegularMode")) +JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE if (getBoolean("kotlin.js.ir.skipRegularMode")) +JsEnvironmentConfigurationDirectives.SKIP_REGULAR_MODE
} }
useAdditionalSourceProviders(
::JsAdditionalSourceProvider,
::CoroutineHelpersSourceFilesProvider,
)
forTestsNotMatching("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/*") { forTestsNotMatching("compiler/testData/codegen/box/diagnostics/functions/tailRecursion/*") {
defaultDirectives { defaultDirectives {
DIAGNOSTICS with "-warnings" DIAGNOSTICS with "-warnings"
@@ -84,47 +84,20 @@ abstract class AbstractJsBlackBoxCodegenTestBase<R : ResultingArtifact.FrontendO
enableMetaInfoHandler() enableMetaInfoHandler()
} }
useConfigurators( configureIrHandlersStep {
::CommonEnvironmentConfigurator,
::JsEnvironmentConfigurator,
)
useAdditionalSourceProviders(
::JsAdditionalSourceProvider,
::CoroutineHelpersSourceFilesProvider,
)
useAdditionalService(::JsLibraryProvider)
useAfterAnalysisCheckers(
::JsFailingTestSuppressor,
::BlackBoxCodegenSuppressor,
::JsArtifactsDumpHandler
)
facadeStep(frontendFacade)
classicFrontendHandlersStep {
commonClassicFrontendHandlersForCodegenTest()
useHandlers(::ClassicDiagnosticsHandler)
}
firHandlersStep {
useHandlers(::FirDiagnosticsHandler)
}
facadeStep(frontendToBackendConverter)
irHandlersStep {
useHandlers(::IrMangledNameAndSignatureDumpHandler) useHandlers(::IrMangledNameAndSignatureDumpHandler)
} }
facadeStep(backendFacade)
klibArtifactsHandlersStep()
afterBackendFacade?.let { facadeStep(it) } afterBackendFacade?.let { facadeStep(it) }
facadeStep(recompileFacade) facadeStep(recompileFacade)
jsArtifactsHandlersStep { jsArtifactsHandlersStep {
useHandlers(::JsSourceMapPathRewriter) useHandlers(::JsSourceMapPathRewriter)
} }
useAfterAnalysisCheckers(
::JsArtifactsDumpHandler
)
forTestsMatching("compiler/testData/codegen/box/involvesIrInterpreter/*") { forTestsMatching("compiler/testData/codegen/box/involvesIrInterpreter/*") {
enableMetaInfoHandler() enableMetaInfoHandler()
configureKlibArtifactsHandlersStep { configureKlibArtifactsHandlersStep {
@@ -136,3 +109,53 @@ abstract class AbstractJsBlackBoxCodegenTestBase<R : ResultingArtifact.FrontendO
} }
} }
} }
@Suppress("reformat")
fun <
R : ResultingArtifact.FrontendOutput<R>,
I : ResultingArtifact.BackendInput<I>,
A : ResultingArtifact.Binary<A>
> TestConfigurationBuilder.commonConfigurationForJsCodegenTest(
targetFrontend: FrontendKind<R>,
frontendFacade: Constructor<FrontendFacade<R>>,
frontendToBackendConverter: Constructor<Frontend2BackendConverter<R, I>>,
backendFacade: Constructor<BackendFacade<I, A>>,
) {
globalDefaults {
frontend = targetFrontend
targetPlatform = JsPlatforms.defaultJsPlatform
dependencyKind = DependencyKind.Binary
}
defaultDirectives {
+DiagnosticsDirectives.REPORT_ONLY_EXPLICITLY_DEFINED_DEBUG_INFO
}
useConfigurators(
::CommonEnvironmentConfigurator,
::JsEnvironmentConfigurator,
)
useAdditionalService(::JsLibraryProvider)
useAfterAnalysisCheckers(
::JsFailingTestSuppressor,
::BlackBoxCodegenSuppressor,
)
facadeStep(frontendFacade)
classicFrontendHandlersStep {
commonClassicFrontendHandlersForCodegenTest()
useHandlers(::ClassicDiagnosticsHandler)
}
firHandlersStep {
useHandlers(::FirDiagnosticsHandler)
}
facadeStep(frontendToBackendConverter)
irHandlersStep()
facadeStep(backendFacade)
klibArtifactsHandlersStep()
}
@@ -20,11 +20,8 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.test.TargetBackend import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestService import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.jsLibraryProvider
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
@@ -46,14 +43,14 @@ private class TestArtifactCache(val moduleName: String, val binaryAsts: MutableM
} }
class JsIrIncrementalDataProvider(private val testServices: TestServices) : TestService { class JsIrIncrementalDataProvider(private val testServices: TestServices) : TestService {
private val fullRuntimeKlib: String = System.getProperty("kotlin.js.full.stdlib.path") private val fullRuntimeKlib = testServices.standardLibrariesPathProvider.fullJsStdlib()
private val defaultRuntimeKlib = System.getProperty("kotlin.js.reduced.stdlib.path") private val defaultRuntimeKlib = testServices.standardLibrariesPathProvider.defaultJsStdlib()
private val kotlinTestKLib = System.getProperty("kotlin.js.kotlin.test.path") private val kotlinTestKLib = testServices.standardLibrariesPathProvider.kotlinTestJsKLib()
private val predefinedKlibHasIcCache = mutableMapOf<String, TestArtifactCache?>( private val predefinedKlibHasIcCache = mutableMapOf<String, TestArtifactCache?>(
File(fullRuntimeKlib).absolutePath to null, fullRuntimeKlib.absolutePath to null,
File(kotlinTestKLib).absolutePath to null, kotlinTestKLib.absolutePath to null,
File(defaultRuntimeKlib).absolutePath to null defaultRuntimeKlib.absolutePath to null
) )
private val icCache: MutableMap<String, TestArtifactCache> = mutableMapOf() private val icCache: MutableMap<String, TestArtifactCache> = mutableMapOf()