[AA] Add symbol tests for symbols from JS klibs

Use test compiler runner to compile JS libraries. Determine compiler
by the specified TARGET_PLATFORM. Add tests for dynamic type and
JS-specific .proto extensions.

Refactor the code for mock library compilation to make the switch
between platforms more straightforward.

KTIJ-27566
KT-63217
This commit is contained in:
Pavel Kirpichenkov
2023-11-17 20:11:08 +02:00
committed by Space Team
parent 0736cb3fac
commit 0eb1a63a2f
35 changed files with 543 additions and 116 deletions
@@ -17,10 +17,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.test.base.AnalysisApiFirT
import org.jetbrains.kotlin.analysis.low.level.api.fir.test.configurators.createKtLibrarySourceModule
import org.jetbrains.kotlin.analysis.test.framework.project.structure.KtModuleFactory
import org.jetbrains.kotlin.analysis.test.framework.project.structure.TestModuleStructureFactory
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.CompiledLibraryProvider
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.TestModuleCompiler
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.TestModuleCompilerJar
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.compiledLibraryProvider
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.*
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestServiceRegistrar
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind
@@ -44,7 +41,7 @@ object AnalysisApiFirLibrarySourceTestConfigurator : AnalysisApiTestConfigurator
builder.apply {
useAdditionalServices(ServiceRegistrationData(CompiledLibraryProvider::class, ::CompiledLibraryProvider))
useAdditionalService<KtModuleFactory> { KtLibrarySourceModuleFactory() }
useAdditionalService<TestModuleCompiler> { TestModuleCompilerJar() }
useAdditionalService<TestModuleCompiler> { DispatchingTestModuleCompiler() }
useDirectives(SealedClassesInheritorsCaclulatorPreAnalysisHandler.Directives)
usePreAnalysisHandlers(::SealedClassesInheritorsCaclulatorPreAnalysisHandler)
useConfigurators(::JvmEnvironmentConfigurator)
@@ -10,7 +10,12 @@ import com.intellij.mock.MockApplication
import com.intellij.openapi.extensions.LoadingOrder
import com.intellij.psi.ClassFileViewProviderFactory
import com.intellij.psi.FileTypeFileViewProviders
import com.intellij.psi.FileViewProviderFactory
import com.intellij.psi.compiled.ClassFileDecompilers
import com.intellij.psi.impl.compiled.ClassFileStubBuilder
import com.intellij.psi.stubs.BinaryFileStubBuilders
import org.jetbrains.kotlin.analysis.decompiler.konan.K2KotlinNativeMetadataDecompiler
import org.jetbrains.kotlin.analysis.decompiler.konan.KlibMetaFileType
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinBuiltInDecompiler
import org.jetbrains.kotlin.analysis.decompiler.psi.KotlinClassFileDecompiler
import org.jetbrains.kotlin.analysis.test.framework.services.disposableProvider
@@ -20,10 +25,21 @@ import org.jetbrains.kotlin.test.services.TestServices
object AnalysisApiLibraryBaseTestServiceRegistrar : AnalysisApiTestServiceRegistrar() {
override fun registerApplicationServices(application: MockApplication, testServices: TestServices) {
FileTypeFileViewProviders.INSTANCE.addExplicitExtension(JavaClassFileType.INSTANCE, ClassFileViewProviderFactory())
FileTypeFileViewProviders.INSTANCE.addExplicitExtension(
KlibMetaFileType,
FileViewProviderFactory { file, _, manager, _ ->
K2KotlinNativeMetadataDecompiler().createFileViewProvider(file, manager, physical = true)
})
BinaryFileStubBuilders.INSTANCE.addExplicitExtension(KlibMetaFileType, ClassFileStubBuilder())
ClassFileDecompilers.getInstance().EP_NAME.point.apply {
registerExtension(KotlinClassFileDecompiler(), LoadingOrder.FIRST, testServices.disposableProvider.getApplicationDisposable())
registerExtension(KotlinBuiltInDecompiler(), LoadingOrder.FIRST, testServices.disposableProvider.getApplicationDisposable())
registerExtension(
K2KotlinNativeMetadataDecompiler(),
LoadingOrder.FIRST,
testServices.disposableProvider.getApplicationDisposable()
)
}
}
}
@@ -18,7 +18,7 @@ import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModulePr
import org.jetbrains.kotlin.analysis.test.framework.services.ExpressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.services.ExpressionMarkersSourceFilePreprocessor
import org.jetbrains.kotlin.analysis.test.framework.services.expressionMarkerProvider
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.CompilerExecutor
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.TestModuleCompiler
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.FrontendKind
import org.jetbrains.kotlin.analysis.test.framework.utils.SkipTestException
@@ -117,7 +117,7 @@ abstract class AbstractAnalysisApiBasedTest : TestWithDisposable() {
useDirectives(*AbstractKotlinCompilerTest.defaultDirectiveContainers.toTypedArray())
useDirectives(JvmEnvironmentConfigurationDirectives)
useDirectives(CompilerExecutor.Directives)
useDirectives(TestModuleCompiler.Directives)
useSourcePreprocessor(::ExpressionMarkersSourceFilePreprocessor)
@@ -10,6 +10,7 @@ import com.intellij.psi.PsiFile
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory
import org.jetbrains.kotlin.analysis.project.structure.*
import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.jvmModularRoots
@@ -17,7 +18,6 @@ import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices
@@ -71,7 +71,10 @@ abstract class KtModuleByCompilerConfiguration(
private fun createJdkFromConfiguration(): KtSdkModule? = configuration.get(JVMConfigurationKeys.JDK_HOME)?.let { jdkHome ->
val jdkHomePaths = StandaloneProjectFactory.getDefaultJdkModulePaths(project, jdkHome.toPath())
val scope = TestModuleStructureFactory.getScopeForLibraryByRoots(jdkHomePaths, testServices)
val scope = StandaloneProjectFactory.createSearchScopeByLibraryRoots(
jdkHomePaths,
testServices.environmentManager.getProjectEnvironment()
)
KtJdkModuleImpl(
"jdk",
@@ -171,7 +174,10 @@ private class LibraryByRoots(
override val project: Project,
testServices: TestServices,
) : KtLibraryModule {
override val contentScope: GlobalSearchScope = TestModuleStructureFactory.getScopeForLibraryByRoots(roots, testServices)
override val contentScope: GlobalSearchScope = StandaloneProjectFactory.createSearchScopeByLibraryRoots(
roots,
testServices.environmentManager.getProjectEnvironment(),
)
override val libraryName: String get() = "Test Library $roots"
override val directRegularDependencies: List<KtModule> get() = emptyList()
override val directDependsOnDependencies: List<KtModule> get() = emptyList()
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.analysis.test.framework.project.structure
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleProjectStructure
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.KtModuleWithFiles
import org.jetbrains.kotlin.analysis.api.standalone.base.project.structure.StandaloneProjectFactory
@@ -20,7 +19,10 @@ import org.jetbrains.kotlin.analysis.test.framework.services.environmentManager
import org.jetbrains.kotlin.analysis.utils.errors.requireIsInstance
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.isJs
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.test.TestInfrastructureInternals
import org.jetbrains.kotlin.test.model.DependencyRelation
import org.jetbrains.kotlin.test.model.TestModule
@@ -56,13 +58,7 @@ object TestModuleStructureFactory {
}
is KtModuleWithModifiableDependencies -> {
addModuleDependencies(testModule, moduleEntriesByName, ktModule)
buildList {
addIfNotNull(getJdkModule(testModule, project, testServices))
addAll(getStdlibModules(testModule, project, testServices))
addAll(getLibraryModules(testServices, testModule, project))
addAll(createLibrariesByCompilerConfigurators(testModule, testServices, project))
}.forEach { library ->
stdlibAndSdkDependencies(ktModule, testModule, project, testServices).forEach { library ->
val cachedLibrary = binaryModulesBySourceRoots.getOrPut(library.getBinaryRoots().toSet()) { library }
ktModule.directRegularDependencies.add(cachedLibrary)
}
@@ -74,6 +70,28 @@ object TestModuleStructureFactory {
return KtModuleProjectStructure(moduleEntries, binaryModulesBySourceRoots.values)
}
private fun stdlibAndSdkDependencies(
module: KtModule,
testModule: TestModule,
project: Project,
testServices: TestServices,
): List<KtBinaryModule> {
return when {
module.platform.isJvm() -> jvmStdlibAndJdkDependencies(testModule, project, testServices)
module.platform.isJs() -> jsStdlibDependencies(project, testServices)
else -> error("Unsupported platform ${module.platform} in this test")
}
}
private fun jvmStdlibAndJdkDependencies(testModule: TestModule, project: Project, testServices: TestServices): List<KtBinaryModule> {
return buildList {
addIfNotNull(getJdkModule(testModule, project, testServices))
addAll(getStdlibModules(testModule, project, testServices))
addAll(getLibraryModules(testServices, testModule, project))
addAll(createLibrariesByCompilerConfigurators(testModule, testServices, project))
}
}
@OptIn(TestInfrastructureInternals::class)
private fun createLibrariesByCompilerConfigurators(
testModule: TestModule,
@@ -130,7 +148,10 @@ object TestModuleStructureFactory {
return KtLibraryModuleImpl(
libraryName,
JvmPlatforms.defaultJvmPlatform,
getScopeForLibraryByRoots(listOf(jar), testServices),
StandaloneProjectFactory.createSearchScopeByLibraryRoots(
listOf(jar),
testServices.environmentManager.getProjectEnvironment()
),
project,
listOf(jar),
librarySources = null,
@@ -167,16 +188,33 @@ object TestModuleStructureFactory {
return KtJdkModuleImpl(
"jdk",
JvmPlatforms.defaultJvmPlatform,
getScopeForLibraryByRoots(jdkSourceRoots, testServices),
StandaloneProjectFactory.createSearchScopeByLibraryRoots(
jdkSourceRoots,
testServices.environmentManager.getProjectEnvironment()
),
project,
jdkSourceRoots
)
}
fun getScopeForLibraryByRoots(roots: Collection<Path>, testServices: TestServices): GlobalSearchScope {
return StandaloneProjectFactory.createSearchScopeByLibraryRoots(
roots,
testServices.environmentManager.getProjectEnvironment()
private fun jsStdlibDependencies(project: Project, testServices: TestServices): List<KtBinaryModule> {
return listOf(
jsStdlib(project, testServices),
)
}
private fun jsStdlib(project: Project, testServices: TestServices): KtBinaryModule {
val jar = testServices.standardLibrariesPathProvider.fullJsStdlib().toPath()
return KtLibraryModuleImpl(
PathUtil.JS_LIB_NAME,
JvmPlatforms.defaultJvmPlatform,
StandaloneProjectFactory.createSearchScopeByLibraryRoots(
listOf(jar),
testServices.environmentManager.getProjectEnvironment()
),
project,
listOf(jar),
librarySources = null,
)
}
@@ -0,0 +1,143 @@
/*
* 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.analysis.test.framework.services.libraries
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.cliArgument
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.platform.isJs
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.sourceFileProvider
import org.jetbrains.kotlin.test.services.standardLibrariesPathProvider
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.io.ByteArrayInputStream
import java.nio.file.Path
import java.util.jar.Attributes
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import kotlin.io.path.div
import kotlin.io.path.outputStream
abstract class CliTestModuleCompiler : TestModuleCompiler() {
internal abstract val compilerKind: CompilerExecutor.CompilerKind
protected abstract fun buildPlatformCompilerOptions(module: TestModule, testServices: TestServices): List<String>
override fun compile(tmpDir: Path, module: TestModule, testServices: TestServices): Path = CompilerExecutor.compileLibrary(
compilerKind,
tmpDir,
buildCompilerOptions(module, testServices),
compilationErrorExpected = Directives.COMPILATION_ERRORS in module.directives
)
override fun compileTestModuleToLibrarySources(module: TestModule, testServices: TestServices): Path {
val tmpDir = KtTestUtil.tmpDir("testSourcesToCompile").toPath()
val librarySourcesPath = tmpDir / "library-sources.jar"
val manifest = Manifest().apply { mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0" }
JarOutputStream(librarySourcesPath.outputStream(), manifest).use { jarOutputStream ->
for (testFile in module.files) {
val text = testServices.sourceFileProvider.getContentOfSourceFile(testFile)
addFileToJar(testFile.relativePath, text, jarOutputStream)
}
}
return librarySourcesPath
}
private fun buildCompilerOptions(module: TestModule, testServices: TestServices): List<String> = buildList {
addAll(buildCommonCompilerOptions(module))
addAll(buildPlatformCompilerOptions(module, testServices))
}
private fun buildCommonCompilerOptions(module: TestModule): List<String> = buildList {
module.directives[LanguageSettingsDirectives.API_VERSION].firstOrNull()?.let { apiVersion ->
addAll(listOf(CommonCompilerArguments::apiVersion.cliArgument, apiVersion.versionString))
}
module.directives[LanguageSettingsDirectives.LANGUAGE].firstOrNull()?.let {
add("-XXLanguage:$it")
}
if (LanguageSettingsDirectives.ALLOW_KOTLIN_PACKAGE in module.directives) {
add(CommonCompilerArguments::allowKotlinPackage.cliArgument)
}
addAll(module.directives[Directives.COMPILER_ARGUMENTS])
}
private fun addFileToJar(path: String, text: String, jarOutputStream: JarOutputStream) {
jarOutputStream.putNextEntry(JarEntry(path))
ByteArrayInputStream(text.toByteArray()).copyTo(jarOutputStream)
jarOutputStream.closeEntry()
}
}
class JvmJarTestModuleCompiler : CliTestModuleCompiler() {
override val compilerKind = CompilerExecutor.CompilerKind.JVM
override fun buildPlatformCompilerOptions(module: TestModule, testServices: TestServices): List<String> = buildList {
module.directives[JvmEnvironmentConfigurationDirectives.JVM_TARGET].firstOrNull()?.let { jvmTarget ->
addAll(listOf(K2JVMCompilerArguments::jvmTarget.cliArgument, jvmTarget.description))
val jdkHome = when {
jvmTarget <= JvmTarget.JVM_1_8 -> KtTestUtil.getJdk8Home()
jvmTarget <= JvmTarget.JVM_11 -> KtTestUtil.getJdk11Home()
jvmTarget <= JvmTarget.JVM_17 -> KtTestUtil.getJdk17Home()
jvmTarget <= JvmTarget.JVM_21 -> KtTestUtil.getJdk21Home()
else -> error("JDK for $jvmTarget is not found")
}
addAll(listOf(K2JVMCompilerArguments::jdkHome.cliArgument, jdkHome.toString()))
}
}
}
class JsKlibTestModuleCompiler : CliTestModuleCompiler() {
override val compilerKind = CompilerExecutor.CompilerKind.JS
override fun buildPlatformCompilerOptions(module: TestModule, testServices: TestServices): List<String> {
return listOf(
K2JSCompilerArguments::libraries.cliArgument, testServices.standardLibrariesPathProvider.fullJsStdlib().absolutePath,
)
}
}
/**
* [DispatchingTestModuleCompiler] chooses the appropriate compiler for a module based on its platform.
* In case all tests in a suite should compile libraries for a single platform, one of the underlying [TestModuleCompiler]s
* can be registered directly. Once new test compilers are introduced, they should be added to [DispatchingTestModuleCompiler].
*/
class DispatchingTestModuleCompiler : TestModuleCompiler() {
private val compilersByKind = mapOf(
CompilerExecutor.CompilerKind.JVM to JvmJarTestModuleCompiler(),
CompilerExecutor.CompilerKind.JS to JsKlibTestModuleCompiler(),
)
override fun compile(tmpDir: Path, module: TestModule, testServices: TestServices): Path {
return getCompiler(module).compileTestModuleToLibrary(module, testServices)
}
override fun compileTestModuleToLibrarySources(module: TestModule, testServices: TestServices): Path {
return getCompiler(module).compileTestModuleToLibrarySources(module, testServices)
}
private fun getCompiler(module: TestModule): CliTestModuleCompiler {
val compilerKindForModule = when {
module.targetPlatform.isJvm() -> CompilerExecutor.CompilerKind.JVM
module.targetPlatform.isJs() -> CompilerExecutor.CompilerKind.JS
else -> error("DispatchingTestModuleCompiler doesn't support the platform: ${module.targetPlatform}")
}
return compilersByKind[compilerKindForModule]
?: error("TestModuleCompiler is not available for ${compilerKindForModule.name}")
}
}
@@ -6,83 +6,68 @@
package org.jetbrains.kotlin.analysis.test.framework.services.libraries
import org.jetbrains.kotlin.analysis.test.framework.utils.SkipTestException
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.cliArgument
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.cli.common.arguments.cliArgument
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.test.MockLibraryUtil
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.div
import kotlin.io.path.exists
import kotlin.io.path.notExists
import kotlin.io.path.*
object CompilerExecutor {
fun compileLibrary(sourcesPath: Path, options: List<String>, compilationErrorExpected: Boolean): Path {
val library = sourcesPath / "library.jar"
val sourceFiles = sourcesPath.toFile().walkBottomUp()
val commands = buildList {
sourceFiles.mapTo(this) { it.absolutePath }
addAll(options)
add("-d")
add(library.absolutePathString())
add("-XXLanguage:-SkipStandaloneScriptsInSourceRoots")
}
try {
MockLibraryUtil.runJvmCompiler(commands)
internal object CompilerExecutor {
fun compileLibrary(compilerKind: CompilerKind, sourcesPath: Path, options: List<String>, compilationErrorExpected: Boolean): Path {
val library = try {
compile(compilerKind, sourcesPath, options)
} catch (e: Throwable) {
if (!compilationErrorExpected) {
throw IllegalStateException("Unexpected compilation error while compiling library", e)
}
null
}
if (library.exists() && compilationErrorExpected) {
if (library?.exists() == true && compilationErrorExpected) {
error("Compilation error expected but, code was compiled successfully")
}
if (library.notExists()) {
if (library == null || library.notExists()) {
throw LibraryWasNotCompiledDueToExpectedCompilationError()
}
return library
}
fun parseCompilerOptionsFromTestdata(module: TestModule): List<String> = buildList {
module.directives[LanguageSettingsDirectives.API_VERSION].firstOrNull()?.let { apiVersion ->
addAll(listOf(CommonCompilerArguments::apiVersion.cliArgument, apiVersion.versionString))
private fun compile(compilerKind: CompilerKind, sourcesPath: Path, options: List<String>): Path {
val sourceFiles = sourcesPath.toFile().walkBottomUp()
val library = when (compilerKind) {
CompilerKind.JVM -> sourcesPath / "library.jar"
CompilerKind.JS -> sourcesPath / "library.klib"
}
module.directives[LanguageSettingsDirectives.LANGUAGE].firstOrNull()?.let {
add("-XXLanguage:$it")
}
if (LanguageSettingsDirectives.ALLOW_KOTLIN_PACKAGE in module.directives) {
add(CommonCompilerArguments::allowKotlinPackage.cliArgument)
}
module.directives[JvmEnvironmentConfigurationDirectives.JVM_TARGET].firstOrNull()?.let { jvmTarget ->
addAll(listOf(K2JVMCompilerArguments::jvmTarget.cliArgument, jvmTarget.description))
val jdkHome = when {
jvmTarget <= JvmTarget.JVM_1_8 -> KtTestUtil.getJdk8Home()
jvmTarget <= JvmTarget.JVM_11 -> KtTestUtil.getJdk11Home()
jvmTarget <= JvmTarget.JVM_17 -> KtTestUtil.getJdk17Home()
jvmTarget <= JvmTarget.JVM_21 -> KtTestUtil.getJdk21Home()
else -> error("JDK for $jvmTarget is not found")
when (compilerKind) {
CompilerKind.JVM -> {
val commands = buildList {
sourceFiles.mapTo(this) { it.absolutePath }
addAll(options)
add(K2JVMCompilerArguments::destination.cliArgument); add(library.absolutePathString())
add("-XXLanguage:-${LanguageFeature.SkipStandaloneScriptsInSourceRoots.name}")
}
MockLibraryUtil.runJvmCompiler(commands)
}
CompilerKind.JS -> {
val commands = buildList {
add(K2JSCompilerArguments::metaInfo.cliArgument)
add(K2JSCompilerArguments::moduleName.cliArgument); add("library")
add(K2JSCompilerArguments::outputDir.cliArgument); add(library.parent.absolutePathString())
add(K2JSCompilerArguments::irProduceKlibFile.cliArgument)
sourceFiles.mapTo(this) { it.absolutePath }
addAll(options)
}
MockLibraryUtil.runJsCompiler(commands)
}
addAll(listOf(K2JVMCompilerArguments::jdkHome.cliArgument, jdkHome.toString()))
}
addAll(module.directives[Directives.COMPILER_ARGUMENTS])
return library
}
object Directives : SimpleDirectivesContainer() {
val COMPILER_ARGUMENTS by stringDirective("List of additional compiler arguments")
val COMPILATION_ERRORS by directive("Is compilation errors expected in the file")
enum class CompilerKind {
JVM, JS
}
}
@@ -5,20 +5,15 @@
package org.jetbrains.kotlin.analysis.test.framework.services.libraries
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestService
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.sourceFileProvider
import org.jetbrains.kotlin.test.util.KtTestUtil
import java.io.ByteArrayInputStream
import java.nio.file.Path
import java.util.jar.Attributes
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream
import java.util.jar.Manifest
import kotlin.io.path.createFile
import kotlin.io.path.div
import kotlin.io.path.outputStream
import kotlin.io.path.writeText
abstract class TestModuleCompiler : TestService {
@@ -29,37 +24,15 @@ abstract class TestModuleCompiler : TestService {
val tmpSourceFile = (tmpDir / testFile.name).createFile()
tmpSourceFile.writeText(text)
}
return compile(tmpDir, module)
return compile(tmpDir, module, testServices)
}
abstract fun compile(tmpDir: Path, module: TestModule): Path
abstract fun compile(tmpDir: Path, module: TestModule, testServices: TestServices): Path
abstract fun compileTestModuleToLibrarySources(module: TestModule, testServices: TestServices): Path?
}
class TestModuleCompilerJar : TestModuleCompiler() {
override fun compile(tmpDir: Path, module: TestModule): Path = CompilerExecutor.compileLibrary(
tmpDir,
CompilerExecutor.parseCompilerOptionsFromTestdata(module),
compilationErrorExpected = CompilerExecutor.Directives.COMPILATION_ERRORS in module.directives
)
override fun compileTestModuleToLibrarySources(module: TestModule, testServices: TestServices): Path {
fun addFileToJar(path: String, text: String, jarOutputStream: JarOutputStream) {
jarOutputStream.putNextEntry(JarEntry(path))
ByteArrayInputStream(text.toByteArray()).copyTo(jarOutputStream)
jarOutputStream.closeEntry()
}
val tmpDir = KtTestUtil.tmpDir("testSourcesToCompile").toPath()
val librarySourcesPath = tmpDir / "library-sources.jar"
val manifest = Manifest().apply { mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0" }
JarOutputStream(librarySourcesPath.outputStream(), manifest).use { jarOutputStream ->
for (testFile in module.files) {
val text = testServices.sourceFileProvider.getContentOfSourceFile(testFile)
addFileToJar(testFile.relativePath, text, jarOutputStream)
}
}
return librarySourcesPath
object Directives : SimpleDirectivesContainer() {
val COMPILER_ARGUMENTS by stringDirective("List of additional compiler arguments")
val COMPILATION_ERRORS by directive("Is compilation errors expected in the file")
}
}
@@ -0,0 +1,6 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtClass
@Y
class Cls
annotation class Y
@@ -0,0 +1,12 @@
KT element: KtClass
KT element text:
@Y public final class Cls public constructor() {
}
FIR element: FirRegularClassImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
@R|Y|() public final [ResolvedTo(BODY_RESOLVE)] class Cls : R|kotlin/Any| {
public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Cls] constructor(): R|Cls|
}
@@ -0,0 +1,5 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtClass
class Cls @Y constructor()
annotation class Y
@@ -0,0 +1,12 @@
KT element: KtClass
KT element text:
public final class Cls @Y public constructor() {
}
FIR element: FirRegularClassImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
public final [ResolvedTo(BODY_RESOLVE)] class Cls : R|kotlin/Any| {
@R|Y|() public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Cls] constructor(): R|Cls|
}
@@ -0,0 +1,3 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
public inline fun Any?.asDynamic(): dynamic = this
@@ -0,0 +1,8 @@
KT element: KtNamedFunction
KT element text:
public inline fun kotlin.Any?.asDynamic(): dynamic /* platform type */ { /* compiled code */ }
FIR element: FirSimpleFunctionImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
public final inline [ResolvedTo(BODY_RESOLVE)] fun R|kotlin/Any?|.asDynamic(): R|dynamic|
@@ -0,0 +1,7 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtClass
enum class Enum {
@Y ENTRY
}
annotation class Y
@@ -0,0 +1,23 @@
KT element: KtClass
KT element text:
public final enum class Enum private constructor() : kotlin.Enum<Enum> {
@Y ENTRY;
}
FIR element: FirRegularClassImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
public final [ResolvedTo(BODY_RESOLVE)] enum class Enum : R|kotlin/Enum<Enum>| {
public final static [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Enum] fun valueOf([ResolvedTo(BODY_RESOLVE)] value: R|kotlin/String|): R|Enum| {
}
public final static [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Enum] fun values(): R|kotlin/Array<Enum>| {
}
public final static [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Enum] val entries: R|kotlin/enums/EnumEntries<Enum>|
public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/enums/EnumEntries<Enum>|
private [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Enum] constructor(): R|Enum|
public final static [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Enum] enum entry ENTRY: R|Enum|
}
@@ -0,0 +1,5 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
@file:JsModule("extModule")
package ext.jspackage.name
external fun foo()
@@ -0,0 +1,8 @@
KT element: KtNamedFunction
KT element text:
public external fun foo(): kotlin.Unit { /* compiled code */ }
FIR element: FirSimpleFunctionImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
public final external [ResolvedTo(BODY_RESOLVE)] fun foo(): R|kotlin/Unit|
@@ -0,0 +1,7 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
@Y
fun fn() {
}
annotation class Y
@@ -0,0 +1,8 @@
KT element: KtNamedFunction
KT element text:
@Y public fun fn(): kotlin.Unit { /* compiled code */ }
FIR element: FirSimpleFunctionImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
@R|Y|() public final [ResolvedTo(BODY_RESOLVE)] fun fn(): R|kotlin/Unit|
@@ -0,0 +1,7 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtClass
@JsModule("jquery")
@JsNonModule
@JsName("$")
external abstract class JQuery() {
}
@@ -0,0 +1,12 @@
KT element: KtClass
KT element text:
@kotlin.js.JsModule @kotlin.js.JsNonModule @kotlin.js.JsName public abstract external class JQuery public constructor() {
}
FIR element: FirRegularClassImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
@R|kotlin/js/JsModule|(import = String(jquery)) @R|kotlin/js/JsNonModule|() @R|kotlin/js/JsName|(name = String($)) public abstract external [ResolvedTo(BODY_RESOLVE)] class JQuery : R|kotlin/Any| {
public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=JQuery] constructor(): R|JQuery|
}
@@ -0,0 +1,6 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtFunction
fun fn(@Y p: String) {
}
annotation class Y
@@ -0,0 +1,8 @@
KT element: KtNamedFunction
KT element text:
public fun fn(@Y p: kotlin.String): kotlin.Unit { /* compiled code */ }
FIR element: FirSimpleFunctionImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
public final [ResolvedTo(BODY_RESOLVE)] fun fn([ResolvedTo(BODY_RESOLVE)] @R|Y|() p: R|kotlin/String|): R|kotlin/Unit|
@@ -0,0 +1,8 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
@Y
var prop: Int = 0
@Y get() = field
@Y set(value) { field = value }
annotation class Y
@@ -0,0 +1,12 @@
KT element: KtProperty
KT element text:
@Y public var prop: kotlin.Int /* compiled code */
public final @Y get
public final @Y set(value: kotlin.Int) {/* compiled code */ }
FIR element: FirPropertyImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
@R|Y|() public final [ResolvedTo(BODY_RESOLVE)] var prop: R|kotlin/Int|
@R|Y|() public [ResolvedTo(BODY_RESOLVE)] get(): R|kotlin/Int|
@R|Y|() public [ResolvedTo(BODY_RESOLVE)] set([ResolvedTo(BODY_RESOLVE)] value: R|kotlin/Int|): R|kotlin/Unit|
@@ -0,0 +1,6 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtProperty
val prop: @Y Int = 1
@Target(AnnotationTarget.TYPE)
annotation class Y
@@ -0,0 +1,8 @@
KT element: KtProperty
KT element text:
public val prop: @Y kotlin.Int = COMPILED_CODE /* compiled code */
FIR element: FirPropertyImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
public final [ResolvedTo(BODY_RESOLVE)] val prop: R|@R|Y|() kotlin/Int|
@@ -0,0 +1,6 @@
// DECLARATION_TYPE: org.jetbrains.kotlin.psi.KtClass
class Cls<@Y T>
@Target(AnnotationTarget.TYPE_PARAMETER)
annotation class Y
@@ -0,0 +1,12 @@
KT element: KtClass
KT element text:
public final class Cls<@Y T> public constructor() {
}
FIR element: FirRegularClassImpl
FIR source kind: KtRealSourceElementKind
FIR element rendered:
public final [ResolvedTo(BODY_RESOLVE)] class Cls<@R|Y|() [ResolvedTo(BODY_RESOLVE)] T> : R|kotlin/Any| {
public [ResolvedTo(BODY_RESOLVE)] [ContainingClassKey=Cls] constructor<@R|Y|() [ResolvedTo(BODY_RESOLVE)] T>(): R|Cls<T>|
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.low.level.api.fir.util.FirDeclarationForCom
import org.jetbrains.kotlin.analysis.project.structure.ProjectStructureProvider
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.CompiledLibraryProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
@@ -23,6 +24,9 @@ import org.jetbrains.kotlin.test.services.service
abstract class AbstractLibraryGetOrBuildFirTest : AbstractLowLevelApiSingleFileTest() {
override val configurator = AnalysisApiFirLibraryBinaryTestConfigurator
override fun configureTest(builder: TestConfigurationBuilder) {
builder.forTestsMatching("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/*") {
this.defaultsProviderBuilder.targetPlatform = JsPlatforms.defaultJsPlatform
}
super.configureTest(builder)
with(builder) {
useDirectives(Directives)
@@ -84,6 +84,82 @@ public class LibraryGetOrBuildFirTestGenerated extends AbstractLibraryGetOrBuild
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/typeParameter.kt");
}
@Nested
@TestMetadata("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js")
@TestDataPath("$PROJECT_ROOT")
public class Js {
@Test
public void testAllFilesPresentInJs() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("classAnnotation.kt")
public void testClassAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/classAnnotation.kt");
}
@Test
@TestMetadata("constructorAnnotation.kt")
public void testConstructorAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/constructorAnnotation.kt");
}
@Test
@TestMetadata("dynamic.kt")
public void testDynamic() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/dynamic.kt");
}
@Test
@TestMetadata("enumAnnotation.kt")
public void testEnumAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/enumAnnotation.kt");
}
@Test
@TestMetadata("fileJsModule.kt")
public void testFileJsModule() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/fileJsModule.kt");
}
@Test
@TestMetadata("functionAnnotation.kt")
public void testFunctionAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/functionAnnotation.kt");
}
@Test
@TestMetadata("jQueryExample.kt")
public void testJQueryExample() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/jQueryExample.kt");
}
@Test
@TestMetadata("parameterAnnotation.kt")
public void testParameterAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/parameterAnnotation.kt");
}
@Test
@TestMetadata("propertyAnnotation.kt")
public void testPropertyAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/propertyAnnotation.kt");
}
@Test
@TestMetadata("typeAnnotation.kt")
public void testTypeAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/typeAnnotation.kt");
}
@Test
@TestMetadata("typeParameterAnnotation.kt")
public void testTypeParameterAnnotation() throws Exception {
runTest("analysis/low-level-api-fir/testData/getOrBuildFirBinary/js/typeParameterAnnotation.kt");
}
}
@Nested
@TestMetadata("analysis/low-level-api-fir/testData/getOrBuildFirBinary/publishedApi")
@TestDataPath("$PROJECT_ROOT")
@@ -34,7 +34,7 @@ object AnalysisApiFirLibraryBinaryTestConfigurator : AnalysisApiTestConfigurator
override fun configureTest(builder: TestConfigurationBuilder, disposable: Disposable) {
builder.apply {
useAdditionalService<KtModuleFactory> { KtLibraryBinaryModuleFactory() }
useAdditionalService<TestModuleCompiler> { TestModuleCompilerJar() }
useAdditionalService<TestModuleCompiler> { DispatchingTestModuleCompiler() }
useAdditionalService<TestModuleDecompiler> { TestModuleDecompilerJar() }
}
}
@@ -11,7 +11,7 @@ import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analysis.test.framework.base.AbstractAnalysisApiBasedTest
import org.jetbrains.kotlin.analysis.test.framework.project.structure.ktModuleProvider
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.CompiledLibraryProvider
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.CompilerExecutor
import org.jetbrains.kotlin.analysis.test.framework.services.libraries.TestModuleCompiler
import org.jetbrains.kotlin.analysis.test.framework.test.configurators.AnalysisApiTestConfigurator
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.asJava.toLightClass
@@ -51,7 +51,7 @@ abstract class AbstractSymbolLightClassesTestBase(
super.configureTest(builder)
with(builder) {
useAdditionalServices(service(::CompiledLibraryProvider))
useDirectives(Directives, CompilerExecutor.Directives)
useDirectives(Directives, TestModuleCompiler.Directives)
useAdditionalSourceProviders(::NullabilityAnnotationSourceProvider)
defaultDirectives {
+ConfigurationDirectives.WITH_STDLIB
@@ -67,7 +67,7 @@ abstract class AbstractSymbolLightClassesTestBase(
}
open fun doTestByFileStructure(ktFiles: List<KtFile>, module: TestModule, testServices: TestServices) {
if (isTestAgainstCompiledCode && CompilerExecutor.Directives.COMPILATION_ERRORS in module.directives) {
if (isTestAgainstCompiledCode && TestModuleCompiler.Directives.COMPILATION_ERRORS in module.directives) {
return
}
@@ -163,7 +163,7 @@ object MockLibraryUtil {
runCompiler(compiler2JVMClass, args)
}
private fun runJsCompiler(args: List<String>) {
fun runJsCompiler(args: List<String>) {
runCompiler(compiler2JSClass, args)
}