[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:
@@ -458,6 +458,20 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
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
|
// used in the daemon for jar cache cleanup
|
||||||
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
||||||
|
|
||||||
|
|||||||
@@ -456,6 +456,20 @@ class KotlinCoreEnvironment private constructor(
|
|||||||
return KotlinCoreEnvironment(projectEnv, configuration, extensionConfigs)
|
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
|
// used in the daemon for jar cache cleanup
|
||||||
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
val applicationEnvironment: KotlinCoreApplicationEnvironment? get() = ourApplicationEnvironment
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ abstract class JvmPlatform : SimplePlatform("JVM") {
|
|||||||
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
@Suppress("DEPRECATION_ERROR")
|
||||||
object JvmPlatforms {
|
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> =
|
private val jvmTargetToJdkPlatform: Map<JvmTarget, TargetPlatform> =
|
||||||
JvmTarget.values().map { it to JdkPlatform(it).toTargetPlatform() }.toMap()
|
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.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.GeneratorExtensions
|
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 firFiles: List<FirFile>? = null
|
||||||
private var scopeSession: ScopeSession? = null
|
private var scopeSession: ScopeSession? = null
|
||||||
private var collectedDiagnostics: Map<FirFile, List<FirDiagnostic<*>>>? = 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 {
|
companion object {
|
||||||
val LIBRARIES_MODULE_NAME = Name.special("<dependencies>")
|
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)
|
constructor(moduleName: String, dependencies: List<ModuleInfo>) : this(Name.identifier(moduleName), dependencies)
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ object CheckerTestUtil {
|
|||||||
return diagnostics
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDebugInfoDiagnostics(
|
fun getDebugInfoDiagnostics(
|
||||||
root: PsiElement,
|
root: PsiElement,
|
||||||
bindingContext: BindingContext,
|
bindingContext: BindingContext,
|
||||||
markDynamicCalls: Boolean,
|
markDynamicCalls: Boolean,
|
||||||
|
|||||||
+4
@@ -36,4 +36,8 @@ public abstract class DiagnosticFactoryWithPsiElement<E extends PsiElement, D ex
|
|||||||
protected boolean isValid(ParametrizedDiagnostic<E> diagnostic) {
|
protected boolean isValid(ParametrizedDiagnostic<E> diagnostic) {
|
||||||
return positioningStrategy.isValid(diagnostic.getPsiElement());
|
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,
|
state: GenerationState,
|
||||||
irModuleFragment: IrModuleFragment,
|
irModuleFragment: IrModuleFragment,
|
||||||
symbolTable: SymbolTable,
|
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)
|
||||||
|
}
|
||||||
+12
@@ -7,3 +7,15 @@ package org.jetbrains.kotlin.test.util
|
|||||||
|
|
||||||
fun Iterable<*>.joinToArrayString(): String = joinToString(separator = ", ", prefix = "[", postfix = "]")
|
fun Iterable<*>.joinToArrayString(): String = joinToString(separator = ", ", prefix = "[", postfix = "]")
|
||||||
fun Array<*>.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()
|
||||||
+68
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -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)
|
||||||
+26
@@ -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
|
||||||
|
}
|
||||||
+41
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -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)
|
||||||
+109
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
@@ -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)
|
||||||
+32
@@ -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
|
||||||
|
}
|
||||||
+36
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+45
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+150
@@ -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)
|
||||||
|
}
|
||||||
+9
@@ -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
|
||||||
|
|
||||||
+12
@@ -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))
|
||||||
|
}
|
||||||
+40
@@ -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()
|
||||||
|
)
|
||||||
|
}
|
||||||
+15
@@ -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"
|
||||||
|
)
|
||||||
|
}
|
||||||
+49
@@ -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()
|
||||||
|
)
|
||||||
|
}
|
||||||
+27
@@ -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"
|
||||||
|
)
|
||||||
|
}
|
||||||
+13
@@ -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")
|
||||||
|
}
|
||||||
+36
@@ -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")
|
||||||
|
}
|
||||||
+32
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+158
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+263
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -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 }
|
||||||
|
}
|
||||||
+32
@@ -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()
|
||||||
+253
@@ -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
|
||||||
|
}
|
||||||
+17
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
+13
@@ -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 }
|
||||||
|
}
|
||||||
+136
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+96
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+99
@@ -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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -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()
|
||||||
+24
@@ -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 }
|
||||||
|
}
|
||||||
+19
@@ -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")
|
||||||
|
}
|
||||||
+19
@@ -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) {}
|
||||||
|
}
|
||||||
+36
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+232
@@ -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) {}
|
||||||
|
}
|
||||||
+41
@@ -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" })
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
+151
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+17
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+100
@@ -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 }
|
||||||
+42
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -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()
|
||||||
+62
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+162
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+22
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+326
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
+62
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -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")
|
||||||
|
}
|
||||||
+37
@@ -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) }
|
firFiles.forEach { it.accept(CfgConsistencyChecker) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private object CfgConsistencyChecker : FirVisitorVoid() {
|
object CfgConsistencyChecker : FirVisitorVoid() {
|
||||||
override fun visitElement(element: FirElement) {
|
override fun visitElement(element: FirElement) {
|
||||||
element.acceptChildren(this)
|
element.acceptChildren(this)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ import org.jetbrains.kotlin.psi.KtFile;
|
|||||||
import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
|
import org.jetbrains.kotlin.psi.KtPsiFactoryKt;
|
||||||
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil;
|
||||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager;
|
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.KtTestUtil;
|
||||||
|
import org.jetbrains.kotlin.test.util.StringUtilsKt;
|
||||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||||
import org.junit.Assert;
|
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) {
|
public static void assertEqualsToFile(@NotNull String message, @NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
|
||||||
try {
|
try {
|
||||||
String actualText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
String actualText = StringUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
||||||
|
|
||||||
if (!expectedFile.exists()) {
|
if (!expectedFile.exists()) {
|
||||||
FileUtil.writeToFile(expectedFile, actualText);
|
FileUtil.writeToFile(expectedFile, actualText);
|
||||||
@@ -299,7 +299,7 @@ public class KotlinTestUtils {
|
|||||||
}
|
}
|
||||||
String expected = FileUtil.loadFile(expectedFile, CharsetToolkit.UTF8, true);
|
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))) {
|
if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) {
|
||||||
throw new FileComparisonFailure(message + ": " + expectedFile.getName(),
|
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.KtPackageDirective
|
||||||
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
|
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? =
|
fun PsiFile.findElementByCommentPrefix(commentText: String): PsiElement? =
|
||||||
findElementsByCommentPrefix(commentText).keys.singleOrNull()
|
findElementsByCommentPrefix(commentText).keys.singleOrNull()
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ abstract class JsPlatform : SimplePlatform("JS") {
|
|||||||
|
|
||||||
@Suppress("DEPRECATION_ERROR")
|
@Suppress("DEPRECATION_ERROR")
|
||||||
object JsPlatforms {
|
object JsPlatforms {
|
||||||
private object DefaultSimpleJsPlatform : JsPlatform()
|
object DefaultSimpleJsPlatform : JsPlatform()
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
message = "Should be accessed only by compatibility layer, other clients should use 'defaultJsPlatform'",
|
message = "Should be accessed only by compatibility layer, other clients should use 'defaultJsPlatform'",
|
||||||
|
|||||||
+2
-1
@@ -327,7 +327,8 @@ include ":compiler:fir:cones",
|
|||||||
":compiler:fir:analysis-tests"
|
":compiler:fir:analysis-tests"
|
||||||
|
|
||||||
include ":compiler:test-infrastructure",
|
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-frontend-fir:idea-fir-low-level-api"
|
||||||
include ":idea:idea-fir-performance-tests"
|
include ":idea:idea-fir-performance-tests"
|
||||||
|
|||||||
Reference in New Issue
Block a user