[TEST] Add implementation of new infrastructure services for compiler tests

All of new classes lays in lays in :compiler:tests-common-new module
  which includes classes for FE 1.0 and FIR diagnostics tests and
  JVM black boxtests
This commit is contained in:
Dmitriy Novozhilov
2020-12-02 17:22:12 +03:00
parent dd402b16d9
commit cb5183ab4d
80 changed files with 4153 additions and 19 deletions
@@ -458,6 +458,20 @@ class KotlinCoreEnvironment private constructor(
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
}
@TestOnly
@JvmStatic
fun createForTests(
projectEnvironment: ProjectEnvironment, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
): KotlinCoreEnvironment {
return KotlinCoreEnvironment(projectEnvironment, initialConfiguration, extensionConfigs)
}
@TestOnly
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
return ProjectEnvironment(parentDisposable, appEnv)
}
// used in the daemon for jar cache cleanup
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
@@ -456,6 +456,20 @@ class KotlinCoreEnvironment private constructor(
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
}
@TestOnly
@JvmStatic
fun createForTests(
projectEnvironment: ProjectEnvironment, initialConfiguration: CompilerConfiguration, extensionConfigs: EnvironmentConfigFiles
): KotlinCoreEnvironment {
return KotlinCoreEnvironment(projectEnvironment, initialConfiguration, extensionConfigs)
}
@TestOnly
fun createProjectEnvironmentForTests(parentDisposable: Disposable, configuration: CompilerConfiguration): ProjectEnvironment {
val appEnv = createApplicationEnvironment(parentDisposable, configuration, unitTestMode = true)
return ProjectEnvironment(parentDisposable, appEnv)
}
// used in the daemon for jar cache cleanup
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
@@ -18,7 +18,7 @@ abstract class JvmPlatform : SimplePlatform("JVM") {
@Suppress("DEPRECATION_ERROR")
object JvmPlatforms {
private val UNSPECIFIED_SIMPLE_JVM_PLATFORM = JdkPlatform(JvmTarget.JVM_1_6)
private val UNSPECIFIED_SIMPLE_JVM_PLATFORM = JdkPlatform(JvmTarget.DEFAULT)
private val jvmTargetToJdkPlatform: Map<JvmTarget, TargetPlatform> =
JvmTarget.values().map { it to JdkPlatform(it).toTargetPlatform() }.toMap()
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
class FirAnalyzerFacade(val session: FirSession, val languageVersionSettings: LanguageVersionSettings, val ktFiles: List<KtFile>) {
class FirAnalyzerFacade(val session: FirSession, val languageVersionSettings: LanguageVersionSettings, val ktFiles: Collection<KtFile>) {
private var firFiles: List<FirFile>? = null
private var scopeSession: ScopeSession? = null
private var collectedDiagnostics: Map<FirFile, List<FirDiagnostic<*>>>? = null
@@ -16,7 +16,10 @@ class FirJvmModuleInfo(override val name: Name, val dependencies: List<ModuleInf
companion object {
val LIBRARIES_MODULE_NAME = Name.special("<dependencies>")
fun createForLibraries(): FirJvmModuleInfo = FirJvmModuleInfo(LIBRARIES_MODULE_NAME, emptyList())
fun createForLibraries(mainModuleName: String? = null): FirJvmModuleInfo {
val name = mainModuleName?.let { Name.special("<dependencies of $it>") } ?: LIBRARIES_MODULE_NAME
return FirJvmModuleInfo(name, emptyList())
}
}
constructor(moduleName: String, dependencies: List<ModuleInfo>) : this(Name.identifier(moduleName), dependencies)
@@ -149,7 +149,7 @@ object CheckerTestUtil {
return diagnostics
}
private fun getDebugInfoDiagnostics(
fun getDebugInfoDiagnostics(
root: PsiElement,
bindingContext: BindingContext,
markDynamicCalls: Boolean,
@@ -36,4 +36,8 @@ public abstract class DiagnosticFactoryWithPsiElement<E extends PsiElement, D ex
protected boolean isValid(ParametrizedDiagnostic<E> diagnostic) {
return positioningStrategy.isValid(diagnostic.getPsiElement());
}
public PositioningStrategy<? super E> getPositioningStrategy() {
return positioningStrategy;
}
}
@@ -124,7 +124,7 @@ class JvmIrCodegenFactory(private val phaseConfig: PhaseConfig) : CodegenFactory
)
}
internal fun doGenerateFilesInternal(
fun doGenerateFilesInternal(
state: GenerationState,
irModuleFragment: IrModuleFragment,
symbolTable: SymbolTable,
@@ -0,0 +1,142 @@
/*
* 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.util
import java.util.*
fun interface Condition<in T> {
operator fun invoke(value: T): Boolean
}
object Conditions {
private val TRUE = Condition<Any?> { true }
private val FALSE = Condition<Any?> { false }
fun <T> alwaysTrue(): Condition<T> {
return TRUE
}
fun <T> alwaysFalse(): Condition<T> {
return FALSE
}
fun <T> notNull(): Condition<T> {
return Condition { it != null }
}
fun <T> constant(value: Boolean): Condition<T> {
return if (value) alwaysTrue() else alwaysFalse()
}
fun <T> instanceOf(clazz: Class<*>): Condition<T> {
return Condition { t -> clazz.isInstance(t) }
}
fun <T> notInstanceOf(clazz: Class<*>): Condition<T> {
return Condition { t -> !clazz.isInstance(t) }
}
fun assignableTo(clazz: Class<*>): Condition<Class<*>> {
return Condition { t -> clazz.isAssignableFrom(t) }
}
fun <T> instanceOf(vararg clazz: Class<*>): Condition<T> {
return Condition { t ->
for (aClass in clazz) {
if (aClass.isInstance(t)) return@Condition true
}
false
}
}
fun <T> `is`(option: T): Condition<T> {
return equalTo(option)
}
fun <T> equalTo(option: Any?): Condition<T> {
return Condition { t -> t == option }
}
fun <T> notEqualTo(option: Any?): Condition<T> {
return Condition { t -> t != option }
}
fun <T> oneOf(vararg options: T): Condition<T> {
return oneOf(options.toList())
}
fun <T> oneOf(options: Collection<T>): Condition<T> {
return Condition { t -> options.contains(t) }
}
fun <T> not(c: Condition<T>): Condition<T> {
if (c === alwaysTrue<Any>()) return alwaysFalse()
if (c === alwaysFalse<Any>()) return alwaysTrue()
return if (c is Not<*>) {
(c as Not<T>).c as Condition<T>
} else Not(c)
}
fun <T> and(c1: Condition<T>, c2: Condition<T>): Condition<T> {
if (c1 === alwaysTrue<Any>() || c2 === alwaysFalse<Any>()) return c2
return if (c2 === alwaysTrue<Any>() || c1 === alwaysFalse<Any>()) c1 else And(c1, c2)
}
fun <T> or(c1: Condition<T>, c2: Condition<T>): Condition<T> {
if (c1 === alwaysFalse<Any>() || c2 === alwaysTrue<Any>()) return c2
return if (c2 === alwaysFalse<Any>() || c1 === alwaysTrue<Any>()) c1 else Or(c1, c2)
}
fun <A, B> compose(func: (A) -> B, condition: Condition<B>): Condition<A> {
return Condition { condition.invoke(func(it)) }
}
fun <T> cached(c: Condition<T>): Condition<T> {
return SoftRefCache(c)
}
private class Not<T>(val c: Condition<T>) : Condition<T> {
override fun invoke(value: T): Boolean {
return !c.invoke(value)
}
}
private class And<T>(val c1: Condition<T>, val c2: Condition<T>) : Condition<T> {
override fun invoke(value: T): Boolean {
return c1.invoke(value) && c2.invoke(value)
}
}
private class Or<T>(val c1: Condition<T>, val c2: Condition<T>) : Condition<T> {
override fun invoke(value: T): Boolean {
return c1.invoke(value) || c2.invoke(value)
}
}
private class SoftRefCache<T>(private val myCondition: Condition<T>) : Condition<T> {
private val myCache: WeakHashMap<T, Boolean> = WeakHashMap();
override fun invoke(value: T): Boolean {
return myCache.computeIfAbsent(value) { myCondition(value) }
}
}
}
infix fun <T> Condition<T>.and(other: Condition<T>): Condition<T> {
return Conditions.and(this, other)
}
infix fun <T> Condition<T>.or(other: Condition<T>): Condition<T> {
return Conditions.or(this, other)
}
operator fun <T> Condition<T>.not(): Condition<T> {
return Conditions.not(this)
}
fun <T> Condition<T>.cached(): Condition<T> {
return Conditions.cached(this)
}
@@ -7,3 +7,15 @@ package org.jetbrains.kotlin.test.util
fun Iterable<*>.joinToArrayString(): String = joinToString(separator = ", ", prefix = "[", postfix = "]")
fun Array<*>.joinToArrayString(): String = joinToString(separator = ", ", prefix = "[", postfix = "]")
private const val DEFAULT_LINE_SEPARATOR = "\n"
fun String.trimTrailingWhitespacesAndAddNewlineAtEOF(): String =
this.trimTrailingWhitespaces().let { result -> if (result.endsWith("\n")) result else result + "\n" }
fun String.trimTrailingWhitespaces(): String =
this.split('\n').joinToString(separator = "\n") { it.trimEnd() }
fun String.convertLineSeparators(separator: String = DEFAULT_LINE_SEPARATOR): String {
return replace(Regex.fromLiteral("\r\n|\r|\n"), separator)
}
@@ -0,0 +1,66 @@
import org.jetbrains.kotlin.ideaExt.idea
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
testApi(project(":compiler:fir:entrypoint"))
testApi(project(":compiler:cli"))
testImplementation(project(":compiler:ir.tree.impl"))
testImplementation(intellijCoreDep()) { includeJars("intellij-core") }
testCompileOnly(project(":kotlin-reflect-api"))
testRuntimeOnly(project(":kotlin-reflect"))
testRuntimeOnly(project(":core:descriptors.runtime"))
testImplementation(projectTests(":generators:test-generator"))
testImplementation(platform("org.junit:junit-bom:5.7.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("org.junit.platform:junit-platform-commons:1.7.0")
testApi(projectTests(":compiler:test-infrastructure"))
testImplementation(projectTests(":compiler:test-infrastructure-utils"))
testImplementation(intellijDep()) {
// This dependency is needed only for FileComparisonFailure
includeJars("idea_rt", rootProject = rootProject)
isTransitive = false
}
// This is needed only for using FileComparisonFailure, which relies on JUnit 3 classes
testRuntimeOnly(commonDep("junit:junit"))
testRuntimeOnly(intellijDep()) {
includeJars("jna", rootProject = rootProject)
}
testRuntimeOnly(toolsJar())
}
val generationRoot = projectDir.resolve("tests-gen")
sourceSets {
"main" { none() }
"test" {
projectDefault()
this.java.srcDir(generationRoot.name)
}
}
if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
apply(plugin = "idea")
idea {
this.module.generatedSourceDirs.add(generationRoot)
}
}
projectTest(parallel = true) {
dependsOn(":dist")
workingDir = rootDir
jvmArgs!!.removeIf { it.contains("-Xmx") }
maxHeapSize = "3g"
useJUnitPlatform()
}
testsJar()
@@ -0,0 +1,68 @@
/*
* 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.backend
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.directives.CodegenTestDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.test.util.joinToArrayString
class BlackBoxCodegenSuppressor(testServices: TestServices) : AfterAnalysisChecker(testServices) {
override val directives: List<DirectivesContainer>
get() = listOf(CodegenTestDirectives)
override fun suppressIfNeeded(failedAssertions: List<AssertionError>): List<AssertionError> {
val moduleStructure = testServices.moduleStructure
val ignoredBackends = moduleStructure.modules.flatMap { it.directives[CodegenTestDirectives.IGNORE_BACKEND] }
if (ignoredBackends.isEmpty()) return failedAssertions
val targetBackends = moduleStructure.modules.flatMap { it.targetBackends }
val matchedBackend = ignoredBackends.intersect(targetBackends)
if (ignoredBackends.contains(TargetBackend.ANY)) {
return processAssertions(failedAssertions)
}
if (matchedBackend.isNotEmpty()) {
return processAssertions(failedAssertions, "for ${matchedBackend.joinToArrayString()}")
}
return failedAssertions
}
private fun processAssertions(failedAssertions: List<AssertionError>, additionalMessage: String = ""): List<AssertionError> {
return if (failedAssertions.isNotEmpty()) emptyList()
else {
val message = buildString {
append("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive")
if (additionalMessage.isNotEmpty()) {
append(" ")
append(additionalMessage)
}
}
listOf(AssertionError(message))
}
}
private val TestModule.targetBackends: List<TargetBackend>
get() = when (backendKind) {
BackendKinds.ClassicBackend -> when {
targetPlatform.isJvm() -> listOf(TargetBackend.JVM, TargetBackend.JVM_OLD)
targetPlatform.isJs() -> listOf(TargetBackend.JS)
else -> emptyList()
}
BackendKinds.IrBackend -> when {
targetPlatform.isJvm() -> listOf(TargetBackend.JVM_IR)
targetPlatform.isJs() -> listOf(TargetBackend.JS_IR)
else -> emptyList()
}
else -> emptyList()
}
}
@@ -0,0 +1,14 @@
/*
* 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.backend.classic
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.TestServices
abstract class ClassicBackendFacade<A : ResultingArtifact.Binary<A>>(
testServices: TestServices,
binaryKind: BinaryKind<A>
) : BackendFacade<ClassicBackendInput, A>(testServices, BackendKinds.ClassicBackend, binaryKind)
@@ -0,0 +1,26 @@
/*
* 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.backend.classic
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact
// Old backend (JVM and JS)
data class ClassicBackendInput(
val psiFiles: Collection<KtFile>,
val bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor,
val project: Project,
val languageVersionSettings: LanguageVersionSettings
) : ResultingArtifact.BackendInput<ClassicBackendInput>() {
override val kind: BackendKinds.ClassicBackend
get() = BackendKinds.ClassicBackend
}
@@ -0,0 +1,41 @@
/*
* 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.backend.classic
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.DefaultCodegenFactory
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
class ClassicJvmBackendFacade(
testServices: TestServices
) : ClassicBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
override fun transform(
module: TestModule,
inputArtifact: ClassicBackendInput
): BinaryArtifacts.Jvm {
val compilerConfiguration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val (psiFiles, bindingContext, moduleDescriptor, project, languageVersionSettings) = inputArtifact
// TODO: add configuring classBuilderFactory
val generationState = GenerationState.Builder(
project,
ClassBuilderFactories.TEST,
moduleDescriptor,
bindingContext,
psiFiles.toList(),
compilerConfiguration
).codegenFactory(DefaultCodegenFactory).build()
KotlinCodegenFacade.compileCorrectFiles(generationState)
return BinaryArtifacts.Jvm(generationState.factory)
}
}
@@ -0,0 +1,23 @@
/*
* 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.backend.handlers
import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.BinaryArtifactHandler
import org.jetbrains.kotlin.test.model.BinaryArtifacts
import org.jetbrains.kotlin.test.services.TestServices
abstract class JvmBinaryArtifactHandler(
testServices: TestServices
) : BinaryArtifactHandler<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm)
abstract class JsBinaryArtifactHandler(
testServices: TestServices
) : BinaryArtifactHandler<BinaryArtifacts.Js>(testServices, ArtifactKinds.Js)
abstract class NativeBinaryArtifactHandler(
testServices: TestServices
) : BinaryArtifactHandler<BinaryArtifacts.Native>(testServices, ArtifactKinds.Native)
@@ -0,0 +1,109 @@
/*
* 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.backend.handlers
import junit.framework.TestCase
import org.jetbrains.kotlin.backend.common.CodegenUtil.getMemberDeclarationsToGenerate
import org.jetbrains.kotlin.cli.jvm.config.jvmClasspathRoots
import org.jetbrains.kotlin.codegen.ClassFileFactory
import org.jetbrains.kotlin.codegen.GeneratedClassLoader
import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil.getFileClassInfoNoResolve
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.BinaryArtifacts
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.utils.addToStdlib.runIf
import java.lang.reflect.Method
import java.net.URLClassLoader
class JvmBoxRunner(
testServices: TestServices
) : JvmBinaryArtifactHandler(testServices) {
companion object {
private val BOX_IN_SEPARATE_PROCESS_PORT = System.getProperty("kotlin.test.box.in.separate.process.port")
}
private var boxMethodFound = false
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (!boxMethodFound) {
assertions.fail { "Can't find box methods" }
}
}
override fun processModule(module: TestModule, info: BinaryArtifacts.Jvm) {
val ktFiles = info.classFileFactory.inputFiles
val classLoader = createClassLoader(module, info.classFileFactory)
for (ktFile in ktFiles) {
val className = ktFile.getFacadeFqName() ?: continue
val clazz = classLoader.getGeneratedClass(className)
val method = clazz.getBoxMethodOrNull() ?: continue
boxMethodFound = true
callBoxMethodAndCheckResult(classLoader, clazz, method, unexpectedBehaviour = false)
return
}
}
private fun callBoxMethodAndCheckResult(
classLoader: URLClassLoader,
clazz: Class<*>?,
method: Method,
unexpectedBehaviour: Boolean
) {
val result = if (BOX_IN_SEPARATE_PROCESS_PORT != null) {
TODO()
// result = invokeBoxInSeparateProcess(classLoader, aClass)
} else {
val savedClassLoader = Thread.currentThread().contextClassLoader
if (savedClassLoader !== classLoader) {
// otherwise the test infrastructure used in the test may conflict with the one from the context classloader
Thread.currentThread().contextClassLoader = classLoader
}
try {
method.invoke(null) as String
} finally {
if (savedClassLoader !== classLoader) {
Thread.currentThread().contextClassLoader = savedClassLoader
}
}
}
if (unexpectedBehaviour) {
TestCase.assertNotSame("OK", result)
} else {
assertions.assertEquals("OK", result)
}
}
private fun createClassLoader(module: TestModule, classFileFactory: ClassFileFactory): GeneratedClassLoader {
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val urls = configuration.jvmClasspathRoots.map { it.toURI().toURL() }
val classLoader = URLClassLoader(urls.toTypedArray())
return GeneratedClassLoader(classFileFactory, classLoader)
}
private fun KtFile.getFacadeFqName(): String? {
return runIf(getMemberDeclarationsToGenerate(this).isNotEmpty()) {
getFileClassInfoNoResolve(this).facadeClassFqName.asString()
}
}
private fun ClassLoader.getGeneratedClass(className: String): Class<*> {
try {
return loadClass(className)
} catch (e: ClassNotFoundException) {
assertions.fail { "No class file was generated for: $className" }
}
}
private fun Class<*>.getBoxMethodOrNull(): Method? {
return try {
getMethod("box")
} catch (e: NoSuchMethodException) {
return null
}
}
}
@@ -0,0 +1,14 @@
/*
* 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.backend.ir
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.TestServices
abstract class IrBackendFacade<A : ResultingArtifact.Binary<A>>(
testServices: TestServices,
binaryKind: BinaryKind<A>
) : BackendFacade<IrBackendInput, A>(testServices, BackendKinds.IrBackend, binaryKind)
@@ -0,0 +1,32 @@
/*
* 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.backend.ir
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
import org.jetbrains.kotlin.backend.jvm.MetadataSerializerFactory
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.linkage.IrProvider
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact
// IR backend (JVM, JS, Native)
data class IrBackendInput(
val state: GenerationState,
val irModuleFragment: IrModuleFragment,
val symbolTable: SymbolTable,
val sourceManager: PsiSourceManager,
val phaseConfig: PhaseConfig,
val irProviders: List<IrProvider>,
val extensions: JvmGeneratorExtensions,
val serializerFactory: MetadataSerializerFactory
) : ResultingArtifact.BackendInput<IrBackendInput>() {
override val kind: BackendKinds.IrBackend
get() = BackendKinds.IrBackend
}
@@ -0,0 +1,36 @@
/*
* 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.backend.ir
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.TestServices
class JvmIrBackendFacade(
testServices: TestServices
) : IrBackendFacade<BinaryArtifacts.Jvm>(testServices, ArtifactKinds.Jvm) {
override fun transform(
module: TestModule,
inputArtifact: IrBackendInput
): BinaryArtifacts.Jvm {
val (state, irModuleFragment, symbolTable, sourceManager, phaseConfig, irProviders, extensions, serializerFactory) = inputArtifact
val codegenFactory = state.codegenFactory as JvmIrCodegenFactory
codegenFactory.doGenerateFilesInternal(
state,
irModuleFragment,
symbolTable,
sourceManager,
phaseConfig,
irProviders,
extensions,
serializerFactory
)
state.factory.done()
return BinaryArtifacts.Jvm(state.factory)
}
}
@@ -0,0 +1,51 @@
/*
* 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.builders
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.fir.PrivateForInline
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.test.services.DefaultsDsl
import org.jetbrains.kotlin.test.services.DefaultsProvider
import org.jetbrains.kotlin.test.model.BackendKind
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendKind
@DefaultsDsl
class DefaultsProviderBuilder {
lateinit var backend: BackendKind<*>
lateinit var frontend: FrontendKind<*>
lateinit var targetPlatform: TargetPlatform
lateinit var dependencyKind: DependencyKind
@PrivateForInline
var languageVersionSettings: LanguageVersionSettings? = null
@PrivateForInline
var languageVersionSettingsBuilder: LanguageVersionSettingsBuilder? = null
@OptIn(PrivateForInline::class)
inline fun languageSettings(init: LanguageVersionSettingsBuilder.() -> Unit) {
languageVersionSettings = LanguageVersionSettingsBuilder().apply(init).also {
languageVersionSettingsBuilder = it
}.build()
}
@OptIn(PrivateForInline::class)
fun build(): DefaultsProvider {
return DefaultsProvider(
backend,
frontend,
languageVersionSettings ?: LanguageVersionSettingsImpl(LanguageVersion.LATEST_STABLE, ApiVersion.LATEST_STABLE),
languageVersionSettingsBuilder ?: LanguageVersionSettingsBuilder(),
targetPlatform,
dependencyKind
)
}
}
@@ -0,0 +1,45 @@
/*
* 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.builders
import org.jetbrains.kotlin.test.directives.model.*
class RegisteredDirectivesBuilder {
private val simpleDirectives: MutableList<SimpleDirective> = mutableListOf()
private val stringDirectives: MutableMap<StringDirective, List<String>> = mutableMapOf()
private val valueDirectives: MutableMap<ValueDirective<*>, List<Any>> = mutableMapOf()
operator fun SimpleDirective.unaryPlus() {
simpleDirectives += this
}
infix fun StringDirective.with(value: String) {
with(listOf(value))
}
infix fun StringDirective.with(values: List<String>) {
stringDirectives.putWithExistsCheck(this, values)
}
infix fun <T : Any> ValueDirective<T>.with(value: T) {
with(listOf(value))
}
infix fun <T : Any> ValueDirective<T>.with(values: List<T>) {
valueDirectives.putWithExistsCheck(this, values)
}
private fun <K : Directive, V> MutableMap<K, V>.putWithExistsCheck(key: K, value: V) {
val alreadyRegistered = put(key, value)
if (alreadyRegistered != null) {
error("Default values for $key directive already registered")
}
}
fun build(): RegisteredDirectives {
return RegisteredDirectivesImpl(simpleDirectives, stringDirectives, valueDirectives)
}
}
@@ -0,0 +1,150 @@
/*
* 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.builders
import org.jetbrains.kotlin.test.TestConfiguration
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.impl.TestConfigurationImpl
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.*
typealias Constructor<T> = (TestServices) -> T
@DefaultsDsl
class TestConfigurationBuilder {
val defaultsProviderBuilder: DefaultsProviderBuilder = DefaultsProviderBuilder()
lateinit var assertions: Assertions
private val facades: MutableList<Constructor<AbstractTestFacade<*, *>>> = mutableListOf()
private val handlers: MutableList<Constructor<AnalysisHandler<*>>> = mutableListOf()
private val sourcePreprocessors: MutableList<Constructor<SourceFilePreprocessor>> = mutableListOf()
private val additionalMetaInfoProcessors: MutableList<Constructor<AdditionalMetaInfoProcessor>> = mutableListOf()
private val environmentConfigurators: MutableList<Constructor<EnvironmentConfigurator>> = mutableListOf()
private val additionalSourceProviders: MutableList<Constructor<AdditionalSourceProvider>> = mutableListOf()
private val metaTestConfigurators: MutableList<Constructor<MetaTestConfigurator>> = mutableListOf()
private val afterAnalysisCheckers: MutableList<Constructor<AfterAnalysisChecker>> = mutableListOf()
private var metaInfoHandlerEnabled: Boolean = false
private val directives: MutableList<DirectivesContainer> = mutableListOf()
val defaultRegisteredDirectivesBuilder: RegisteredDirectivesBuilder = RegisteredDirectivesBuilder()
private val configurationsByTestDataCondition: MutableList<Pair<Regex, TestConfigurationBuilder.() -> Unit>> = mutableListOf()
fun forTestsMatching(pattern: String, configuration: TestConfigurationBuilder.() -> Unit) {
val regex = pattern.toMatchingRegexString().toRegex()
forTestsMatching(regex, configuration)
}
infix fun String.or(other: String): String {
return """$this|$other"""
}
private fun String.toMatchingRegexString(): String = """^${replace("*", ".*")}$"""
fun forTestsMatching(pattern: Regex, configuration: TestConfigurationBuilder.() -> Unit) {
configurationsByTestDataCondition += pattern to configuration
}
inline fun globalDefaults(init: DefaultsProviderBuilder.() -> Unit) {
defaultsProviderBuilder.apply(init)
}
fun unregisterAllFacades() {
facades.clear()
}
fun useFrontendFacades(vararg constructor: Constructor<FrontendFacade<*>>) {
facades += constructor
}
fun useBackendFacades(vararg constructor: Constructor<BackendFacade<*, *>>) {
facades += constructor
}
fun useFrontend2BackendConverters(vararg constructor: Constructor<Frontend2BackendConverter<*, *>>) {
facades += constructor
}
fun useFrontendHandlers(vararg constructor: Constructor<FrontendOutputHandler<*>>) {
handlers += constructor
}
fun useBackendHandlers(vararg constructor: Constructor<BackendInputHandler<*>>) {
handlers += constructor
}
fun useArtifactsHandlers(vararg constructor: Constructor<BinaryArtifactHandler<*>>) {
handlers += constructor
}
fun useSourcePreprocessor(vararg preprocessors: Constructor<SourceFilePreprocessor>) {
sourcePreprocessors += preprocessors
}
fun useDirectives(vararg directives: DirectivesContainer) {
this.directives += directives
}
fun useConfigurators(vararg environmentConfigurators: Constructor<EnvironmentConfigurator>) {
this.environmentConfigurators += environmentConfigurators
}
fun useMetaInfoProcessors(vararg updaters: Constructor<AdditionalMetaInfoProcessor>) {
additionalMetaInfoProcessors += updaters
}
fun useAdditionalSourceProviders(vararg providers: Constructor<AdditionalSourceProvider>) {
additionalSourceProviders += providers
}
fun useMetaTestConfigurators(vararg configurators: Constructor<MetaTestConfigurator>) {
metaTestConfigurators += configurators
}
fun useAfterAnalysisCheckers(vararg checkers: Constructor<AfterAnalysisChecker>) {
afterAnalysisCheckers += checkers
}
inline fun defaultDirectives(init: RegisteredDirectivesBuilder.() -> Unit) {
defaultRegisteredDirectivesBuilder.apply(init)
}
fun enableMetaInfoHandler() {
metaInfoHandlerEnabled = true
}
fun build(testDataPath: String): TestConfiguration {
for ((regex, configuration) in configurationsByTestDataCondition) {
if (regex.matches(testDataPath)) {
this.configuration()
}
}
return TestConfigurationImpl(
defaultsProviderBuilder.build(),
assertions,
facades,
handlers,
sourcePreprocessors,
additionalMetaInfoProcessors,
environmentConfigurators,
additionalSourceProviders,
metaTestConfigurators,
afterAnalysisCheckers,
metaInfoHandlerEnabled,
directives,
defaultRegisteredDirectivesBuilder.build()
)
}
}
inline fun testConfiguration(testDataPath: String, init: TestConfigurationBuilder.() -> Unit): TestConfiguration {
return TestConfigurationBuilder().apply(init).build(testDataPath)
}
@@ -0,0 +1,9 @@
/*
* 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.builders
@DslMarker
annotation class TestConfigurationDslMarker()
@@ -0,0 +1,7 @@
/*
* 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.builders
@@ -0,0 +1,12 @@
/*
* 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.builders
import org.jetbrains.kotlin.test.TestRunner
inline fun testRunner(testDataPath: String, crossinline init: TestConfigurationBuilder.() -> Unit): TestRunner {
return TestRunner(testConfiguration(testDataPath, init))
}
@@ -0,0 +1,40 @@
/*
* 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.SimpleDirectivesContainer
object AdditionalFilesDirectives : SimpleDirectivesContainer() {
val CHECK_TYPE by directive(
description = """
Adds util functions for type checking
See file ./compiler/testData/diagnostics/helpers/types/checkType.kt
""".trimIndent()
)
val WITH_COROUTINES by directive(
description = """
Adds util functions for checking coroutines
See file ./compiler/testData/diagnostics/helpers/coroutines/CoroutineHelpers.kt
""".trimIndent()
)
val CHECK_STATE_MACHINE by directive(
description = """
Adds util functions for checking state machines
May be enabled only with $WITH_COROUTINES directive
See file ./compiler/testData/diagnostics/helpers/coroutines/StateMachineChecker.kt
""".trimIndent()
)
val CHECK_TAIL_CALL_OPTIMIZATION by directive(
description = """
Adds util functions for checking tail call optimizations
May be enabled only with $WITH_COROUTINES directive
See file ./compiler/testData/diagnostics/helpers/coroutines/TailCallOptimizationChecker.kt
""".trimIndent()
)
}
@@ -0,0 +1,15 @@
/*
* 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.TargetBackend
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
object CodegenTestDirectives : SimpleDirectivesContainer() {
val IGNORE_BACKEND by enumDirective<TargetBackend>(
description = "Ignore failures of test on target backend"
)
}
@@ -0,0 +1,49 @@
/*
* 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.JvmEnvironmentConfigurationDirectives.USE_JAVAC
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
object DiagnosticsDirectives : SimpleDirectivesContainer() {
val WITH_NEW_INFERENCE by directive(
description = "Enables rendering different diagnostics for old and new inference"
)
val DIAGNOSTICS by stringDirective(
description = """
Enables or disables rendering of specific diagnostics.
Syntax:
Must be '[+-]DIAGNOSTIC_FACTORY_NAME'
where '+' means 'include'
'-' means 'exclude'
'+' May be used in case if some diagnostic was disabled by default in test runner
and it should be enabled in specific test
""".trimIndent()
)
val SKIP_TXT by directive(
description = "Disables handler which dumps declarations to testName.txt"
)
val NI_EXPECTED_FILE by directive(
description = "Create separate .ni.txt file for declarations dump with new inference enabled"
)
val SKIP_JAVAC by directive(
description = "Skip this test if $USE_JAVAC enabled"
)
val JAVAC_EXPECTED_FILE by directive(
description = "Dump descriptors to .javac.txt file if $USE_JAVAC enabled"
)
val MARK_DYNAMIC_CALLS by directive(
description = """
Render debug info about dynamic calls
""".trimIndent()
)
}
@@ -0,0 +1,27 @@
/*
* 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.SimpleDirectivesContainer
object FirDiagnosticsDirectives : SimpleDirectivesContainer() {
val DUMP_CFG by directive(
description = """
Dumps control flow graphs of all declarations to `testName.dot` file
This directive may be applied only to all modules
""".trimIndent()
)
val FIR_DUMP by directive(
description = """
Dumps resulting fir to `testName.fir` file
""".trimIndent()
)
val FIR_IDENTICAL by directive(
description = "Contents of fir test data file and FE 1.0 are identical"
)
}
@@ -0,0 +1,13 @@
/*
* 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.serialization.js.ModuleKind
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
object JsEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
val MODULE_KIND by enumDirective<ModuleKind>("Specifies kind of js module")
}
@@ -0,0 +1,36 @@
/*
* 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.config.JvmTarget
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.directives.model.SimpleDirectivesContainer
object JvmEnvironmentConfigurationDirectives : SimpleDirectivesContainer() {
val JVM_TARGET by enumDirective<JvmTarget>(
description = "Target bytecode version",
additionalParser = JvmTarget.Companion::fromString
)
val JDK_KIND by enumDirective<TestJdkKind>("JDK used in tests")
val FULL_JDK by directive("Add full java standard library to classpath")
val STDLIB_JDK8 by directive("Add Java 8 stdlib to classpath")
val WITH_RUNTIME by directive(
description = """
Add Kotlin stdlib to classpath
This directive is deprecated, use WITH_STDLIB instead
""".trimIndent()
)
val WITH_STDLIB by directive("Add Kotlin runtime to classpath")
val WITH_REFLECT by directive("Add Kotlin reflect to classpath")
val NO_RUNTIME by directive("Don't add any runtime libs to classpath")
val ANDROID_ANNOTATIONS by directive("Add android annotations to classpath")
val USE_PSI_CLASS_FILES_READING by directive("Use a slower (PSI-based) class files reading implementation")
val USE_JAVAC by directive("Enable javac integration")
}
@@ -0,0 +1,32 @@
/*
* 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.classic
import org.jetbrains.kotlin.test.backend.classic.ClassicBackendInput
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.model.*
class ClassicFrontend2ClassicBackendConverter(
testServices: TestServices
) : Frontend2BackendConverter<ClassicFrontendOutputArtifact, ClassicBackendInput>(
testServices,
FrontendKinds.ClassicFrontend,
BackendKinds.ClassicBackend
) {
override fun transform(
module: TestModule,
inputArtifact: ClassicFrontendOutputArtifact
): ClassicBackendInput {
val (psiFiles, analysisResults, project, languageVersionSettings) = inputArtifact
return ClassicBackendInput(
psiFiles.values,
analysisResults.bindingContext,
analysisResults.moduleDescriptor,
project,
languageVersionSettings
)
}
}
@@ -0,0 +1,158 @@
/*
* 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.classic
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.ir.BuiltinSymbolsBase
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.backend.jvm.JvmNameProvider
import org.jetbrains.kotlin.backend.jvm.codegen.DescriptorMetadataSerializer
import org.jetbrains.kotlin.backend.jvm.jvmPhases
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin
import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.ir.backend.jvm.serialization.EmptyLoggingContext
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrLinker
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmManglerDesc
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.TypeTranslator
import org.jetbrains.kotlin.psi2ir.Psi2IrConfiguration
import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
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
class ClassicFrontend2IrConverter(
testServices: TestServices
) : Frontend2BackendConverter<ClassicFrontendOutputArtifact, IrBackendInput>(
testServices,
FrontendKinds.ClassicFrontend,
BackendKinds.IrBackend
) {
override fun transform(
module: TestModule,
inputArtifact: ClassicFrontendOutputArtifact
): IrBackendInput {
val (psiFiles, analysisResult, project, languageVersionSettings) = inputArtifact
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val files = psiFiles.values.toList()
val phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jvmPhases)
val state = GenerationState.Builder(
project, ClassBuilderFactories.TEST, analysisResult.moduleDescriptor, analysisResult.bindingContext,
files, configuration
).codegenFactory(
JvmIrCodegenFactory(phaseConfig)
).isIrBackend(true).build()
val extensions = JvmGeneratorExtensions()
val mangler = JvmManglerDesc(MainFunctionDetector(state.bindingContext, state.languageVersionSettings))
val psi2ir = Psi2IrTranslator(state.languageVersionSettings, Psi2IrConfiguration())
val symbolTable = SymbolTable(JvmIdSignatureDescriptor(mangler), IrFactoryImpl, JvmNameProvider)
val psi2irContext = psi2ir.createGeneratorContext(state.module, state.bindingContext, symbolTable, extensions)
val pluginExtensions = IrGenerationExtension.getInstances(state.project)
val functionFactory = IrFunctionFactory(psi2irContext.irBuiltIns, symbolTable)
psi2irContext.irBuiltIns.functionFactory = functionFactory
val stubGenerator = DeclarationStubGenerator(
psi2irContext.moduleDescriptor, symbolTable, psi2irContext.irBuiltIns.languageVersionSettings, extensions
)
val frontEndContext = object : TranslationPluginContext {
override val moduleDescriptor: ModuleDescriptor
get() = psi2irContext.moduleDescriptor
override val bindingContext: BindingContext
get() = psi2irContext.bindingContext
override val symbolTable: ReferenceSymbolTable
get() = symbolTable
override val typeTranslator: TypeTranslator
get() = psi2irContext.typeTranslator
override val irBuiltIns: IrBuiltIns
get() = psi2irContext.irBuiltIns
}
val irLinker = JvmIrLinker(
psi2irContext.moduleDescriptor,
EmptyLoggingContext,
psi2irContext.irBuiltIns,
symbolTable,
functionFactory,
frontEndContext,
stubGenerator,
mangler
)
val pluginContext by lazy {
psi2irContext.run {
val symbols = BuiltinSymbolsBase(irBuiltIns, moduleDescriptor.builtIns, symbolTable.lazyWrapper)
IrPluginContextImpl(
moduleDescriptor, bindingContext, languageVersionSettings, symbolTable, typeTranslator, irBuiltIns, irLinker, symbols
)
}
}
for (extension in pluginExtensions) {
psi2ir.addPostprocessingStep { moduleFragment ->
val old = stubGenerator.unboundSymbolGeneration
try {
stubGenerator.unboundSymbolGeneration = true
extension.generate(moduleFragment, pluginContext)
} finally {
stubGenerator.unboundSymbolGeneration = old
}
}
}
val dependencies = psi2irContext.moduleDescriptor.allDependencyModules.map {
val kotlinLibrary = (it.getCapability(KlibModuleOrigin.CAPABILITY) as? DeserializedKlibModuleOrigin)?.library
irLinker.deserializeIrModuleHeader(it, kotlinLibrary)
}
val irProviders = listOf(irLinker)
val irModuleFragment = psi2ir.generateModuleFragment(
psi2irContext,
files,
irProviders,
pluginExtensions,
expectDescriptorToSymbol = null
)
irLinker.postProcess()
stubGenerator.unboundSymbolGeneration = true
// We need to compile all files we reference in Klibs
irModuleFragment.files.addAll(dependencies.flatMap { it.files })
return IrBackendInput(
state,
irModuleFragment,
symbolTable,
psi2irContext.sourceManager,
phaseConfig,
irProviders,
extensions,
::DescriptorMetadataSerializer
)
}
}
@@ -0,0 +1,263 @@
/*
* 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.classic
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory
import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.JVMConfigurationKeys.JVM_TARGET
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.context.withModule
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.frontend.java.di.createContainerForLazyResolveWithJava
import org.jetbrains.kotlin.frontend.java.di.initJvmBuiltInsForTopDownAnalysis
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.js.analyze.TopDownAnalyzerFacadeForJS
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.load.java.lazy.SingleModuleClassResolver
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.js.isJs
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.CompilerEnvironment
import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer
import org.jetbrains.kotlin.resolve.TopDownAnalysisMode
import org.jetbrains.kotlin.resolve.jvm.JavaDescriptorResolver
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import org.jetbrains.kotlin.serialization.deserialization.MetadataPartProvider
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.FrontendFacade
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.util.KtTestUtil
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import java.io.File
class ClassicFrontendFacade(
testServices: TestServices
) : FrontendFacade<ClassicFrontendOutputArtifact>(testServices, FrontendKinds.ClassicFrontend) {
override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::ModuleDescriptorProvider))
override fun analyze(module: TestModule): ClassicFrontendOutputArtifact {
val dependencyProvider = testServices.dependencyProvider
val moduleDescriptorProvider = testServices.moduleDescriptorProvider
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
val packagePartProviderFactory = compilerConfigurationProvider.getPackagePartProviderFactory(module)
val project = compilerConfigurationProvider.getProject(module)
val configuration = compilerConfigurationProvider.getCompilerConfiguration(module)
val ktFilesMap = testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).toMutableMap()
val languageVersionSettings = module.languageVersionSettings
val sourceDependencies = module.dependencies.filter { it.kind == DependencyKind.Source }
val dependentDescriptors = sourceDependencies.map {
val testModule = dependencyProvider.getTestModule(it.moduleName)
moduleDescriptorProvider.getModuleDescriptor(testModule)
}
var hasCommonModules = false
sourceDependencies.forEach {
val dependencyModule = dependencyProvider.getTestModule(it.moduleName)
if (dependencyModule.targetPlatform.isCommon()) {
val artifact = dependencyProvider.getArtifact(dependencyModule, FrontendKinds.ClassicFrontend)
/*
* We need create KtFiles again with new project because otherwise we can access to some caches using
* old project as key which may leads to missing services in core environment
*/
ktFilesMap.putAll(testServices.sourceFileProvider.getKtFilesForSourceFiles(artifact.allKtFiles.keys, project))
hasCommonModules = true
}
}
val ktFiles = ktFilesMap.values.toList()
setupJavacIfNeeded(module, ktFiles, configuration)
val analysisResult = performResolve(
module,
project,
configuration,
packagePartProviderFactory,
ktFiles,
languageVersionSettings,
dependentDescriptors,
hasCommonModules
)
moduleDescriptorProvider.replaceModuleDescriptorForModule(module, analysisResult.moduleDescriptor)
return ClassicFrontendOutputArtifact(
ktFilesMap,
analysisResult,
project,
languageVersionSettings
)
}
private fun setupJavacIfNeeded(
module: TestModule,
ktFiles: List<KtFile>,
configuration: CompilerConfiguration
) {
if (JvmEnvironmentConfigurationDirectives.USE_JAVAC !in module.directives) return
val mockJdk = runIf(JvmEnvironmentConfigurationDirectives.FULL_JDK !in module.directives) {
File(KtTestUtil.getHomeDirectory(), "compiler/testData/mockJDK/jre/lib/rt.jar")
}
testServices.compilerConfigurationProvider.registerJavacForModule(module, ktFiles, mockJdk)
configuration.put(JVMConfigurationKeys.USE_JAVAC, true)
}
private fun performResolve(
module: TestModule,
project: Project,
configuration: CompilerConfiguration,
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
files: List<KtFile>,
languageVersionSettings: LanguageVersionSettings,
dependentDescriptors: List<ModuleDescriptorImpl>,
hasCommonModules: Boolean
): AnalysisResult {
val targetPlatform = module.targetPlatform
return when {
targetPlatform.isJvm() -> performJvmModuleResolve(
module,
project,
configuration,
packagePartProviderFactory,
files,
dependentDescriptors,
hasCommonModules
)
targetPlatform.isJs() -> performJsModuleResolve(project, configuration, files, dependentDescriptors)
targetPlatform.isNative() -> TODO()
targetPlatform.isCommon() -> performCommonModuleResolve(module, files, languageVersionSettings)
else -> error("Should not be here")
}
}
@OptIn(ExperimentalStdlibApi::class)
private fun performJvmModuleResolve(
module: TestModule,
project: Project,
configuration: CompilerConfiguration,
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
files: List<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>,
hasCommonModules: Boolean
): AnalysisResult {
val moduleTrace = NoScopeRecordCliBindingTrace()
if (!hasCommonModules) {
return TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(
project,
files,
moduleTrace,
configuration.copy(),
packagePartProviderFactory,
explicitModuleDependencyList = dependentDescriptors
)
}
val projectContext = ProjectContext(project, "test project context")
val storageManager = projectContext.storageManager
val builtIns = JvmBuiltIns(storageManager, JvmBuiltIns.Kind.FROM_CLASS_LOADER)
val moduleDescriptor = ModuleDescriptorImpl(Name.special("<${module.name}>"), storageManager, builtIns, module.targetPlatform)
val dependencies = buildList {
add(moduleDescriptor)
add(moduleDescriptor.builtIns.builtInsModule)
addAll(dependentDescriptors)
}
moduleDescriptor.setDependencies(dependencies)
val moduleContentScope = GlobalSearchScope.allScope(project)
val moduleClassResolver = SingleModuleClassResolver()
val moduleContext = projectContext.withModule(moduleDescriptor)
val jvmTarget = configuration[JVM_TARGET] ?: JvmTarget.DEFAULT
val container = createContainerForLazyResolveWithJava(
JvmPlatforms.jvmPlatformByTargetVersion(jvmTarget), // TODO(dsavvinov): do not pass JvmTarget around
moduleContext,
moduleTrace,
FileBasedDeclarationProviderFactory(moduleContext.storageManager, files),
moduleContentScope,
moduleClassResolver,
CompilerEnvironment, LookupTracker.DO_NOTHING,
ExpectActualTracker.DoNothing,
packagePartProviderFactory(moduleContentScope),
module.languageVersionSettings,
useBuiltInsProvider = true
)
container.initJvmBuiltInsForTopDownAnalysis()
moduleClassResolver.resolver = container.get()
moduleDescriptor.initialize(
CompositePackageFragmentProvider(
listOf(
container.get<KotlinCodeAnalyzer>().packageFragmentProvider,
container.get<JavaDescriptorResolver>().packageFragmentProvider
)
)
)
container.get<LazyTopDownAnalyzer>().analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, files)
return AnalysisResult.success(moduleTrace.bindingContext, moduleDescriptor)
}
@OptIn(ExperimentalStdlibApi::class)
private fun performJsModuleResolve(
project: Project,
configuration: CompilerConfiguration,
files: List<KtFile>,
dependentDescriptors: List<ModuleDescriptorImpl>
): AnalysisResult {
val jsConfig = JsConfig(project, configuration)
val dependentDescriptorsIncludingLibraries = buildList {
addAll(dependentDescriptors)
addAll(jsConfig.moduleDescriptors)
}
return TopDownAnalyzerFacadeForJS.analyzeFiles(
files,
project,
configuration,
moduleDescriptors = dependentDescriptorsIncludingLibraries,
friendModuleDescriptors = emptyList()
)
}
private fun performCommonModuleResolve(
module: TestModule,
files: List<KtFile>,
languageVersionSettings: LanguageVersionSettings,
): AnalysisResult {
return CommonResolverForModuleFactory.analyzeFiles(
files,
Name.special("<${module.name}>"),
dependOnBuiltIns = true,
languageVersionSettings,
module.targetPlatform,
// TODO: add dependency manager
) { _ ->
// TODO
MetadataPartProvider.Empty
}
}
}
@@ -0,0 +1,26 @@
/*
* 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.classic
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact
import org.jetbrains.kotlin.test.model.TestFile
data class ClassicFrontendOutputArtifact(
val allKtFiles: Map<TestFile, KtFile>,
val analysisResult: AnalysisResult,
val project: Project,
val languageVersionSettings: LanguageVersionSettings
) : ResultingArtifact.FrontendOutput<ClassicFrontendOutputArtifact>() {
override val kind: FrontendKinds.ClassicFrontend
get() = FrontendKinds.ClassicFrontend
val ktFiles: Map<TestFile, KtFile> = allKtFiles.filterKeys { !it.isAdditional }
}
@@ -0,0 +1,32 @@
/*
* 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.classic
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.test.services.TestService
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
import org.jetbrains.kotlin.test.model.TestModule
class ModuleDescriptorProvider(
private val testServices: TestServices
) : TestService {
private val moduleDescriptorByModule = mutableMapOf<TestModule, ModuleDescriptorImpl>()
fun getModuleDescriptor(testModule: TestModule): ModuleDescriptorImpl {
return moduleDescriptorByModule[testModule] ?: testServices.assertions.fail {
"Module descriptor for module ${testModule.name} not found"
}
}
fun replaceModuleDescriptorForModule(testModule: TestModule, moduleDescriptor: ModuleDescriptor) {
require(moduleDescriptor is ModuleDescriptorImpl)
moduleDescriptorByModule[testModule] = moduleDescriptor
}
}
val TestServices.moduleDescriptorProvider: ModuleDescriptorProvider by TestServices.testServiceAccessor()
@@ -0,0 +1,253 @@
/*
* 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.classic.handlers
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.asJava.getJvmSignatureDiagnostics
import org.jetbrains.kotlin.checkers.diagnostics.SyntaxErrorDiagnostic
import org.jetbrains.kotlin.checkers.utils.CheckerTestUtil
import org.jetbrains.kotlin.checkers.utils.DiagnosticsRenderingConfiguration
import org.jetbrains.kotlin.codeMetaInfo.model.CodeMetaInfo
import org.jetbrains.kotlin.codeMetaInfo.model.DiagnosticCodeMetaInfo
import org.jetbrains.kotlin.codeMetaInfo.model.ParsedCodeMetaInfo
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.MARK_DYNAMIC_CALLS
import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import java.util.*
class ClassicDiagnosticsHandler(testServices: TestServices) : ClassicFrontendAnalysisHandler(testServices) {
override val directivesContainers: List<DirectivesContainer> =
listOf(DiagnosticsDirectives)
override val additionalServices: List<ServiceRegistrationData> =
listOf(service(::DiagnosticsService))
private val globalMetadataInfoHandler: GlobalMetadataInfoHandler
get() = testServices.globalMetadataInfoHandler
private val diagnosticsService: DiagnosticsService
get() = testServices.diagnosticsService
@OptIn(ExperimentalStdlibApi::class)
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) {
var allDiagnostics = info.analysisResult.bindingContext.diagnostics + computeJvmSignatureDiagnostics(info)
if (AdditionalFilesDirectives.CHECK_TYPE in module.directives) {
allDiagnostics = allDiagnostics.filter { it.factory.name != Errors.UNDERSCORE_USAGE_WITHOUT_BACKTICKS.name }
}
if (LanguageSettingsDirectives.API_VERSION in module.directives) {
allDiagnostics = allDiagnostics.filter { it.factory.name != Errors.NEWER_VERSION_IN_SINCE_KOTLIN.name }
}
val diagnosticsPerFile = allDiagnostics.groupBy { it.psiFile }
val withNewInferenceModeEnabled = testServices.withNewInferenceModeEnabled()
val configuration = DiagnosticsRenderingConfiguration(
platform = null,
withNewInference = info.languageVersionSettings.supportsFeature(LanguageFeature.NewInference),
languageVersionSettings = info.languageVersionSettings,
skipDebugInfoDiagnostics = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
.getBoolean(JVMConfigurationKeys.IR)
)
for ((file, ktFile) in info.ktFiles) {
val diagnostics = diagnosticsPerFile[ktFile] ?: emptyList()
for (diagnostic in diagnostics) {
if (!diagnostic.isValid) continue
if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name)) continue
globalMetadataInfoHandler.addMetadataInfosForFile(
file,
diagnostic.toMetaInfo(
module,
file,
configuration.withNewInference,
withNewInferenceModeEnabled
)
)
}
for (errorElement in AnalyzingUtils.getSyntaxErrorRanges(ktFile)) {
globalMetadataInfoHandler.addMetadataInfosForFile(
file,
SyntaxErrorDiagnostic(errorElement).toMetaInfo(
module,
file,
configuration.withNewInference,
withNewInferenceModeEnabled
)
)
}
processDebugInfoDiagnostics(configuration, module, file, ktFile, info, withNewInferenceModeEnabled)
}
}
private fun computeJvmSignatureDiagnostics(info: ClassicFrontendOutputArtifact): Set<Diagnostic> {
if (testServices.moduleStructure.modules.any { !it.targetPlatform.isJvm() }) return emptySet()
val bindingContext = info.analysisResult.bindingContext
val project = info.project
val jvmSignatureDiagnostics = HashSet<Diagnostic>()
for (ktFile in info.ktFiles.values) {
val declarations = PsiTreeUtil.findChildrenOfType(ktFile, KtDeclaration::class.java)
for (declaration in declarations) {
val diagnostics = getJvmSignatureDiagnostics(
declaration,
bindingContext.diagnostics,
GlobalSearchScope.allScope(project)
) ?: continue
jvmSignatureDiagnostics.addAll(diagnostics.forElement(declaration))
}
}
return jvmSignatureDiagnostics
}
private fun Diagnostic.toMetaInfo(
module: TestModule,
file: TestFile,
newInferenceEnabled: Boolean,
withNewInferenceModeEnabled: Boolean
): List<DiagnosticCodeMetaInfo> = textRanges.map { range ->
val metaInfo = DiagnosticCodeMetaInfo(range, ClassicMetaInfoUtils.renderDiagnosticNoArgs, this)
if (withNewInferenceModeEnabled) {
metaInfo.attributes += if (newInferenceEnabled) OldNewInferenceMetaInfoProcessor.NI else OldNewInferenceMetaInfoProcessor.OI
}
if (file !in module.files) {
val targetPlatform = module.targetPlatform
metaInfo.attributes += when {
targetPlatform.isJvm() -> "JVM"
targetPlatform.isJs() -> "JS"
targetPlatform.isNative() -> "NATIVE"
targetPlatform.isCommon() -> "COMMON"
else -> error("Should not be here")
}
}
val existing = globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo)
if (existing.any { it.description != null }) {
metaInfo.replaceRenderConfiguration(ClassicMetaInfoUtils.renderDiagnosticWithArgs)
}
metaInfo
}
private fun processDebugInfoDiagnostics(
configuration: DiagnosticsRenderingConfiguration,
module: TestModule,
file: TestFile,
ktFile: KtFile,
info: ClassicFrontendOutputArtifact,
withNewInferenceModeEnabled: Boolean
) {
val diagnosedRanges = globalMetadataInfoHandler.getExistingMetaInfosForFile(file)
.groupBy(
keySelector = { it.start..it.end },
valueTransform = { it.tag }
)
.mapValues { (_, it) -> it.toMutableSet() }
val debugAnnotations = CheckerTestUtil.getDebugInfoDiagnostics(
ktFile,
info.analysisResult.bindingContext,
markDynamicCalls = MARK_DYNAMIC_CALLS in module.directives,
dynamicCallDescriptors = mutableListOf(),
configuration,
dataFlowValueFactory = DataFlowValueFactoryImpl(info.languageVersionSettings),
info.analysisResult.moduleDescriptor as ModuleDescriptorImpl,
diagnosedRanges = diagnosedRanges
)
debugAnnotations.mapNotNull { debugAnnotation ->
if (!diagnosticsService.shouldRenderDiagnostic(module, debugAnnotation.diagnostic.factory.name)) return@mapNotNull null
globalMetadataInfoHandler.addMetadataInfosForFile(
file,
debugAnnotation.diagnostic.toMetaInfo(module, file, configuration.withNewInference, withNewInferenceModeEnabled)
)
}
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
class OldNewInferenceMetaInfoProcessor(testServices: TestServices) : AdditionalMetaInfoProcessor(testServices) {
companion object {
const val OI = "OI"
const val NI = "NI"
}
override fun processMetaInfos(module: TestModule, file: TestFile) {
/*
* Rules for OI/NI attribute:
* ┌──────────┬──────┬──────┬──────────┐
* │ │ OI │ NI │ nothing │ <- reported
* ├──────────┼──────┼──────┼──────────┤
* │ nothing │ both │ both │ nothing │
* │ OI │ OI │ both │ OI │
* │ NI │ both │ NI │ NI │
* │ both │ both │ both │ opposite │ <- OI if NI enabled in test and vice versa
* └──────────┴──────┴──────┴──────────┘
* ^ existed
*/
if (!testServices.withNewInferenceModeEnabled()) return
val newInferenceEnabled = module.languageVersionSettings.supportsFeature(LanguageFeature.NewInference)
val (currentFlag, otherFlag) = when (newInferenceEnabled) {
true -> NI to OI
false -> OI to NI
}
val matchedExistedInfos = mutableSetOf<ParsedCodeMetaInfo>()
val matchedReportedInfos = mutableSetOf<CodeMetaInfo>()
val allReportedInfos = globalMetadataInfoHandler.getReportedMetaInfosForFile(file)
for ((_, reportedInfos) in allReportedInfos.groupBy { Triple(it.start, it.end, it.tag) }) {
val existedInfos = globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, reportedInfos.first())
for ((reportedInfo, existedInfo) in reportedInfos.zip(existedInfos)) {
matchedExistedInfos += existedInfo
matchedReportedInfos += reportedInfo
if (currentFlag !in reportedInfo.attributes) continue
if (currentFlag in existedInfo.attributes) continue
reportedInfo.attributes.remove(currentFlag)
}
}
if (allReportedInfos.size != matchedReportedInfos.size) {
for (info in allReportedInfos) {
if (info !in matchedReportedInfos) {
info.attributes.remove(currentFlag)
}
}
}
val allExistedInfos = globalMetadataInfoHandler.getExistingMetaInfosForFile(file)
if (allExistedInfos.size == matchedExistedInfos.size) return
val newInfos = allExistedInfos.mapNotNull {
if (it in matchedExistedInfos) return@mapNotNull null
if (currentFlag in it.attributes) return@mapNotNull null
it.copy().apply {
if (otherFlag !in attributes) {
attributes += otherFlag
}
}
}
globalMetadataInfoHandler.addMetadataInfosForFile(file, newInfos)
}
}
private fun TestServices.withNewInferenceModeEnabled(): Boolean {
return DiagnosticsDirectives.WITH_NEW_INFERENCE in moduleStructure.allDirectives
}
@@ -0,0 +1,17 @@
/*
* 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.classic.handlers
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.FrontendOutputHandler
import org.jetbrains.kotlin.test.services.TestServices
abstract class ClassicFrontendAnalysisHandler(
testServices: TestServices
) : FrontendOutputHandler<ClassicFrontendOutputArtifact>(testServices, FrontendKinds.ClassicFrontend)
@@ -0,0 +1,13 @@
/*
* 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.classic.handlers
import org.jetbrains.kotlin.codeMetaInfo.renderConfigurations.DiagnosticCodeMetaInfoRenderConfiguration
object ClassicMetaInfoUtils {
val renderDiagnosticNoArgs = DiagnosticCodeMetaInfoRenderConfiguration().apply { renderParams = false }
val renderDiagnosticWithArgs = DiagnosticCodeMetaInfoRenderConfiguration().apply { renderParams = true }
}
@@ -0,0 +1,136 @@
/*
* 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.classic.handlers
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.isJavaFile
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.test.util.DescriptorValidator
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator
import org.jetbrains.kotlin.test.util.RecursiveDescriptorComparator.RECURSIVE
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumper
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumperImpl
import org.jetbrains.kotlin.utils.keysToMap
import java.util.function.Predicate
import java.util.regex.Pattern
class DeclarationsDumpHandler(
testServices: TestServices
) : ClassicFrontendAnalysisHandler(testServices) {
companion object {
private val NAMES_OF_CHECK_TYPE_HELPER = listOf("checkSubtype", "CheckTypeInv", "_", "checkType").map { Name.identifier(it) }
private val JAVA_PACKAGE_PATTERN = Pattern.compile("^\\s*package [.\\w\\d]*", Pattern.MULTILINE)
}
override val directivesContainers: List<DirectivesContainer>
get() = listOf(DiagnosticsDirectives)
private val dumper: MultiModuleInfoDumper = MultiModuleInfoDumperImpl(moduleHeaderTemplate = "// -- Module: <%s> --")
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (dumper.isEmpty()) return
val resultDump = dumper.generateResultingDump()
val testDataFile = testServices.moduleStructure.originalTestDataFiles.first()
val allDirectives = testServices.moduleStructure.allDirectives
val prefix = when {
DiagnosticsDirectives.NI_EXPECTED_FILE in allDirectives &&
testServices.moduleStructure.modules.any { it.languageVersionSettings.supportsFeature(LanguageFeature.NewInference) } -> ".ni"
JvmEnvironmentConfigurationDirectives.USE_JAVAC in allDirectives
&& DiagnosticsDirectives.JAVAC_EXPECTED_FILE in allDirectives -> ".javac"
else -> ""
}
val expectedFileName = "${testDataFile.nameWithoutExtension}$prefix.txt"
val expectedFile = testDataFile.parentFile.resolve(expectedFileName)
assertions.assertEqualsToFile(expectedFile, resultDump)
}
override fun processModule(module: TestModule, info: ClassicFrontendOutputArtifact) {
if (DiagnosticsDirectives.SKIP_TXT in module.directives) return
val moduleDescriptor = info.analysisResult.moduleDescriptor
val checkTypeEnabled = AdditionalFilesDirectives.CHECK_TYPE in module.directives
val comparator = RecursiveDescriptorComparator(
createdAffectedPackagesConfiguration(module.files, info.ktFiles, moduleDescriptor, checkTypeEnabled)
)
val packages = listOf(FqName.ROOT)
val textByPackage = packages.keysToMap { StringBuilder() }
for ((packageName, packageText) in textByPackage.entries) {
val aPackage = moduleDescriptor.getPackage(packageName)
assertions.assertFalse(aPackage.isEmpty())
val actualSerialized = comparator.serializeRecursively(aPackage)
packageText.append(actualSerialized)
}
val allPackagesText = textByPackage.values.joinToString("\n")
dumper.builderForModule(module).appendLine(allPackagesText)
}
private fun createdAffectedPackagesConfiguration(
testFiles: List<TestFile>,
ktFiles: Map<TestFile, KtFile>,
moduleDescriptor: ModuleDescriptor,
checkTypeEnabled: Boolean
): RecursiveDescriptorComparator.Configuration {
val packagesNames = testFiles.mapNotNullTo(mutableSetOf()) {
val ktFile = ktFiles[it]
when {
ktFile != null -> ktFile.packageFqName.pathSegments().firstOrNull() ?: SpecialNames.ROOT_PACKAGE
it.isJavaFile -> getJavaFilePackage(it)
else -> null
}
}
val stepIntoFilter = Predicate<DeclarationDescriptor> { descriptor ->
val module = DescriptorUtils.getContainingModuleOrNull(descriptor)
if (module != moduleDescriptor) return@Predicate false
if (descriptor is PackageViewDescriptor) {
val fqName = descriptor.fqName
return@Predicate fqName.isRoot || fqName.pathSegments().first() in packagesNames
}
if (checkTypeEnabled && descriptor.name in NAMES_OF_CHECK_TYPE_HELPER) return@Predicate false
true
}
return RECURSIVE.filterRecursion(stepIntoFilter)
.withValidationStrategy(DescriptorValidator.ValidationVisitor.errorTypesAllowed())
.checkFunctionContracts(true)
}
private fun getJavaFilePackage(testFile: TestFile): Name {
val matcher = JAVA_PACKAGE_PATTERN.matcher(testFile.originalContent)
if (matcher.find()) {
return testFile.originalContent
.substring(matcher.start(), matcher.end())
.split(" ")
.last()
.filter { !it.isWhitespace() }
.let { Name.identifier(it.split(".").first()) }
}
return SpecialNames.ROOT_PACKAGE
}
}
@@ -0,0 +1,21 @@
/*
* 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.classic.handlers
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.services.MetaTestConfigurator
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.moduleStructure
class DiagnosticTestWithJavacSkipConfigurator(testServices: TestServices) : MetaTestConfigurator(testServices) {
override val directives: List<DirectivesContainer>
get() = listOf(DiagnosticsDirectives)
override fun shouldSkipTest(): Boolean {
return DiagnosticsDirectives.SKIP_JAVAC in testServices.moduleStructure.allDirectives
}
}
@@ -0,0 +1,46 @@
/*
* 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.classic.handlers
import org.jetbrains.kotlin.codeMetaInfo.clearTextFromDiagnosticMarkup
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
import org.jetbrains.kotlin.test.runners.AbstractFirDiagnosticTest
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.test.utils.firTestDataFile
import java.io.File
class FirTestDataConsistencyHandler(testServices: TestServices) : AfterAnalysisChecker(testServices) {
override val directives: List<DirectivesContainer>
get() = listOf(FirDiagnosticsDirectives)
override fun check(failedAssertions: List<AssertionError>) {
val moduleStructure = testServices.moduleStructure
val testData = moduleStructure.originalTestDataFiles.first()
if (testData.extension == "kts") return
if (FirDiagnosticsDirectives.FIR_IDENTICAL in moduleStructure.allDirectives) return
val firTestData = testData.firTestDataFile
if (!firTestData.exists()) {
runFirTestAndGeneratedTestData(testData, firTestData)
return
}
val originalFileContent = clearTextFromDiagnosticMarkup(testData.readText())
val firFileContent = clearTextFromDiagnosticMarkup(firTestData.readText())
testServices.assertions.assertEquals(originalFileContent, firFileContent) {
"Original and fir test data aren't identical. " +
"Please, add changes from ${testData.name} to ${firTestData.name}"
}
}
private fun runFirTestAndGeneratedTestData(testData: File, firTestData: File) {
firTestData.writeText(clearTextFromDiagnosticMarkup(testData.readText()))
val test = object : AbstractFirDiagnosticTest() {}
test.runTest(firTestData.absolutePath)
}
}
@@ -0,0 +1,96 @@
/*
* 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.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.jvm.JvmGeneratorExtensions
import org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory
import org.jetbrains.kotlin.backend.jvm.jvmPhases
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.jvm.compiler.NoScopeRecordCliBindingTrace
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.codegen.ClassBuilderFactories
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.container.get
import org.jetbrains.kotlin.fir.backend.jvm.FirJvmBackendClassResolver
import org.jetbrains.kotlin.fir.backend.jvm.FirMetadataSerializer
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.ir.descriptors.IrFunctionFactory
import org.jetbrains.kotlin.ir.util.generateTypicalIrProviderList
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.CompilerEnvironment
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
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
class Fir2IrResultsConverter(
testServices: TestServices
) : Frontend2BackendConverter<FirOutputArtifact, IrBackendInput>(
testServices,
FrontendKinds.FIR,
BackendKinds.IrBackend
) {
override fun transform(
module: TestModule,
inputArtifact: FirOutputArtifact
): IrBackendInput {
val extensions = JvmGeneratorExtensions()
val (irModuleFragment, symbolTable, sourceManager, components) = inputArtifact.firAnalyzerFacade.convertToIr(extensions)
val dummyBindingContext = NoScopeRecordCliBindingTrace().bindingContext
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
val configuration = compilerConfigurationProvider.getCompilerConfiguration(module)
val phaseConfig = configuration.get(CLIConfigurationKeys.PHASE_CONFIG) ?: PhaseConfig(jvmPhases)
val codegenFactory = JvmIrCodegenFactory(phaseConfig)
// TODO: handle fir from light tree
val ktFiles = inputArtifact.firFiles.values.mapNotNull { it.psi as KtFile? }
// Create and initialize the module and its dependencies
val project = compilerConfigurationProvider.getProject(module)
val container = TopDownAnalyzerFacadeForJVM.createContainer(
project, ktFiles, NoScopeRecordCliBindingTrace(), configuration,
compilerConfigurationProvider.getPackagePartProviderFactory(module),
::FileBasedDeclarationProviderFactory, CompilerEnvironment,
TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles), emptyList()
)
val generationState = GenerationState.Builder(
project, ClassBuilderFactories.BINARIES,
container.get(), dummyBindingContext, ktFiles,
configuration
).codegenFactory(
codegenFactory
).isIrBackend(
true
).jvmBackendClassResolver(
FirJvmBackendClassResolver(components)
).build()
irModuleFragment.irBuiltins.functionFactory = IrFunctionFactory(irModuleFragment.irBuiltins, symbolTable)
val irProviders = generateTypicalIrProviderList(
irModuleFragment.descriptor, irModuleFragment.irBuiltins, symbolTable, extensions = extensions
)
return IrBackendInput(
generationState,
irModuleFragment,
symbolTable,
sourceManager,
phaseConfig,
irProviders,
extensions,
) { context, irClass, _, serializationBindings, parent ->
FirMetadataSerializer(inputArtifact.session, context, irClass, serializationBindings, components, parent)
}
}
}
@@ -0,0 +1,26 @@
/*
* 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.test.ExceptionFromTestError
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.moduleStructure
class FirFailingTestSuppressor(testServices: TestServices) : AfterAnalysisChecker(testServices) {
override fun suppressIfNeeded(failedAssertions: List<AssertionError>): List<AssertionError> {
val testFile = testServices.moduleStructure.originalTestDataFiles.first()
val failFile = testFile.parentFile.resolve("${testFile.nameWithoutExtension}.fail")
val exceptionFromFir = failedAssertions.firstOrNull { it is ExceptionFromTestError }
return when {
failFile.exists() && exceptionFromFir != null -> emptyList()
failFile.exists() && exceptionFromFir == null -> {
failedAssertions + AssertionError("Fail file exists but no exception was throw. Please remove ${failFile.name}")
}
else -> failedAssertions
}
}
}
@@ -0,0 +1,99 @@
/*
* 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 com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementFinder
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.ProjectScope
import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
import org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM
import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade
import org.jetbrains.kotlin.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.session.FirJvmModuleInfo
import org.jetbrains.kotlin.fir.session.FirSessionFactory
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.*
class FirFrontendFacade(
testServices: TestServices
) : FrontendFacade<FirOutputArtifact>(testServices, FrontendKinds.FIR) {
override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::FirModuleInfoProvider))
override fun analyze(module: TestModule): FirOutputArtifact {
val moduleInfoProvider = testServices.firModuleInfoProvider
val compilerConfigurationProvider = testServices.compilerConfigurationProvider
// TODO: add configurable parser
val project = compilerConfigurationProvider.getProject(module)
PsiElementFinder.EP.getPoint(project).unregisterExtension(JavaElementFinder::class.java)
val ktFiles = testServices.sourceFileProvider.getKtFilesForSourceFiles(module.files, project).values
val sessionProvider = moduleInfoProvider.firSessionProvider
val languageVersionSettings = module.languageVersionSettings
val builtinsModuleInfo = moduleInfoProvider.builtinsModuleInfoForModule(module)
val packagePartProviderFactory = compilerConfigurationProvider.getPackagePartProviderFactory(module)
createSessionForBuiltins(builtinsModuleInfo, sessionProvider, project, packagePartProviderFactory)
createSessionForBinaryDependencies(module, sessionProvider, project, packagePartProviderFactory)
val sourcesScope = TopDownAnalyzerFacadeForJVM.newModuleSearchScope(project, ktFiles)
val sourcesModuleInfo = moduleInfoProvider.convertToFirModuleInfo(module)
val session = FirSessionFactory.createJavaModuleBasedSession(
sourcesModuleInfo,
sessionProvider,
sourcesScope,
project,
languageVersionSettings = languageVersionSettings
)
val firAnalyzerFacade = FirAnalyzerFacade(session, languageVersionSettings, ktFiles)
val firFiles = firAnalyzerFacade.runResolution()
val filesMap = firFiles.mapNotNull { firFile ->
val testFile = module.files.firstOrNull { it.name == firFile.name } ?: return@mapNotNull null
testFile to firFile
}.toMap()
return FirOutputArtifact(session, filesMap, firAnalyzerFacade)
}
private fun createSessionForBuiltins(
builtinsModuleInfo: FirJvmModuleInfo,
sessionProvider: FirProjectSessionProvider,
project: Project,
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
) {
//For BuiltIns, registered in sessionProvider automatically
val allProjectScope = GlobalSearchScope.allScope(project)
FirSessionFactory.createLibrarySession(
builtinsModuleInfo, sessionProvider, allProjectScope, project,
packagePartProviderFactory(allProjectScope)
)
}
private fun createSessionForBinaryDependencies(
module: TestModule,
sessionProvider: FirProjectSessionProvider,
project: Project,
packagePartProviderFactory: (GlobalSearchScope) -> JvmPackagePartProvider,
) {
val librariesScope = ProjectScope.getLibrariesScope(project)
val librariesModuleInfo = FirJvmModuleInfo.createForLibraries(module.name)
FirSessionFactory.createLibrarySession(
librariesModuleInfo,
sessionProvider,
librariesScope,
project,
packagePartProviderFactory(librariesScope)
)
}
}
@@ -0,0 +1,44 @@
/*
* 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.fir.java.FirProjectSessionProvider
import org.jetbrains.kotlin.fir.session.FirJvmModuleInfo
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.test.services.TestService
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.dependencyProvider
import org.jetbrains.kotlin.test.model.TestModule
class FirModuleInfoProvider(
private val testServices: TestServices
) : TestService {
val firSessionProvider = FirProjectSessionProvider()
private val builtinsByModule: MutableMap<TestModule, FirJvmModuleInfo> = mutableMapOf()
private val firModuleInfoByModule: MutableMap<TestModule, FirJvmModuleInfo> = mutableMapOf()
fun convertToFirModuleInfo(module: TestModule): FirJvmModuleInfo {
return firModuleInfoByModule.getOrPut(module) {
val dependencies = mutableListOf(builtinsModuleInfoForModule(module))
module.dependencies.mapTo(dependencies) {
convertToFirModuleInfo(testServices.dependencyProvider.getTestModule(it.moduleName))
}
FirJvmModuleInfo(
module.name,
dependencies
)
}
}
fun builtinsModuleInfoForModule(module: TestModule): FirJvmModuleInfo {
return builtinsByModule.getOrPut(module) {
FirJvmModuleInfo(Name.special("<built-ins>"), emptyList())
}
}
}
val TestServices.firModuleInfoProvider: FirModuleInfoProvider by TestServices.testServiceAccessor()
@@ -0,0 +1,24 @@
/*
* 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.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.FirAnalyzerFacade
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.ResultingArtifact
import org.jetbrains.kotlin.test.model.TestFile
data class FirOutputArtifact(
val session: FirSession,
val allFirFiles: Map<TestFile, FirFile>,
val firAnalyzerFacade: FirAnalyzerFacade
) : ResultingArtifact.FrontendOutput<FirOutputArtifact>() {
override val kind: FrontendKinds.FIR
get() = FrontendKinds.FIR
val firFiles: Map<TestFile, FirFile> = allFirFiles.filterKeys { !it.isAdditional }
}
@@ -0,0 +1,19 @@
/*
* 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.handlers
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.FrontendOutputHandler
import org.jetbrains.kotlin.test.services.TestServices
import java.io.File
abstract class FirAnalysisHandler(
testServices: TestServices
) : FrontendOutputHandler<FirOutputArtifact>(testServices, FrontendKinds.FIR) {
protected val File.nameWithoutFirExtension: String
get() = nameWithoutExtension.removeSuffix(".fir")
}
@@ -0,0 +1,19 @@
/*
* 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.handlers
import org.jetbrains.kotlin.fir.AbstractFirDiagnosticsTest
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
class FirCfgConsistencyHandler(testServices: TestServices) : FirAnalysisHandler(testServices) {
override fun processModule(module: TestModule, info: FirOutputArtifact) {
info.firFiles.values.forEach { it.accept(AbstractFirDiagnosticsTest.CfgConsistencyChecker) }
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -0,0 +1,36 @@
/*
* 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.handlers
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.FirControlFlowGraphRenderVisitor
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.moduleStructure
// TODO: adapt to multifile and multimodule tests
class FirCfgDumpHandler(testServices: TestServices) : FirAnalysisHandler(testServices) {
override val directivesContainers: List<DirectivesContainer>
get() = listOf(FirDiagnosticsDirectives)
private val builder = StringBuilder()
private var alreadyDumped: Boolean = false
override fun processModule(module: TestModule, info: FirOutputArtifact) {
if (alreadyDumped || FirDiagnosticsDirectives.DUMP_CFG !in module.directives) return
val file = info.firFiles.values.first()
file.accept(FirControlFlowGraphRenderVisitor(builder))
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (!alreadyDumped) return
val testDataFile = testServices.moduleStructure.originalTestDataFiles.first()
val expectedFile = testDataFile.parentFile.resolve("${testDataFile.nameWithoutFirExtension}.dot")
assertions.assertEqualsToFile(expectedFile, builder.toString())
}
}
@@ -0,0 +1,90 @@
/*
* 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.handlers
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.codeMetaInfo.model.CodeMetaInfo
import org.jetbrains.kotlin.codeMetaInfo.renderConfigurations.AbstractCodeMetaInfoRenderConfiguration
import org.jetbrains.kotlin.diagnostics.PositioningStrategy
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDefaultErrorMessages
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnostic
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderer
import org.jetbrains.kotlin.fir.psi
object FirMetaInfoUtils {
val renderDiagnosticNoArgs = FirDiagnosticCodeMetaRenderConfiguration().apply { renderParams = false }
val renderDiagnosticWithArgs = FirDiagnosticCodeMetaRenderConfiguration().apply { renderParams = true }
}
class FirDiagnosticCodeMetaInfo(
val diagnostic: FirDiagnostic<*>,
renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration
) : CodeMetaInfo {
// TODO: this implementation is hacky and doesn't support proper ranges for light tree diagnostics
private val textRangeFromClassicDiagnostic: TextRange? = run {
val psi = diagnostic.element.psi ?: return@run null
@Suppress("UNCHECKED_CAST")
val positioningStrategy = diagnostic.factory.positioningStrategy.psiStrategy as PositioningStrategy<PsiElement>
positioningStrategy.mark(psi).first()
}
override var renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration = renderConfiguration
private set
override val start: Int
get() = textRangeFromClassicDiagnostic?.startOffset ?: diagnostic.element.startOffset
override val end: Int
get() = textRangeFromClassicDiagnostic?.endOffset ?: diagnostic.element.endOffset
override val tag: String
get() = renderConfiguration.getTag(this)
override val attributes: MutableList<String> = mutableListOf()
override fun asString(): String = renderConfiguration.asString(this)
fun replaceRenderConfiguration(renderConfiguration: FirDiagnosticCodeMetaRenderConfiguration) {
this.renderConfiguration = renderConfiguration
}
}
class FirDiagnosticCodeMetaRenderConfiguration(
val renderSeverity: Boolean = false,
) : AbstractCodeMetaInfoRenderConfiguration(renderParams = false) {
private val crossPlatformLineBreak = """\r?\n""".toRegex()
override fun asString(codeMetaInfo: CodeMetaInfo): String {
if (codeMetaInfo !is FirDiagnosticCodeMetaInfo) return ""
return (getTag(codeMetaInfo)
+ getPlatformsString(codeMetaInfo)
+ getParamsString(codeMetaInfo))
.replace(crossPlatformLineBreak, "")
}
private fun getParamsString(codeMetaInfo: FirDiagnosticCodeMetaInfo): String {
if (!renderParams) return ""
val params = mutableListOf<String>()
val diagnostic = codeMetaInfo.diagnostic
val renderer = FirDefaultErrorMessages.getRendererForDiagnostic(diagnostic) as FirDiagnosticRenderer<FirDiagnostic<*>>
params.add(renderer.render(diagnostic))
if (renderSeverity)
params.add("severity='${diagnostic.severity}'")
params.add(getAdditionalParams(codeMetaInfo))
return "(\"${params.filter { it.isNotEmpty() }.joinToString("; ")}\")"
}
fun getTag(codeMetaInfo: FirDiagnosticCodeMetaInfo): String {
return codeMetaInfo.diagnostic.factory.name
}
}
@@ -0,0 +1,232 @@
/*
* 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.handlers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.checkers.diagnostics.factories.DebugInfoDiagnosticFactory1
import org.jetbrains.kotlin.checkers.utils.TypeOfCall
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.resolve.AnalyzingUtils
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.addIfNotNull
class FirDiagnosticsHandler(testServices: TestServices) : FirAnalysisHandler(testServices) {
companion object {
private val allowedKindsForDebugInfo = setOf(
FirRealSourceElementKind,
FirFakeSourceElementKind.DesugaredCompoundAssignment,
)
}
private val globalMetadataInfoHandler: GlobalMetadataInfoHandler
get() = testServices.globalMetadataInfoHandler
private val diagnosticsService: DiagnosticsService
get() = testServices.diagnosticsService
override val directivesContainers: List<DirectivesContainer> =
listOf(DiagnosticsDirectives)
override val additionalServices: List<ServiceRegistrationData> =
listOf(service(::DiagnosticsService))
override fun processModule(module: TestModule, info: FirOutputArtifact) {
val diagnosticsPerFile = info.firAnalyzerFacade.runCheckers()
for (file in module.files) {
val firFile = info.firFiles[file] ?: continue
val diagnostics = diagnosticsPerFile[firFile] ?: continue
val diagnosticsMetadataInfos = diagnostics.mapNotNull { diagnostic ->
if (!diagnosticsService.shouldRenderDiagnostic(module, diagnostic.factory.name)) return@mapNotNull null
// SYNTAX errors will be reported later
if (diagnostic.factory == FirErrors.SYNTAX) return@mapNotNull null
if (!diagnostic.isValid) return@mapNotNull null
diagnostic.toMetaInfo(file)
}
globalMetadataInfoHandler.addMetadataInfosForFile(file, diagnosticsMetadataInfos)
collectSyntaxDiagnostics(file, firFile)
collectDebugInfoDiagnostics(file, firFile)
}
}
private fun FirDiagnostic<*>.toMetaInfo(file: TestFile, forceRenderArguments: Boolean = false): FirDiagnosticCodeMetaInfo {
val metaInfo = FirDiagnosticCodeMetaInfo(this, FirMetaInfoUtils.renderDiagnosticNoArgs)
val shouldRenderArguments = forceRenderArguments || globalMetadataInfoHandler.getExistingMetaInfosForActualMetadata(file, metaInfo)
.any { it.description != null }
if (shouldRenderArguments) {
metaInfo.replaceRenderConfiguration(FirMetaInfoUtils.renderDiagnosticWithArgs)
}
return metaInfo
}
private fun collectSyntaxDiagnostics(testFile: TestFile, firFile: FirFile) {
// TODO: support in light tree
val psiFile = firFile.psi ?: return
val metaInfos = AnalyzingUtils.getSyntaxErrorRanges(psiFile).map {
FirErrors.SYNTAX.on(FirRealPsiSourceElement(it)).toMetaInfo(testFile)
}
globalMetadataInfoHandler.addMetadataInfosForFile(testFile, metaInfos)
}
private fun collectDebugInfoDiagnostics(
testFile: TestFile,
firFile: FirFile,
) {
val result = mutableListOf<FirDiagnostic<*>>()
val diagnosedRangesToDiagnosticNames = globalMetadataInfoHandler.getExistingMetaInfosForFile(testFile).groupBy(
keySelector = { it.start..it.end },
valueTransform = { it.tag }
).mapValues { (_, it) -> it.toSet() }
object : FirDefaultVisitorVoid() {
override fun visitElement(element: FirElement) {
if (element is FirExpression) {
result.addIfNotNull(
createExpressionTypeDiagnosticIfExpected(
element, diagnosedRangesToDiagnosticNames
)
)
}
element.acceptChildren(this)
}
override fun visitFunctionCall(functionCall: FirFunctionCall) {
result.addIfNotNull(
createCallDiagnosticIfExpected(functionCall, functionCall.calleeReference, diagnosedRangesToDiagnosticNames)
)
super.visitFunctionCall(functionCall)
}
}.let(firFile::accept)
globalMetadataInfoHandler.addMetadataInfosForFile(testFile, result.map { it.toMetaInfo(testFile, forceRenderArguments = true) })
}
fun createExpressionTypeDiagnosticIfExpected(
element: FirExpression,
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>
): FirDiagnosticWithParameters1<FirSourceElement, String>? =
DebugInfoDiagnosticFactory1.EXPRESSION_TYPE.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) {
element.typeRef.renderAsString((element as? FirExpressionWithSmartcast)?.originalType)
}
private fun FirTypeRef.renderAsString(originalTypeRef: FirTypeRef?): String {
val type = coneTypeSafe<ConeKotlinType>() ?: return "Type is unknown"
val rendered = type.renderForDebugInfo()
val originalTypeRendered = originalTypeRef?.coneTypeSafe<ConeKotlinType>()?.renderForDebugInfo() ?: return rendered
return "$rendered & $originalTypeRendered"
}
private fun createCallDiagnosticIfExpected(
element: FirElement,
reference: FirNamedReference,
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>
): FirDiagnosticWithParameters1<FirSourceElement, String>? =
DebugInfoDiagnosticFactory1.CALL.createDebugInfoDiagnostic(element, diagnosedRangesToDiagnosticNames) {
val resolvedSymbol = (reference as? FirResolvedNamedReference)?.resolvedSymbol
val fqName = resolvedSymbol?.fqNameUnsafe()
Renderers.renderCallInfo(fqName, getTypeOfCall(reference, resolvedSymbol))
}
private inline fun DebugInfoDiagnosticFactory1.createDebugInfoDiagnostic(
element: FirElement,
diagnosedRangesToDiagnosticNames: Map<IntRange, Set<String>>,
argument: () -> String,
): FirDiagnosticWithParameters1<FirSourceElement, String>? {
val sourceElement = element.source ?: return null
if (sourceElement.kind !in allowedKindsForDebugInfo) return null
// Lambda argument is always (?) duplicated by function literal
// Block expression is always (?) duplicated by single block expression
if (sourceElement.elementType == KtNodeTypes.LAMBDA_ARGUMENT || sourceElement.elementType == KtNodeTypes.BLOCK) return null
if (diagnosedRangesToDiagnosticNames[sourceElement.startOffset..sourceElement.endOffset]?.contains(this.name) != true) return null
val argumentText = argument()
return when (sourceElement) {
is FirPsiSourceElement<*> -> FirPsiDiagnosticWithParameters1(
sourceElement,
argumentText,
severity,
FirDiagnosticFactory1(
name,
severity,
)
)
is FirLightSourceElement -> FirLightDiagnosticWithParameters1(
sourceElement,
argumentText,
severity,
FirDiagnosticFactory1<FirSourceElement, PsiElement, String>(
name,
severity
)
)
}
}
private fun getTypeOfCall(
reference: FirNamedReference,
resolvedSymbol: AbstractFirBasedSymbol<*>?
): String {
if (resolvedSymbol == null) return TypeOfCall.UNRESOLVED.nameToRender
if ((resolvedSymbol as? FirFunctionSymbol)?.callableId?.callableName == OperatorNameConventions.INVOKE
&& reference.name != OperatorNameConventions.INVOKE
) {
return TypeOfCall.VARIABLE_THROUGH_INVOKE.nameToRender
}
return when (val fir = resolvedSymbol.fir) {
is FirProperty -> {
TypeOfCall.PROPERTY_GETTER.nameToRender
}
is FirFunction<*> -> buildString {
if (fir is FirCallableMemberDeclaration<*>) {
if (fir.status.isInline) append("inline ")
if (fir.status.isInfix) append("infix ")
if (fir.status.isOperator) append("operator ")
if (fir.receiverTypeRef != null) append("extension ")
}
append(TypeOfCall.FUNCTION.nameToRender)
}
else -> TypeOfCall.OTHER.nameToRender
}
}
private fun AbstractFirBasedSymbol<*>.fqNameUnsafe(): FqNameUnsafe? = when (this) {
is FirClassLikeSymbol<*> -> classId.asSingleFqName().toUnsafe()
is FirCallableSymbol<*> -> callableId.asFqNameForDebugInfo().toUnsafe()
else -> null
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
}
@@ -0,0 +1,41 @@
/*
* 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.handlers
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumper
import org.jetbrains.kotlin.test.utils.MultiModuleInfoDumperImpl
class FirDumpHandler(
testServices: TestServices
) : FirAnalysisHandler(testServices) {
private val dumper: MultiModuleInfoDumper = MultiModuleInfoDumperImpl()
override val directivesContainers: List<DirectivesContainer>
get() = listOf(FirDiagnosticsDirectives)
override fun processModule(module: TestModule, info: FirOutputArtifact) {
if (FirDiagnosticsDirectives.FIR_DUMP !in module.directives) return
val builderForModule = dumper.builderForModule(module)
val firFiles = info.firFiles
firFiles.values.forEach { builderForModule.append(it.render()) }
}
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
if (dumper.isEmpty()) return
// TODO: change according to multiple testdata files
val testDataFile = testServices.moduleStructure.originalTestDataFiles.first()
val expectedFile = testDataFile.parentFile.resolve("${testDataFile.nameWithoutFirExtension}.fir.txt")
val actualText = dumper.generateResultingDump()
assertions.assertEqualsToFile(expectedFile, actualText, message = { "Content is not equal" })
}
}
@@ -0,0 +1,53 @@
/*
* 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.handlers
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.assertions
import org.jetbrains.kotlin.test.services.moduleStructure
import org.jetbrains.kotlin.test.utils.firTestDataFile
import org.jetbrains.kotlin.test.utils.isFirTestData
import org.jetbrains.kotlin.test.utils.originalTestDataFile
import java.io.File
class FirIdenticalChecker(testServices: TestServices) : AfterAnalysisChecker(testServices) {
override fun check(failedAssertions: List<AssertionError>) {
if (failedAssertions.isNotEmpty()) return
val file = testServices.moduleStructure.originalTestDataFiles.first()
if (file.isFirTestData) {
addDirectiveToClassicFile(file)
} else {
removeFirFileIfExist(file)
}
}
private fun addDirectiveToClassicFile(firFile: File) {
val classicFile = firFile.originalTestDataFile
val classicFileContent = classicFile.readText()
val firFileContent = firFile.readText()
if (classicFileContent == firFileContent) {
classicFile.writer().use {
it.appendLine("// ${FirDiagnosticsDirectives.FIR_IDENTICAL.name}")
it.append(classicFileContent)
}
firFile.delete()
testServices.assertions.fail {
"""
Dumps via FIR & via old FE are the same.
Deleted .fir.txt dump, added // FIR_IDENTICAL to test source
Please re-run the test now
""".trimIndent()
}
}
}
private fun removeFirFileIfExist(classicFile: File) {
val firFile = classicFile.firTestDataFile
firFile.delete()
}
}
@@ -0,0 +1,151 @@
/*
* 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.impl
import com.intellij.openapi.Disposable
import org.jetbrains.kotlin.test.Constructor
import org.jetbrains.kotlin.test.TestConfiguration
import org.jetbrains.kotlin.test.directives.model.ComposedDirectivesContainer
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.impl.ModuleStructureExtractorImpl
import org.jetbrains.kotlin.test.utils.TestDisposable
class TestConfigurationImpl(
defaultsProvider: DefaultsProvider,
assertions: Assertions,
facades: List<Constructor<AbstractTestFacade<*, *>>>,
analysisHandlers: List<Constructor<AnalysisHandler<*>>>,
sourcePreprocessors: List<Constructor<SourceFilePreprocessor>>,
additionalMetaInfoProcessors: List<Constructor<AdditionalMetaInfoProcessor>>,
environmentConfigurators: List<Constructor<EnvironmentConfigurator>>,
additionalSourceProviders: List<Constructor<AdditionalSourceProvider>>,
metaTestConfigurators: List<Constructor<MetaTestConfigurator>>,
afterAnalysisCheckers: List<Constructor<AfterAnalysisChecker>>,
override val metaInfoHandlerEnabled: Boolean,
directives: List<DirectivesContainer>,
override val defaultRegisteredDirectives: RegisteredDirectives
) : TestConfiguration() {
override val rootDisposable: Disposable = TestDisposable()
override val testServices: TestServices = TestServices()
private val allDirectives = directives.toMutableSet()
override val directives: DirectivesContainer by lazy {
when (allDirectives.size) {
0 -> DirectivesContainer.Empty
1 -> allDirectives.single()
else -> ComposedDirectivesContainer(allDirectives)
}
}
override val moduleStructureExtractor: ModuleStructureExtractor = ModuleStructureExtractorImpl(
testServices,
additionalSourceProviders.map { it.invoke(testServices) }.also {
it.flatMapTo(allDirectives) { provider -> provider.directives }
}
)
override val metaTestConfigurators: List<MetaTestConfigurator> = metaTestConfigurators.map {
it.invoke(testServices).also { configurator ->
allDirectives += configurator.directives
}
}
override val afterAnalysisCheckers: List<AfterAnalysisChecker> = afterAnalysisCheckers.map {
it.invoke(testServices).also { checker ->
allDirectives += checker.directives
}
}
init {
testServices.apply {
@OptIn(ExperimentalStdlibApi::class)
val sourceFilePreprocessors = sourcePreprocessors.map { it.invoke(this@apply) }
val sourceFileProvider = SourceFileProviderImpl(sourceFilePreprocessors)
register(SourceFileProvider::class, sourceFileProvider)
val configurators = environmentConfigurators.map { it.invoke(this) }
configurators.flatMapTo(allDirectives) { it.directivesContainers }
for (configurator in configurators) {
configurator.additionalServices.forEach { register(it) }
}
val environmentProvider = CompilerConfigurationProviderImpl(
rootDisposable,
configurators
)
register(CompilerConfigurationProvider::class, environmentProvider)
register(Assertions::class, assertions)
register(DefaultsProvider::class, defaultsProvider)
register(DefaultRegisteredDirectivesProvider::class, DefaultRegisteredDirectivesProvider(defaultRegisteredDirectives))
val metaInfoProcessors = additionalMetaInfoProcessors.map { it.invoke(this) }
register(GlobalMetadataInfoHandler::class, GlobalMetadataInfoHandler(this, metaInfoProcessors))
}
}
private val facades: Map<TestArtifactKind<*>, Map<TestArtifactKind<*>, AbstractTestFacade<*, *>>> =
facades
.map { it.invoke(testServices) }
.groupBy { it.inputKind }
.mapValues { (frontendKind, converters) ->
converters.groupBy { it.outputKind }.mapValues {
it.value.singleOrNull() ?: manyFacadesError("converters", "$frontendKind -> ${it.key}")
}
}
private val analysisHandlers: Map<TestArtifactKind<*>, List<AnalysisHandler<*>>> =
analysisHandlers.map { it.invoke(testServices).also(this::registerDirectivesAndServices) }
.groupBy { it.artifactKind }
.withDefault { emptyList() }
private fun manyFacadesError(name: String, kinds: String): Nothing {
error("Too many $name passed for $kinds configuration")
}
private fun registerDirectivesAndServices(handler: AnalysisHandler<*>) {
allDirectives += handler.directivesContainers
testServices.register(handler.additionalServices)
}
init {
testServices.apply {
this@TestConfigurationImpl.facades.values.forEach { it.values.forEach { facade -> register(facade.additionalServices) } }
}
}
override fun <I : ResultingArtifact<I>, O : ResultingArtifact<O>> getFacade(
inputKind: TestArtifactKind<I>,
outputKind: TestArtifactKind<O>
): AbstractTestFacade<I, O> {
@Suppress("UNCHECKED_CAST")
return facades[inputKind]?.get(outputKind) as AbstractTestFacade<I, O>?
?: facadeNotFoundError(inputKind, outputKind)
}
private fun facadeNotFoundError(from: Any, to: Any): Nothing {
error("Facade for converting '$from' to '$to' not found")
}
override fun <A : ResultingArtifact<A>> getHandlers(artifactKind: TestArtifactKind<A>): List<AnalysisHandler<A>> {
@Suppress("UNCHECKED_CAST")
return analysisHandlers.getValue(artifactKind) as List<AnalysisHandler<A>>
}
override fun getAllHandlers(): List<AnalysisHandler<*>> {
return analysisHandlers.values.flatten()
}
}
@@ -0,0 +1,30 @@
/*
* 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.model
import org.jetbrains.kotlin.codegen.ClassFileFactory
object BinaryArtifacts {
class Jvm(val classFileFactory: ClassFileFactory) : ResultingArtifact.Binary<Jvm>() {
override val kind: BinaryKind<Jvm>
get() = ArtifactKinds.Jvm
}
class Js : ResultingArtifact.Binary<Js>() {
override val kind: BinaryKind<Js>
get() = ArtifactKinds.Js
}
class Native : ResultingArtifact.Binary<Native>() {
override val kind: BinaryKind<Native>
get() = ArtifactKinds.Native
}
class KLib : ResultingArtifact.Binary<KLib>() {
override val kind: BinaryKind<KLib>
get() = ArtifactKinds.KLib
}
}
@@ -0,0 +1,54 @@
/*
* 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.model
import org.jetbrains.kotlin.test.backend.classic.ClassicBackendInput
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.frontend.classic.ClassicFrontendOutputArtifact
import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact
object FrontendKinds {
object ClassicFrontend : FrontendKind<ClassicFrontendOutputArtifact>("ClassicFrontend")
object FIR : FrontendKind<FirOutputArtifact>("FIR")
fun fromString(string: String): FrontendKind<*>? {
return when (string) {
"ClassicFrontend" -> ClassicFrontend
"FIR" -> FIR
else -> null
}
}
}
object BackendKinds {
object ClassicBackend : BackendKind<ClassicBackendInput>("ClassicBackend")
object IrBackend : BackendKind<IrBackendInput>("IrBackend")
fun fromString(string: String): BackendKind<*>? {
return when (string) {
"ClassicBackend" -> ClassicBackend
"IrBackend" -> IrBackend
else -> null
}
}
}
object ArtifactKinds {
object Jvm : BinaryKind<BinaryArtifacts.Jvm>("JVM")
object Js : BinaryKind<BinaryArtifacts.Js>("JS")
object Native : BinaryKind<BinaryArtifacts.Native>("Native")
object KLib : BinaryKind<BinaryArtifacts.KLib>("KLib")
fun fromString(string: String): BinaryKind<*>? {
return when (string) {
"Jvm" -> Jvm
"Js" -> Js
"Native" -> Native
"KLib" -> KLib
else -> null
}
}
}
@@ -0,0 +1,17 @@
/*
* 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.preprocessors
import org.jetbrains.kotlin.codeMetaInfo.clearTextFromDiagnosticMarkup
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.services.SourceFilePreprocessor
import org.jetbrains.kotlin.test.services.TestServices
class MetaInfosCleanupPreprocessor(testServices: TestServices) : SourceFilePreprocessor(testServices) {
override fun process(file: TestFile, content: String): String {
return clearTextFromDiagnosticMarkup(content)
}
}
@@ -0,0 +1,32 @@
/*
* 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.services
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import java.io.File
class AdditionalDiagnosticsSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
companion object {
private const val HELPERS_PATH = "./compiler/testData/diagnostics/helpers"
private const val CHECK_TYPE_PATH = "$HELPERS_PATH/types/checkType.kt"
}
override val directives: List<DirectivesContainer> =
listOf(AdditionalFilesDirectives)
@OptIn(ExperimentalStdlibApi::class)
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
return buildList {
if (containsDirective(globalDirectives, module, AdditionalFilesDirectives.CHECK_TYPE)) {
add(File(CHECK_TYPE_PATH).toTestFile())
}
}
}
}
@@ -0,0 +1,100 @@
/*
* 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.services
import com.intellij.mock.MockProject
import com.intellij.openapi.Disposable
import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.JvmPackagePartProvider
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import java.io.File
abstract class CompilerConfigurationProvider : TestService {
abstract val testRootDisposable: Disposable
protected abstract fun getKotlinCoreEnvironment(module: TestModule): KotlinCoreEnvironment
fun getProject(module: TestModule): Project {
return getKotlinCoreEnvironment(module).project
}
fun getPackagePartProviderFactory(module: TestModule): (GlobalSearchScope) -> JvmPackagePartProvider {
return getKotlinCoreEnvironment(module)::createPackagePartProvider
}
fun getCompilerConfiguration(module: TestModule): CompilerConfiguration {
return getKotlinCoreEnvironment(module).configuration
}
fun registerJavacForModule(module: TestModule, ktFiles: List<KtFile>, mockJdk: File?) {
val environment = getKotlinCoreEnvironment(module)
val bootClasspath = mockJdk?.let { listOf(it) }
environment.registerJavac(kotlinFiles = ktFiles, bootClasspath = bootClasspath)
}
}
val TestServices.compilerConfigurationProvider: CompilerConfigurationProvider by TestServices.testServiceAccessor()
class CompilerConfigurationProviderImpl(
override val testRootDisposable: Disposable,
val configurators: List<EnvironmentConfigurator>
) : CompilerConfigurationProvider() {
private val cache: MutableMap<TestModule, KotlinCoreEnvironment> = mutableMapOf()
override fun getKotlinCoreEnvironment(module: TestModule): KotlinCoreEnvironment {
return cache.getOrPut(module) {
val projectEnv = KotlinCoreEnvironment.createProjectEnvironmentForTests(testRootDisposable, CompilerConfiguration())
KotlinCoreEnvironment.createForTests(
projectEnv,
createCompilerConfiguration(module, projectEnv.project),
EnvironmentConfigFiles.JVM_CONFIG_FILES
)
}
}
private fun createCompilerConfiguration(module: TestModule, project: MockProject): CompilerConfiguration {
val configuration = CompilerConfiguration()
configuration[CommonConfigurationKeys.MODULE_NAME] = module.name
configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] = object : MessageCollector {
override fun clear() {}
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) {
if (severity == CompilerMessageSeverity.ERROR) {
val prefix = if (location == null) "" else "(" + location.path + ":" + location.line + ":" + location.column + ") "
throw AssertionError(prefix + message)
}
}
override fun hasErrors(): Boolean = false
}
configuration.languageVersionSettings = module.languageVersionSettings
configurators.forEach { it.configureCompilerConfiguration(configuration, module, project) }
return configuration
}
private operator fun <T : Any> CompilerConfiguration.set(key: CompilerConfigurationKey<T>, value: T) {
put(key, value)
}
}
val TestModule.javaFiles: List<TestFile>
get() = files.filter { it.isJavaFile }
@@ -0,0 +1,42 @@
/*
* 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.services
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.CHECK_STATE_MACHINE
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.CHECK_TAIL_CALL_OPTIMIZATION
import org.jetbrains.kotlin.test.directives.AdditionalFilesDirectives.WITH_COROUTINES
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import java.io.File
class CoroutineHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
companion object {
private const val HELPERS_PATH = "./compiler/testData/diagnostics/helpers/coroutines"
private const val COROUTINE_HELPERS_PATH = "$HELPERS_PATH/CoroutineHelpers.kt"
private const val STATE_MACHINE_CHECKER_PATH = "$HELPERS_PATH/StateMachineChecker.kt"
private const val TAIL_CALL_OPTIMIZATION_CHECKER_PATH = "$HELPERS_PATH/TailCallOptimizationChecker.kt"
}
override val directives: List<DirectivesContainer> =
listOf(AdditionalFilesDirectives)
@OptIn(ExperimentalStdlibApi::class)
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
if (WITH_COROUTINES !in module.directives) return emptyList()
return buildList {
add(File(COROUTINE_HELPERS_PATH).toTestFile())
if (CHECK_STATE_MACHINE in module.directives) {
add(File(STATE_MACHINE_CHECKER_PATH).toTestFile())
}
if (CHECK_TAIL_CALL_OPTIMIZATION in module.directives) {
add(File(TAIL_CALL_OPTIMIZATION_CHECKER_PATH).toTestFile())
}
}
}
}
@@ -0,0 +1,49 @@
/*
* 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.services
import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.util.*
class DiagnosticsService(val testServices: TestServices) : TestService {
private val conditionsPerModule: MutableMap<TestModule, Condition<String>> = mutableMapOf()
private val globalDefinedDiagnostics by lazy {
testServices.moduleStructure.allDirectives[DiagnosticsDirectives.DIAGNOSTICS]
}
fun shouldRenderDiagnostic(module: TestModule, name: String): Boolean {
val condition = conditionsPerModule.getOrPut(module) {
computeDiagnosticConditionForModule(module)
}
return condition(name)
}
private fun computeDiagnosticConditionForModule(module: TestModule): Condition<String> {
val diagnosticsInDirective = module.directives[DiagnosticsDirectives.DIAGNOSTICS] + globalDefinedDiagnostics
val enabledNames = mutableSetOf<String>()
val disabledNames = mutableSetOf<String>()
for (diagnosticInDirective in diagnosticsInDirective) {
val enabled = when {
diagnosticInDirective.startsWith("+") -> true
diagnosticInDirective.startsWith("-") -> false
else -> error("Incorrect diagnostics directive syntax. See reference:\n${DiagnosticsDirectives.DIAGNOSTICS.description}")
}
val name = diagnosticInDirective.substring(1)
val collection = if (enabled) enabledNames else disabledNames
collection += name
}
if (disabledNames.isEmpty()) return Conditions.alwaysTrue()
var condition = !Conditions.oneOf(disabledNames)
if (enabledNames.isNotEmpty()) {
condition = condition or Conditions.oneOf(enabledNames)
}
return condition.cached()
}
}
val TestServices.diagnosticsService: DiagnosticsService by TestServices.testServiceAccessor()
@@ -0,0 +1,62 @@
/*
* 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.services
import com.intellij.rt.execution.junit.FileComparisonFailure
import org.jetbrains.kotlin.test.util.convertLineSeparators
import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF
import org.jetbrains.kotlin.utils.rethrow
import org.junit.jupiter.api.function.Executable
import java.io.File
import java.io.IOException
import org.junit.jupiter.api.Assertions as JUnit5PlatformAssertions
object JUnit5Assertions : Assertions() {
override fun assertEqualsToFile(expectedFile: File, actual: String, sanitizer: (String) -> String, message: () -> String) {
try {
val actualText = actual.trim { it <= ' ' }.convertLineSeparators().trimTrailingWhitespacesAndAddNewlineAtEOF()
if (!expectedFile.exists()) {
expectedFile.writeText(actualText)
org.junit.jupiter.api.fail("Expected data file did not exist. Generating: $expectedFile")
}
val expected = expectedFile.readText().convertLineSeparators()
val expectedText = expected.trim { it <= ' ' }.trimTrailingWhitespacesAndAddNewlineAtEOF()
if (sanitizer.invoke(expectedText) != sanitizer.invoke(actualText)) {
throw FileComparisonFailure(
"${message()}: ${expectedFile.name}",
expected, actual, expectedFile.absolutePath
)
}
} catch (e: IOException) {
throw rethrow(e)
}
}
override fun assertEquals(expected: Any?, actual: Any?, message: (() -> String)?) {
JUnit5PlatformAssertions.assertEquals(expected, actual, message?.invoke())
}
override fun assertNotEquals(expected: Any?, actual: Any?, message: (() -> String)?) {
JUnit5PlatformAssertions.assertNotEquals(expected, actual, message?.invoke())
}
override fun assertTrue(value: Boolean, message: (() -> String)?) {
JUnit5PlatformAssertions.assertTrue(value, message?.invoke())
}
override fun assertFalse(value: Boolean, message: (() -> String)?) {
JUnit5PlatformAssertions.assertFalse(value, message?.invoke())
}
override fun assertAll(exceptions: List<AssertionError>) {
exceptions.singleOrNull()?.let { throw it }
JUnit5PlatformAssertions.assertAll(exceptions.map { Executable { throw it } })
}
override fun fail(message: () -> String): Nothing {
org.junit.jupiter.api.fail(message)
}
}
@@ -0,0 +1,36 @@
/*
* 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.services.configuration
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.util.joinToArrayString
class JsEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
override val directivesContainers: List<DirectivesContainer>
get() = listOf(JsEnvironmentConfigurationDirectives)
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
val moduleKinds = module.directives[JsEnvironmentConfigurationDirectives.MODULE_KIND]
val moduleKind = when (moduleKinds.size) {
0 -> ModuleKind.PLAIN
1 -> moduleKinds.single()
else -> error("Too many module kinds passed ${moduleKinds.joinToArrayString()}")
}
configuration.put(JSConfigurationKeys.MODULE_KIND, moduleKind)
configuration.put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB)
}
}
@@ -0,0 +1,162 @@
/*
* 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.services.configuration
import com.intellij.mock.MockProject
import com.intellij.openapi.util.SystemInfo
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.BackendKinds
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.jvm.CompiledJarManager
import org.jetbrains.kotlin.test.services.jvm.compiledJarManager
import org.jetbrains.kotlin.test.util.KtTestUtil
import org.jetbrains.kotlin.test.util.joinToArrayString
import java.io.File
class JvmEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
override val directivesContainers: List<DirectivesContainer>
get() = listOf(JvmEnvironmentConfigurationDirectives)
override val additionalServices: List<ServiceRegistrationData>
get() = listOf(service(::CompiledJarManager))
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
if (module.targetPlatform !in JvmPlatforms.allJvmPlatforms) return
val registeredDirectives = module.allRegisteredDirectives
val targets = registeredDirectives[JvmEnvironmentConfigurationDirectives.JVM_TARGET]
when (targets.size) {
0 -> {}
1 -> configuration.put(JVMConfigurationKeys.JVM_TARGET, targets.single())
else -> error("Too many jvm targets passed: ${targets.joinToArrayString()}")
}
when (extractJdkKind(registeredDirectives)) {
TestJdkKind.MOCK_JDK -> {
configuration.addJvmClasspathRoot(KtTestUtil.findMockJdkRtJar())
configuration.put(JVMConfigurationKeys.NO_JDK, true)
}
TestJdkKind.MODIFIED_MOCK_JDK -> {
configuration.addJvmClasspathRoot(KtTestUtil.findMockJdkRtModified())
configuration.put(JVMConfigurationKeys.NO_JDK, true)
}
TestJdkKind.FULL_JDK_6 -> {
val jdk6 = System.getenv("JDK_16") ?: error("Environment variable JDK_16 is not set")
configuration.put(JVMConfigurationKeys.JDK_HOME, File(jdk6))
}
TestJdkKind.FULL_JDK_9 -> {
configuration.put(JVMConfigurationKeys.JDK_HOME, KtTestUtil.getJdk9Home())
}
TestJdkKind.FULL_JDK -> {
if (SystemInfo.IS_AT_LEAST_JAVA9) {
configuration.put(JVMConfigurationKeys.JDK_HOME, File(System.getProperty("java.home")))
}
}
TestJdkKind.ANDROID_API -> {
configuration.addJvmClasspathRoot(KtTestUtil.findAndroidApiJar())
configuration.put(JVMConfigurationKeys.NO_JDK, true)
}
}
val configurationKind = extractConfigurationKind(registeredDirectives)
if (configurationKind.withRuntime) {
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTests())
configuration.addJvmClasspathRoot(ForTestCompileRuntime.scriptRuntimeJarForTests())
configuration.addJvmClasspathRoot(ForTestCompileRuntime.kotlinTestJarForTests())
} else if (configurationKind.withMockRuntime) {
configuration.addJvmClasspathRoot(ForTestCompileRuntime.minimalRuntimeJarForTests())
configuration.addJvmClasspathRoot(ForTestCompileRuntime.scriptRuntimeJarForTests())
}
if (configurationKind.withReflection) {
configuration.addJvmClasspathRoot(ForTestCompileRuntime.reflectJarForTests())
}
configuration.addJvmClasspathRoot(KtTestUtil.getAnnotationsJar())
if (JvmEnvironmentConfigurationDirectives.STDLIB_JDK8 in module.directives) {
configuration.addJvmClasspathRoot(ForTestCompileRuntime.runtimeJarForTestsWithJdk8())
}
if (JvmEnvironmentConfigurationDirectives.ANDROID_ANNOTATIONS in module.directives) {
configuration.addJvmClasspathRoot(ForTestCompileRuntime.androidAnnotationsForTests())
}
val isIr = module.backendKind == BackendKinds.IrBackend
configuration.put(JVMConfigurationKeys.IR, isIr)
module.javaFiles.takeIf { it.isNotEmpty() }?.let { javaFiles ->
javaFiles.forEach { testServices.sourceFileProvider.getRealFileForSourceFile(it) }
configuration.addJavaSourceRoot(testServices.sourceFileProvider.javaSourceDirectory)
}
configuration.registerModuleDependencies(module)
if (JvmEnvironmentConfigurationDirectives.USE_PSI_CLASS_FILES_READING in module.directives) {
configuration.put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, true)
}
}
private fun extractJdkKind(registeredDirectives: RegisteredDirectives): TestJdkKind {
val fullJdkEnabled = JvmEnvironmentConfigurationDirectives.FULL_JDK in registeredDirectives
val jdkKinds = registeredDirectives[JvmEnvironmentConfigurationDirectives.JDK_KIND]
if (fullJdkEnabled) {
if (jdkKinds.isNotEmpty()) {
error("FULL_JDK and JDK_KIND can not be used together")
}
return TestJdkKind.FULL_JDK
}
return when (jdkKinds.size) {
0 -> TestJdkKind.MOCK_JDK
1 -> jdkKinds.single()
else -> error("Too many jdk kinds passed: ${jdkKinds.joinToArrayString()}")
}
}
private fun extractConfigurationKind(registeredDirectives: RegisteredDirectives): ConfigurationKind {
val withRuntime = JvmEnvironmentConfigurationDirectives.WITH_RUNTIME in registeredDirectives || JvmEnvironmentConfigurationDirectives.WITH_STDLIB in registeredDirectives
val withReflect = JvmEnvironmentConfigurationDirectives.WITH_REFLECT in registeredDirectives
val noRuntime = JvmEnvironmentConfigurationDirectives.NO_RUNTIME in registeredDirectives
if (noRuntime && withRuntime) {
error("NO_RUNTIME and WITH_RUNTIME can not be used together")
}
if (withReflect && !withRuntime) {
error("WITH_REFLECT may be used only with WITH_RUNTIME")
}
return when {
withRuntime && withReflect -> ConfigurationKind.ALL
withRuntime -> ConfigurationKind.NO_KOTLIN_REFLECT
noRuntime -> ConfigurationKind.JDK_NO_RUNTIME
else -> ConfigurationKind.JDK_ONLY
}
}
private fun CompilerConfiguration.registerModuleDependencies(module: TestModule) {
val dependencyProvider = testServices.dependencyProvider
val modulesFromDependencies = module.dependencies
.filter { it.kind == DependencyKind.Binary }
.map { dependencyProvider.getTestModule(it.moduleName) }
.takeIf { it.isNotEmpty() }
?: return
val jarManager = testServices.compiledJarManager
val dependenciesClassPath = modulesFromDependencies.map { jarManager.getCompiledJarForModule(it) }
addJvmClasspathRoots(dependenciesClassPath)
}
}
@@ -0,0 +1,22 @@
/*
* 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.services.configuration
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.script.loadScriptingPlugin
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.isKtsFile
class ScriptingEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule, project: MockProject) {
if (module.files.any { it.isKtsFile }) {
loadScriptingPlugin(configuration)
}
}
}
@@ -0,0 +1,28 @@
/*
* 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.services.fir
import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives
import org.jetbrains.kotlin.test.services.MetaTestConfigurator
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.utils.firTestDataFile
import java.io.File
class FirOldFrontendMetaConfigurator(testServices: TestServices) : MetaTestConfigurator(testServices) {
override fun transformTestDataPath(testDataFileName: String): String {
val originalFile = File(testDataFileName)
val isFirIdentical = originalFile.useLines { it.first() == "// ${FirDiagnosticsDirectives.FIR_IDENTICAL.name}" }
return if (isFirIdentical) {
testDataFileName
} else {
val firFile = originalFile.firTestDataFile
if (!firFile.exists()) {
originalFile.copyTo(firFile)
}
firFile.absolutePath
}
}
}
@@ -0,0 +1,326 @@
/*
* 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.services.impl
import org.jetbrains.kotlin.platform.CommonPlatforms
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.test.builders.LanguageVersionSettingsBuilder
import org.jetbrains.kotlin.test.directives.ModuleStructureDirectives
import org.jetbrains.kotlin.test.directives.model.ComposedRegisteredDirectives
import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.util.joinToArrayString
import org.jetbrains.kotlin.utils.DFS
import java.io.File
class ModuleStructureExtractorImpl(
testServices: TestServices,
additionalSourceProviders: List<AdditionalSourceProvider>
) : ModuleStructureExtractor(testServices, additionalSourceProviders) {
companion object {
private val allowedExtensionsForFiles = listOf(".kt", ".kts", ".java")
private val moduleDirectiveRegex = """([\w-]+)(\((.*)\))?""".toRegex()
}
override fun splitTestDataByModules(
testDataFileName: String,
directivesContainer: DirectivesContainer,
): TestModuleStructure {
val testDataFile = File(testDataFileName)
val extractor = ModuleStructureExtractorWorker(listOf(testDataFile), directivesContainer)
return extractor.splitTestDataByModules()
}
private inner class ModuleStructureExtractorWorker constructor(
private val testDataFiles: List<File>,
private val directivesContainer: DirectivesContainer,
) {
private val assertions: Assertions
get() = testServices.assertions
private val defaultsProvider: DefaultsProvider
get() = testServices.defaultsProvider
private lateinit var currentTestDataFile: File
private val defaultFileName: String
get() = currentTestDataFile.name
private val defaultModuleName: String
get() = "main"
private var currentModuleName: String? = null
private var currentModuleTargetPlatform: TargetPlatform? = null
private var currentModuleFrontendKind: FrontendKind<*>? = null
private var currentModuleBackendKind: BackendKind<*>? = null
private var currentModuleLanguageVersionSettingsBuilder: LanguageVersionSettingsBuilder = initLanguageSettingsBuilder()
private var dependenciesOfCurrentModule = mutableListOf<DependencyDescription>()
private var filesOfCurrentModule = mutableListOf<TestFile>()
private var currentFileName: String? = null
private var firstFileInModule: Boolean = true
private var linesOfCurrentFile = mutableListOf<String>()
private var startLineNumberOfCurrentFile = 0
private var directivesBuilder = RegisteredDirectivesParser(directivesContainer, assertions)
private var globalDirectives: RegisteredDirectives? = null
private val modules = mutableListOf<TestModule>()
private val moduleDirectiveBuilder = RegisteredDirectivesParser(ModuleStructureDirectives, assertions)
fun splitTestDataByModules(): TestModuleStructure {
for (testDataFile in testDataFiles) {
currentTestDataFile = testDataFile
val lines = testDataFile.readLines()
lines.forEachIndexed { lineNumber, line ->
val rawDirective = RegisteredDirectivesParser.parseDirective(line)
if (tryParseStructureDirective(rawDirective, lineNumber + 1)) {
linesOfCurrentFile.add(line)
return@forEachIndexed
}
tryParseRegularDirective(rawDirective)
linesOfCurrentFile.add(line)
}
}
finishModule()
val sortedModules = sortModules(modules)
checkCycles(modules)
return TestModuleStructureImpl(sortedModules, testDataFiles)
}
private fun sortModules(modules: List<TestModule>): List<TestModule> {
val moduleByName = modules.groupBy { it.name }.mapValues { (name, modules) ->
modules.singleOrNull() ?: error("Duplicated modules with name $name")
}
return DFS.topologicalOrder(modules) { module ->
module.dependencies.map {
val moduleName = it.moduleName
moduleByName[moduleName] ?: error("Module \"$moduleName\" not found while observing dependencies of \"${module.name}\"")
}
}.asReversed()
}
private fun checkCycles(modules: List<TestModule>) {
val visited = mutableSetOf<String>()
for (module in modules) {
val moduleName = module.name
visited.add(moduleName)
for (dependency in module.dependencies) {
val dependencyName = dependency.moduleName
if (dependencyName == moduleName) {
error("Module $moduleName has dependency to itself")
}
if (dependencyName !in visited) {
error("There is cycle in modules dependencies. See modules: $dependencyName, $moduleName")
}
}
}
}
/*
* returns [true] means that passed directive was module directive and line is processed
*/
private fun tryParseStructureDirective(rawDirective: RegisteredDirectivesParser.RawDirective?, lineNumber: Int): Boolean {
if (rawDirective == null) return false
val (directive, values) = moduleDirectiveBuilder.convertToRegisteredDirective(rawDirective) ?: return false
when (directive) {
ModuleStructureDirectives.MODULE -> {
/*
* There was previous module, so we should save it
*/
if (currentModuleName != null) {
finishModule()
} else {
finishGlobalDirectives()
}
val (moduleName, dependencies) = splitRawModuleStringToNameAndDependencies(values.joinToString(separator = " "))
currentModuleName = moduleName
dependencies.mapTo(dependenciesOfCurrentModule) { name ->
val kind = defaultsProvider.defaultDependencyKind
DependencyDescription(name, kind, DependencyRelation.Dependency)
}
}
ModuleStructureDirectives.DEPENDENCY,
ModuleStructureDirectives.DEPENDS_ON -> {
val name = values.first() as String
val kind = values.getOrNull(1)?.let { valueOfOrNull(it as String) } ?: defaultsProvider.defaultDependencyKind
val relation = when (directive) {
ModuleStructureDirectives.DEPENDENCY -> DependencyRelation.Dependency
ModuleStructureDirectives.DEPENDS_ON -> DependencyRelation.DependsOn
else -> error("Should not be here")
}
dependenciesOfCurrentModule.add(DependencyDescription(name, kind, relation))
}
ModuleStructureDirectives.TARGET_FRONTEND -> {
val name = values.singleOrNull() as? String? ?: assertions.fail {
"Target frontend specified incorrectly\nUsage: ${directive.description}"
}
currentModuleFrontendKind = FrontendKinds.fromString(name) ?: assertions.fail {
"Unknown frontend: $name"
}
}
ModuleStructureDirectives.TARGET_BACKEND_KIND -> {
val name = values.singleOrNull() as? String ?: assertions.fail {
"Target backend specified incorrectly\nUsage: ${directive.description}"
}
currentModuleBackendKind = BackendKinds.fromString(name) ?: assertions.fail {
"Unknown backend: $name"
}
}
ModuleStructureDirectives.FILE -> {
if (currentFileName != null) {
finishFile()
} else {
resetFileCaches()
}
currentFileName = (values.first() as String).also(::validateFileName)
startLineNumberOfCurrentFile = lineNumber
}
else -> return false
}
return true
}
private fun splitRawModuleStringToNameAndDependencies(moduleDirectiveString: String): Pair<String, List<String>> {
val matchResult = moduleDirectiveRegex.matchEntire(moduleDirectiveString)
?: error("\"$moduleDirectiveString\" doesn't matches with pattern \"moduleName(dep1, dep2)\"")
val (name, _, dependencies) = matchResult.destructured
if (dependencies.isBlank()) return name to emptyList()
return name to dependencies.split(" ")
}
private fun finishGlobalDirectives() {
globalDirectives = directivesBuilder.build()
resetModuleCaches()
resetFileCaches()
}
private fun finishModule() {
finishFile()
val moduleDirectives = directivesBuilder.build() + testServices.defaultDirectives + globalDirectives
currentModuleLanguageVersionSettingsBuilder.configureUsingDirectives(moduleDirectives)
val moduleName = currentModuleName ?: defaultModuleName
val testModule = TestModule(
name = moduleName,
targetPlatform = currentModuleTargetPlatform ?: parseModulePlatformByName(moduleName) ?: defaultsProvider.defaultPlatform,
frontendKind = currentModuleFrontendKind ?: defaultsProvider.defaultFrontend,
backendKind = currentModuleBackendKind ?: defaultsProvider.defaultBackend,
files = filesOfCurrentModule,
dependencies = dependenciesOfCurrentModule,
directives = moduleDirectives,
languageVersionSettings = currentModuleLanguageVersionSettingsBuilder.build()
)
modules += testModule
additionalSourceProviders.flatMapTo(filesOfCurrentModule) { additionalSourceProvider ->
additionalSourceProvider.produceAdditionalFiles(
globalDirectives ?: RegisteredDirectives.Empty,
testModule
).also { additionalFiles ->
require(additionalFiles.all { it.isAdditional }) {
"Files produced by ${additionalSourceProvider::class.qualifiedName} should have flag `isAdditional = true`"
}
}
}
firstFileInModule = true
resetModuleCaches()
}
private fun parseModulePlatformByName(moduleName: String): TargetPlatform? {
val nameSuffix = moduleName.substringAfterLast("-", "").toUpperCase()
return when {
nameSuffix == "COMMON" -> CommonPlatforms.defaultCommonPlatform
nameSuffix == "JVM" -> JvmPlatforms.unspecifiedJvmPlatform // TODO(dsavvinov): determine JvmTarget precisely
nameSuffix == "JS" -> JsPlatforms.defaultJsPlatform
nameSuffix == "NATIVE" -> NativePlatforms.unspecifiedNativePlatform
nameSuffix.isEmpty() -> null // TODO(dsavvinov): this leads to 'null'-platform in ModuleDescriptor
else -> throw IllegalStateException("Can't determine platform by name $nameSuffix")
}
}
private fun finishFile() {
val filename = currentFileName ?: defaultFileName
if (filesOfCurrentModule.any { it.name == filename }) {
error("File with name \"$filename\" already defined in module ${currentModuleName ?: defaultModuleName}")
}
filesOfCurrentModule.add(
TestFile(
relativePath = filename,
originalContent = linesOfCurrentFile.joinToString(separator = System.lineSeparator(), postfix = System.lineSeparator()),
originalFile = currentTestDataFile,
startLineNumberInOriginalFile = startLineNumberOfCurrentFile,
isAdditional = false
)
)
firstFileInModule = false
resetFileCaches()
}
private fun resetModuleCaches() {
firstFileInModule = true
currentModuleName = null
currentModuleTargetPlatform = null
currentModuleFrontendKind = null
currentModuleBackendKind = null
currentModuleLanguageVersionSettingsBuilder = initLanguageSettingsBuilder()
filesOfCurrentModule = mutableListOf()
dependenciesOfCurrentModule = mutableListOf()
directivesBuilder = RegisteredDirectivesParser(directivesContainer, assertions)
}
private fun resetFileCaches() {
if (!firstFileInModule) {
linesOfCurrentFile = mutableListOf()
}
currentFileName = null
startLineNumberOfCurrentFile = 0
}
private fun tryParseRegularDirective(rawDirective: RegisteredDirectivesParser.RawDirective?) {
if (rawDirective == null) return
val parsedDirective = directivesBuilder.convertToRegisteredDirective(rawDirective) ?: return
directivesBuilder.addParsedDirective(parsedDirective)
}
private fun validateFileName(fileName: String) {
if (!allowedExtensionsForFiles.any { fileName.endsWith(it) }) {
assertions.fail {
"Filename $fileName is not valid. Allowed extensions: ${allowedExtensionsForFiles.joinToArrayString()}"
}
}
}
private fun initLanguageSettingsBuilder(): LanguageVersionSettingsBuilder {
return defaultsProvider.newLanguageSettingsBuilder()
}
}
}
private operator fun RegisteredDirectives.plus(other: RegisteredDirectives?): RegisteredDirectives {
return when {
other == null -> this
other.isEmpty() -> this
this.isEmpty() -> other
else -> ComposedRegisteredDirectives(this, other)
}
}
inline fun <reified T : Enum<T>> valueOfOrNull(value: String): T? {
for (enumValue in enumValues<T>()) {
if (enumValue.name == value) {
return enumValue
}
}
return null
}
@@ -0,0 +1,62 @@
/*
* 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.services.impl
import org.jetbrains.kotlin.platform.TargetPlatform
import org.jetbrains.kotlin.platform.js.JsPlatforms
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.platform.konan.NativePlatforms
import org.jetbrains.kotlin.test.directives.model.ComposedRegisteredDirectives
import org.jetbrains.kotlin.test.directives.model.RegisteredDirectives
import org.jetbrains.kotlin.test.model.BinaryKind
import org.jetbrains.kotlin.test.model.ArtifactKinds
import org.jetbrains.kotlin.test.model.DependencyKind
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestModuleStructure
import java.io.File
class TestModuleStructureImpl(
override val modules: List<TestModule>,
override val originalTestDataFiles: List<File>
) : TestModuleStructure() {
override val allDirectives: RegisteredDirectives = ComposedRegisteredDirectives(modules.map { it.directives })
@OptIn(ExperimentalStdlibApi::class)
private val targetArtifactsByModule: Map<String, List<BinaryKind<*>>> = buildMap {
for (module in modules) {
val result = mutableListOf<BinaryKind<*>>()
for (dependency in module.dependencies) {
if (dependency.kind == DependencyKind.KLib) {
result += ArtifactKinds.KLib
}
}
module.targetPlatform.toArtifactKind()?.let { result += it }
put(module.name, result)
}
}
override fun getTargetArtifactKinds(module: TestModule): List<BinaryKind<*>> {
return targetArtifactsByModule[module.name] ?: emptyList()
}
override fun toString(): String {
return buildString {
modules.forEach {
appendLine(it)
appendLine()
}
}
}
companion object {
private fun TargetPlatform.toArtifactKind(): BinaryKind<*>? = when (this) {
in JvmPlatforms.allJvmPlatforms -> ArtifactKinds.Jvm
in JsPlatforms.allJsPlatforms -> ArtifactKinds.Js
in NativePlatforms.allNativePlatforms -> ArtifactKinds.Native
else -> null
}
}
}
@@ -0,0 +1,37 @@
/*
* 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.services.jvm
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.output.writeAll
import org.jetbrains.kotlin.test.model.ArtifactKinds
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.compilerConfigurationProvider
import org.jetbrains.kotlin.test.services.dependencyProvider
import java.io.File
import java.nio.file.Files
class CompiledJarManager(val testServices: TestServices) : TestService {
private val jarCache = mutableMapOf<TestModule, File>()
fun getCompiledJarForModule(module: TestModule): File {
return jarCache.getOrPut(module) {
val outputDir = Files.createTempDirectory("module_${module.name}").toFile()
val classFileFactory = testServices.dependencyProvider.getArtifact(module, ArtifactKinds.Jvm).classFileFactory
val outputFileCollection = SimpleOutputFileCollection(classFileFactory.currentOutput)
val messageCollector = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
outputFileCollection.writeAll(outputDir, messageCollector, reportOutputFiles = false)
classFileFactory.releaseGeneratedOutput()
outputDir
}
}
}
val TestServices.compiledJarManager: CompiledJarManager by TestServices.testServiceAccessor()
@@ -0,0 +1,28 @@
/*
* 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.utils
import java.io.File
private const val FIR_KT = ".fir.kt"
private const val KT = ".kt"
val File.isFirTestData: Boolean
get() = name.endsWith(FIR_KT)
val File.originalTestDataFile: File
get() = if (isFirTestData) {
parentFile.resolve("${name.removeSuffix(FIR_KT)}$KT")
} else {
this
}
val File.firTestDataFile: File
get() = if (isFirTestData) {
this
} else {
parentFile.resolve("${name.removeSuffix(KT)}$FIR_KT")
}
@@ -0,0 +1,37 @@
/*
* 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.utils
import org.jetbrains.kotlin.test.model.TestModule
abstract class MultiModuleInfoDumper {
abstract fun builderForModule(module: TestModule): StringBuilder
abstract fun generateResultingDump(): String
abstract fun isEmpty(): Boolean
}
// TODO: consider about tests with multiple testdata files
class MultiModuleInfoDumperImpl(private val moduleHeaderTemplate: String = "Module: %s") : MultiModuleInfoDumper() {
private val builderByModule = LinkedHashMap<TestModule, StringBuilder>()
override fun builderForModule(module: TestModule): StringBuilder {
return builderByModule.getOrPut(module, ::StringBuilder)
}
override fun generateResultingDump(): String {
builderByModule.values.singleOrNull()?.let { return it.toString() }
return buildString {
for ((module, builder) in builderByModule) {
appendLine(String.format(moduleHeaderTemplate, module.name))
append(builder)
}
}
}
override fun isEmpty(): Boolean {
return builderByModule.isEmpty()
}
}
@@ -0,0 +1,18 @@
/*
* 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.utils
import com.intellij.openapi.Disposable
class TestDisposable : Disposable {
@Volatile
var isDisposed = false
private set
override fun dispose() {
isDisposed = true
}
}
@@ -295,7 +295,7 @@ abstract class AbstractFirDiagnosticsTest : AbstractFirBaseDiagnosticsTest() {
firFiles.forEach { it.accept(CfgConsistencyChecker) }
}
private object CfgConsistencyChecker : FirVisitorVoid() {
object CfgConsistencyChecker : FirVisitorVoid() {
override fun visitElement(element: FirElement) {
element.acceptChildren(this)
}
@@ -52,8 +52,8 @@ import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
import org.jetbrains.kotlin.test.util.JetTestUtilsKt;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.util.StringUtilsKt;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
import org.junit.Assert;
@@ -291,7 +291,7 @@ public class KotlinTestUtils {
public static void assertEqualsToFile(@NotNull String message, @NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
try {
String actualText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
String actualText = StringUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
if (!expectedFile.exists()) {
FileUtil.writeToFile(expectedFile, actualText);
@@ -299,7 +299,7 @@ public class KotlinTestUtils {
}
String expected = FileUtil.loadFile(expectedFile, CharsetToolkit.UTF8, true);
String expectedText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
String expectedText = StringUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) {
throw new FileComparisonFailure(message + ": " + expectedFile.getName(),
@@ -24,14 +24,6 @@ import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtPackageDirective
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
fun String.trimTrailingWhitespaces(): String =
this.split('\n').joinToString(separator = "\n") { it.trimEnd() }
fun String.trimTrailingWhitespacesAndAddNewlineAtEOF(): String =
this.trimTrailingWhitespaces().let {
result -> if (result.endsWith("\n")) result else result + "\n"
}
fun PsiFile.findElementByCommentPrefix(commentText: String): PsiElement? =
findElementsByCommentPrefix(commentText).keys.singleOrNull()
@@ -15,7 +15,7 @@ abstract class JsPlatform : SimplePlatform("JS") {
@Suppress("DEPRECATION_ERROR")
object JsPlatforms {
private object DefaultSimpleJsPlatform : JsPlatform()
object DefaultSimpleJsPlatform : JsPlatform()
@Deprecated(
message = "Should be accessed only by compatibility layer, other clients should use 'defaultJsPlatform'",
+2 -1
View File
@@ -327,7 +327,8 @@ include ":compiler:fir:cones",
":compiler:fir:analysis-tests"
include ":compiler:test-infrastructure",
":compiler:test-infrastructure-utils"
":compiler:test-infrastructure-utils",
":compiler:tests-common-new"
include ":idea:idea-frontend-fir:idea-fir-low-level-api"
include ":idea:idea-fir-performance-tests"