Modularized tests. Support parsing of original compiler arguments

Now testData contains info about original compiler arguments, which can
be reused to support plugins and various other arguments

Support original arguments in modularized tests
- Properly support JDK9 modules
- Opt-ins from original arguments
- jdkHome from module data or arguments, as well as noJdk option
- Fix friendPaths not configured
- jvmDefault now handled properly

^KT-56075
^KT-55879
This commit is contained in:
Simon Ogorodnik
2023-02-01 20:32:58 +01:00
committed by Space Team
parent 36c3d62ba1
commit 59e69481a3
7 changed files with 193 additions and 59 deletions
@@ -5,34 +5,96 @@
package org.jetbrains.kotlin.fir package org.jetbrains.kotlin.fir
import com.intellij.psi.PsiJavaModule
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.cli.jvm.config.JvmModulePathRoot
import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots import org.jetbrains.kotlin.cli.jvm.config.configureJdkClasspathRoots
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
abstract class AbstractFrontendModularizedTest : AbstractModularizedTest() { abstract class AbstractFrontendModularizedTest : AbstractModularizedTest() {
fun createDefaultConfiguration(moduleData: ModuleData): CompilerConfiguration {
fun configureLanguageVersionSettings(
configuration: CompilerConfiguration,
moduleData: ModuleData,
languageVersion: LanguageVersion,
configureFlags: MutableMap<AnalysisFlag<*>, Any?>.() -> Unit = {},
configureFeatures: MutableMap<LanguageFeature, LanguageFeature.State>.() -> Unit = {}
) {
val originalArguments = moduleData.arguments as? K2JVMCompilerArguments
configuration.languageVersionSettings = LanguageVersionSettingsImpl(
languageVersion,
originalArguments?.apiVersion?.let { ApiVersion.parse(it) } ?: ApiVersion.LATEST_STABLE,
analysisFlags = buildMap {
put(AnalysisFlags.optIn, moduleData.optInAnnotations + originalArguments?.optIn.orEmpty())
if (originalArguments != null) {
put(AnalysisFlags.skipPrereleaseCheck, originalArguments.skipPrereleaseCheck)
put(JvmAnalysisFlags.jvmDefaultMode, JvmDefaultMode.fromStringOrNull(originalArguments.jvmDefault))
}
configureFlags()
},
specificFeatures = buildMap {
configureFeatures()
}
)
}
fun createDefaultConfiguration(
moduleData: ModuleData
): CompilerConfiguration {
val configuration = KotlinTestUtils.newConfiguration() val configuration = KotlinTestUtils.newConfiguration()
val originalArguments = moduleData.arguments as? K2JVMCompilerArguments
moduleData.javaSourceRoots.forEach { moduleData.javaSourceRoots.forEach {
configuration.addJavaSourceRoot(it.path, it.packagePrefix) configuration.addJavaSourceRoot(it.path, it.packagePrefix)
} }
configuration.addJvmClasspathRoots(moduleData.classpath)
configuration.languageVersionSettings = LanguageVersionSettingsImpl(
LanguageVersion.LATEST_STABLE,
ApiVersion.LATEST_STABLE,
analysisFlags = mutableMapOf(AnalysisFlags.optIn to moduleData.optInAnnotations)
)
configuration.configureJdkClasspathRoots()
// in case of modular jdk only val isJava9Module = moduleData.javaSourceRoots.any { (file, packagePrefix) ->
configuration.putIfNotNull(JVMConfigurationKeys.JDK_HOME, moduleData.modularJdkRoot) packagePrefix == null &&
(file.name == PsiJavaModule.MODULE_INFO_FILE ||
(file.isDirectory && file.listFiles()!!.any { it.name == PsiJavaModule.MODULE_INFO_FILE }))
}
for (rootPath in moduleData.classpath) {
if (isJava9Module) {
configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(rootPath))
}
configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmClasspathRoot(rootPath))
}
val jdkHome =
moduleData.modularJdkRoot
?: moduleData.jdkHome?.absoluteFile
?: originalArguments?.jdkHome?.fixPath()?.absoluteFile
if (originalArguments != null) {
configuration.put(JVMConfigurationKeys.NO_JDK, originalArguments.noJdk)
for (modularRoot in originalArguments.javaModulePath?.split(File.pathSeparatorChar).orEmpty()) {
configuration.add(CLIConfigurationKeys.CONTENT_ROOTS, JvmModulePathRoot(modularRoot.fixPath()))
}
configuration.put(
JVMConfigurationKeys.JVM_TARGET,
originalArguments.jvmTargetIfSupported() ?: JvmTarget.DEFAULT
)
}
configuration.putIfNotNull(JVMConfigurationKeys.JDK_HOME, jdkHome)
configuration.configureJdkClasspathRoots()
configuration.addAll( configuration.addAll(
CLIConfigurationKeys.CONTENT_ROOTS, CLIConfigurationKeys.CONTENT_ROOTS,
moduleData.sources.filter { it.extension == "kt" || it.isDirectory }.map { KotlinSourceRoot(it.absolutePath, false) }) moduleData.sources.filter { it.extension == "kt" || it.isDirectory }.map { KotlinSourceRoot(it.absolutePath, false) })
configuration.addAll(JVMConfigurationKeys.FRIEND_PATHS, moduleData.friendDirs.map { it.absolutePath })
return configuration return configuration
} }
} }
@@ -128,10 +128,27 @@ abstract class AbstractFullPipelineModularizedTest : AbstractModularizedTest() {
} }
private fun configureBaseArguments(args: K2JVMCompilerArguments, moduleData: ModuleData, tmp: Path) { private fun configureBaseArguments(args: K2JVMCompilerArguments, moduleData: ModuleData, tmp: Path) {
val originalArguments = moduleData.arguments as? K2JVMCompilerArguments
if (originalArguments != null) {
args.apiVersion = originalArguments.apiVersion
args.noJdk = originalArguments.noJdk
args.noStdlib = originalArguments.noStdlib
args.noReflect = originalArguments.noReflect
args.jvmTarget = originalArguments.jvmTargetIfSupported()?.description
args.jsr305 = originalArguments.jsr305
args.nullabilityAnnotations = originalArguments.nullabilityAnnotations
args.jspecifyAnnotations = originalArguments.jspecifyAnnotations
args.jvmDefault = originalArguments.jvmDefault
args.jdkRelease = originalArguments.jdkRelease
args.progressiveMode = originalArguments.progressiveMode
args.optIn = (moduleData.optInAnnotations + (originalArguments.optIn ?: emptyArray())).toTypedArray()
args.allowKotlinPackage = originalArguments.allowKotlinPackage
} else {
args.jvmTarget = JVM_TARGET
args.allowKotlinPackage = true
}
args.reportPerf = true args.reportPerf = true
args.jvmTarget = JVM_TARGET args.jdkHome = moduleData.jdkHome?.absolutePath ?: originalArguments?.jdkHome?.fixPath()?.absolutePath
args.allowKotlinPackage = true
args.jdkHome = moduleData.jdkHome?.absolutePath
args.renderInternalDiagnosticNames = true args.renderInternalDiagnosticNames = true
configureArgsUsingBuildFile(args, moduleData, tmp) configureArgsUsingBuildFile(args, moduleData, tmp)
} }
@@ -6,7 +6,11 @@
package org.jetbrains.kotlin.fir package org.jetbrains.kotlin.fir
import com.intellij.openapi.util.JDOMUtil import com.intellij.openapi.util.JDOMUtil
import com.intellij.util.xmlb.XmlSerializer
import org.jdom.Element import org.jdom.Element
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
@@ -37,11 +41,16 @@ data class ModuleData(
val friendDirs = rawFriendDirs.map { it.fixPath() } val friendDirs = rawFriendDirs.map { it.fixPath() }
val jdkHome = rawJdkHome?.fixPath() val jdkHome = rawJdkHome?.fixPath()
val modularJdkRoot = rawModularJdkRoot?.fixPath() val modularJdkRoot = rawModularJdkRoot?.fixPath()
/**
* Raw compiler arguments, as it was passed to original module build
*/
var arguments: CommonCompilerArguments? = null
} }
data class JavaSourceRootData<Path : Any>(val path: Path, val packagePrefix: String?) data class JavaSourceRootData<Path : Any>(val path: Path, val packagePrefix: String?)
private fun String.fixPath(): File = File(ROOT_PATH_PREFIX, this.removePrefix("/")) internal fun String.fixPath(): File = File(ROOT_PATH_PREFIX, this.removePrefix("/"))
private val ROOT_PATH_PREFIX:String = System.getProperty("fir.bench.prefix", "/") private val ROOT_PATH_PREFIX:String = System.getProperty("fir.bench.prefix", "/")
private val OUTPUT_DIR_REGEX_FILTER:String = System.getProperty("fir.bench.filter", ".*") private val OUTPUT_DIR_REGEX_FILTER:String = System.getProperty("fir.bench.filter", ".*")
@@ -136,7 +145,16 @@ abstract class AbstractModularizedTest : KtUsefulTestCase() {
private fun loadModuleDumpFile(file: File): List<ModuleData> { private fun loadModuleDumpFile(file: File): List<ModuleData> {
val rootElement = JDOMUtil.load(file) val rootElement = JDOMUtil.load(file)
val modules = rootElement.getChildren("module") val modules = rootElement.getChildren("module")
return modules.map { node -> loadModule(node) } val arguments = rootElement.getChild("compilerArguments")?.let { loadCompilerArguments(it) }
return modules.map { node -> loadModule(node).also { it.arguments = arguments } }
}
private fun loadCompilerArguments(argumentsRoot: Element): CommonCompilerArguments? {
val element = argumentsRoot.children.singleOrNull() ?: return null
return when (element.name) {
"K2JVMCompilerArguments" -> K2JVMCompilerArguments().also { XmlSerializer.deserializeInto(it, element) }
else -> null
}
} }
protected abstract fun beforePass(pass: Int) protected abstract fun beforePass(pass: Int)
@@ -172,3 +190,10 @@ abstract class AbstractModularizedTest : KtUsefulTestCase() {
afterPass(pass) afterPass(pass)
} }
} }
internal fun K2JVMCompilerArguments.jvmTargetIfSupported(): JvmTarget? {
val specified = jvmTarget?.let { JvmTarget.fromString(it) } ?: return null
if (specified != JvmTarget.JVM_1_6) return specified
return null
}
@@ -7,21 +7,30 @@ package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
/**
* Language version to be used for K1 FP tests
*/
internal val LANGUAGE_VERSION_K1: String = System.getProperty("fir.bench.language.version.k1", "1.8")
class FE1FullPipelineModularizedTest : AbstractFullPipelineModularizedTest() { class FE1FullPipelineModularizedTest : AbstractFullPipelineModularizedTest() {
override fun configureArguments(args: K2JVMCompilerArguments, moduleData: ModuleData) { override fun configureArguments(args: K2JVMCompilerArguments, moduleData: ModuleData) {
args.useK2 = false args.useK2 = false
args.useIR = true args.useIR = true
args.jvmDefault = "compatibility" args.languageVersion = LANGUAGE_VERSION_K1
args.apiVersion = API_VERSION // TODO: Remove when support for old modularized tests is removed
args.optIn = arrayOf( if (moduleData.arguments == null) {
"kotlin.RequiresOptIn", args.jvmDefault = "compatibility"
"kotlin.contracts.ExperimentalContracts", args.apiVersion = API_VERSION
"kotlin.io.path.ExperimentalPathApi", args.optIn = arrayOf(
"org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI" "kotlin.RequiresOptIn",
) "kotlin.contracts.ExperimentalContracts",
args.multiPlatform = true "kotlin.io.path.ExperimentalPathApi",
args.noStdlib = true "org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI"
args.noReflect = true )
args.multiPlatform = true
args.noStdlib = true
args.noReflect = true
}
} }
fun testTotalKotlin() { fun testTotalKotlin() {
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.asJava.finder.JavaElementFinder
import org.jetbrains.kotlin.cli.common.toBooleanLenient import org.jetbrains.kotlin.cli.common.toBooleanLenient
import org.jetbrains.kotlin.cli.jvm.compiler.* import org.jetbrains.kotlin.cli.jvm.compiler.*
import org.jetbrains.kotlin.cli.jvm.compiler.pipeline.collectSources import org.jetbrains.kotlin.cli.jvm.compiler.pipeline.collectSources
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector
@@ -213,6 +214,7 @@ class FirResolveModularizedTotalKotlinTest : AbstractFrontendModularizedTest() {
override fun processModule(moduleData: ModuleData): ProcessorAction { override fun processModule(moduleData: ModuleData): ProcessorAction {
val disposable = Disposer.newDisposable() val disposable = Disposer.newDisposable()
val configuration = createDefaultConfiguration(moduleData) val configuration = createDefaultConfiguration(moduleData)
configureLanguageVersionSettings(configuration, moduleData, LanguageVersion.fromVersionString(LANGUAGE_VERSION_K2)!!)
val environment = KotlinCoreEnvironment.createForTests(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForTests(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
PsiElementFinder.EP.getPoint(environment.project) PsiElementFinder.EP.getPoint(environment.project)
@@ -8,11 +8,11 @@ package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
// This is used for API version configuration for both frontends // This is used for API version configuration for both frontends
// TODO: rename system property // TODO: Deprecated and only used in old FP tests
internal val API_VERSION: String = System.getProperty("fir.bench.language.version", "1.4") internal val API_VERSION: String = System.getProperty("fir.bench.language.version", "1.4")
// This is used for language version configuration for K2 only. K1 always uses default version // This is used for language version configuration for K2 only. K1 uses LANGUAGE_VERSION_K1
private val LANGUAGE_VERSION_K2: String = System.getProperty("fir.bench.language.version.k2", "2.0") internal val LANGUAGE_VERSION_K2: String = System.getProperty("fir.bench.language.version.k2", "2.0")
class FullPipelineModularizedTest : AbstractFullPipelineModularizedTest() { class FullPipelineModularizedTest : AbstractFullPipelineModularizedTest() {
@@ -20,16 +20,19 @@ class FullPipelineModularizedTest : AbstractFullPipelineModularizedTest() {
args.useK2 = true args.useK2 = true
args.useIR = true args.useIR = true
args.languageVersion = LANGUAGE_VERSION_K2 args.languageVersion = LANGUAGE_VERSION_K2
args.apiVersion = API_VERSION // TODO: Remove when support for old modularized tests is removed
args.jvmDefault = "compatibility" if (moduleData.arguments == null) {
args.optIn = moduleData.optInAnnotations.toTypedArray() + arrayOf( args.apiVersion = API_VERSION
"kotlin.RequiresOptIn", args.jvmDefault = "compatibility"
"kotlin.contracts.ExperimentalContracts", args.optIn = moduleData.optInAnnotations.toTypedArray() + arrayOf(
"kotlin.io.path.ExperimentalPathApi", "kotlin.RequiresOptIn",
"org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI" "kotlin.contracts.ExperimentalContracts",
) "kotlin.io.path.ExperimentalPathApi",
args.noStdlib = true "org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI"
args.noReflect = true )
args.noStdlib = true
args.noReflect = true
}
} }
fun testTotalKotlin() { fun testTotalKotlin() {
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir package org.jetbrains.kotlin.fir
import com.intellij.openapi.Disposable
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY
@@ -16,6 +17,9 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler import org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler
import org.jetbrains.kotlin.config.* import org.jetbrains.kotlin.config.*
import org.jetbrains.kotlin.fir.scopes.ProcessorAction import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.FlexibleTypeImpl
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.PrintStream import java.io.PrintStream
import kotlin.system.measureNanoTime import kotlin.system.measureNanoTime
@@ -67,28 +71,28 @@ class NonFirResolveModularizedTotalKotlinTest : AbstractFrontendModularizedTest(
writeMessageToLog("$message: ${time * 1e-6} ms") writeMessageToLog("$message: ${time * 1e-6} ms")
} }
override fun processModule(moduleData: ModuleData): ProcessorAction { private fun configureAndSetupEnvironment(moduleData: ModuleData, disposable: Disposable): KotlinCoreEnvironment {
val disposable = Disposer.newDisposable()
val configuration = createDefaultConfiguration(moduleData) val configuration = createDefaultConfiguration(moduleData)
configureLanguageVersionSettings(
configuration.languageVersionSettings = configuration, moduleData,
LanguageVersionSettingsImpl( LanguageVersion.fromVersionString(LANGUAGE_VERSION_K1)!!,
configuration.languageVersionSettings.languageVersion, configureFeatures = {
configuration.languageVersionSettings.apiVersion, put(LanguageFeature.NewInference, if (USE_NI) LanguageFeature.State.ENABLED else LanguageFeature.State.DISABLED)
specificFeatures = mapOf( },
LanguageFeature.NewInference to if (USE_NI) LanguageFeature.State.ENABLED else LanguageFeature.State.DISABLED configureFlags = {
), // TODO: Remove when old tests are no longer supported
analysisFlags = mapOf( if (moduleData.arguments == null) {
AnalysisFlags.skipPrereleaseCheck to true, put(AnalysisFlags.skipPrereleaseCheck, true)
AnalysisFlags.optIn to moduleData.optInAnnotations }
) }
) )
// TODO: Remove when old tests are no longer supported
System.getProperty("fir.bench.oldfe.jvm_target")?.let { if (moduleData.arguments == null) {
configuration.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.fromString(it) ?: error("Unknown JvmTarget")) System.getProperty("fir.bench.oldfe.jvm_target")?.let {
configuration.put(JVMConfigurationKeys.JVM_TARGET, JvmTarget.fromString(it) ?: error("Unknown JvmTarget"))
}
} }
configuration.put(MESSAGE_COLLECTOR_KEY, object : MessageCollector { configuration.put(MESSAGE_COLLECTOR_KEY, object : MessageCollector {
override fun clear() { override fun clear() {
@@ -111,6 +115,18 @@ class NonFirResolveModularizedTotalKotlinTest : AbstractFrontendModularizedTest(
}) })
val environment = KotlinCoreEnvironment.createForTests(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES) val environment = KotlinCoreEnvironment.createForTests(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
val visibilityManager = ModuleVisibilityManager.SERVICE.getInstance(environment.project)
for (friendDir in configuration.getList(JVMConfigurationKeys.FRIEND_PATHS)) {
visibilityManager.addFriendPath(friendDir)
}
return environment
}
override fun processModule(moduleData: ModuleData): ProcessorAction {
val disposable = Disposer.newDisposable()
val environment = configureAndSetupEnvironment(moduleData, disposable)
runAnalysis(environment) runAnalysis(environment)
Disposer.dispose(disposable) Disposer.dispose(disposable)