Implement new faster version of Jar virtual file system

It's only enabled by default in FIR and might be turned on with a CLI flag

The main idea is that default FarFS re-read ZIP file list each time when
class file is requested that is quite slow.
We read it once and them reading bytes from the known offset.

Also, unlike the default version we don't perform attributes check on each access
On the one hand, it works faster on the other it might not notice that one
of the JAR has been changed during compilation process
But looks like it's not supposed to be a frequent even during
compilation of a single module
This commit is contained in:
Denis.Zharkov
2021-03-16 17:34:03 +03:00
committed by TeamCityServer
parent 559e7d223a
commit 8f06e59d3b
10 changed files with 586 additions and 1 deletions
@@ -214,6 +214,12 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
)
var useOldClassFilesReading: Boolean by FreezableVar(false)
@Argument(
value = "-Xuse-fast-jar-file-system",
description = "Use fast implementation on Jar FS. This may speed up compilation time, but currently it's an experimental mode"
)
var useFastJarFileSystem: Boolean by FreezableVar(false)
@Argument(
value = "-Xdump-declarations-to",
valueDescription = "<path>",
@@ -68,6 +68,7 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.STRONG_W
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.toBooleanLenient
import org.jetbrains.kotlin.cli.jvm.JvmRuntimeVersionsConsistencyChecker
import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem
import org.jetbrains.kotlin.cli.jvm.config.*
import org.jetbrains.kotlin.cli.jvm.index.*
import org.jetbrains.kotlin.cli.jvm.javac.JavacWrapperRegistrar
@@ -158,6 +159,8 @@ class KotlinCoreEnvironment private constructor(
val configuration: CompilerConfiguration = initialConfiguration.apply { setupJdkClasspathRoots(configFiles) }.copy()
private val jarFileSystem: VirtualFileSystem
init {
PersistentFSConstants::class.java.getDeclaredField("ourMaxIntellisenseFileSize")
.apply { isAccessible = true }
@@ -167,6 +170,12 @@ class KotlinCoreEnvironment private constructor(
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
jarFileSystem = when {
configuration.getBoolean(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM) ||
configuration.getBoolean(CommonConfigurationKeys.USE_FIR) -> FastJarFileSystem()
else -> applicationEnvironment.jarFileSystem
}
(projectEnvironment as? ProjectEnvironment)?.registerExtensionsFromPlugins(configuration)
// otherwise consider that project environment is properly configured before passing to the environment
// TODO: consider some asserts to check important extension points
@@ -391,7 +400,7 @@ class KotlinCoreEnvironment private constructor(
}
private fun findJarRoot(file: File): VirtualFile? =
applicationEnvironment.jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
jarFileSystem.findFileByPath("$file${URLUtil.JAR_SEPARATOR}")
private fun getSourceRootsCheckingForDuplicates(): List<KotlinSourceRoot> {
val uniqueSourceRoots = hashSetOf<String>()
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2021 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.cli.jvm.compiler.jarfs
import com.intellij.openapi.util.Couple
import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.containers.ConcurrentFactoryMap
class FastJarFileSystem : DeprecatedVirtualFileSystem() {
private val myHandlers: MutableMap<String, FastJarHandler> =
ConcurrentFactoryMap.createMap { key: String -> FastJarHandler(this@FastJarFileSystem, key) }
override fun getProtocol(): String {
return StandardFileSystems.JAR_PROTOCOL
}
override fun findFileByPath(path: String): VirtualFile? {
val pair = splitPath(path)
return myHandlers[pair.first]!!.findFileByPath(pair.second)
}
override fun refresh(asynchronous: Boolean) {}
override fun refreshAndFindFileByPath(path: String): VirtualFile? {
return findFileByPath(path)
}
fun clearHandlersCache() {
myHandlers.clear()
}
companion object {
fun splitPath(path: String): Couple<String> {
val separator = path.indexOf("!/")
require(separator >= 0) { "Path in JarFileSystem must contain a separator: $path" }
val localPath = path.substring(0, separator)
val pathInJar = path.substring(separator + 2)
return Couple.of(localPath, pathInJar)
}
}
}
@@ -0,0 +1,165 @@
/*
* Copyright 2010-2021 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.cli.jvm.compiler.jarfs
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.impl.ZipHandler
import com.intellij.util.containers.FactoryMap
import com.intellij.util.io.FileAccessorCache
import com.intellij.util.text.ByteArrayCharSequence
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import java.io.RandomAccessFile
import java.util.*
internal class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandler(path) {
private val myRoot: VirtualFile?
private val ourEntryMap: Map<String, ZipEntryDescription>
private val cachedManifest: ByteArray?
init {
RandomAccessFile(file, "r").use { randomAccessFile ->
val bufferedRandomAccessFile = BufferedRandomAccessFile(randomAccessFile)
ourEntryMap = bufferedRandomAccessFile.parseCentralDirectory().associateBy { it.relativePath }
cachedManifest = ourEntryMap[MANIFEST_PATH]?.let(bufferedRandomAccessFile::contentsToByteArray)
}
val entries: MutableMap<EntryInfo, FastJarVirtualFile> = HashMap()
val entriesMap = entriesMap
val childrenMap = FactoryMap.create<FastJarVirtualFile, MutableList<VirtualFile>> { ArrayList() }
for (info in entriesMap.values) {
val file = getOrCreateFile(info, entries)
val parent = file.parent
if (parent != null) {
childrenMap[parent]?.add(file)
}
}
val rootInfo = getEntryInfo("")
myRoot = rootInfo?.let { getOrCreateFile(it, entries) }
for ((key, childList) in childrenMap) {
key.children = childList.toTypedArray()
}
}
private fun getOrCreateFile(info: EntryInfo, entries: MutableMap<EntryInfo, FastJarVirtualFile>): FastJarVirtualFile {
var file = entries[info]
if (file == null) {
val parent = info.parent
file = FastJarVirtualFile(this, info.shortName,
if (info.isDirectory) -1 else info.length,
info.timestamp,
parent?.let { getOrCreateFile(it, entries) })
entries[info] = file
}
return file
}
fun findFileByPath(pathInJar: String): VirtualFile? {
return myRoot?.findFileByRelativePath(pathInJar)
}
@Throws(IOException::class)
override fun createEntriesMap(): Map<String, EntryInfo> {
val mapToEntryInfo = mutableMapOf<String, EntryInfo>()
mapToEntryInfo[""] = EntryInfo("", true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, null)
for (zipEntry in ourEntryMap.values) {
getOrCreate(zipEntry, mapToEntryInfo)
}
return mapToEntryInfo
}
private fun getOrCreate(entry: ZipEntryDescription, map: MutableMap<String, EntryInfo>): EntryInfo {
var isDirectory = entry.isDirectory
var entryName = entry.relativePath
if (StringUtil.endsWithChar(entryName, '/')) {
entryName = entryName.substring(0, entryName.length - 1)
isDirectory = true
}
if (StringUtil.startsWithChar(entryName, '/') || StringUtil.startsWithChar(entryName, '\\')) {
entryName = entryName.substring(1)
}
var info = map[entryName]
if (info != null) return info
val path = splitPathAndFix(entryName)
val parentInfo = getOrCreateDirectory(path.first, map)
if ("." == path.second) {
return parentInfo
}
info = store(map, parentInfo, path.second, isDirectory, entry.uncompressedSize.toLong(), 0, path.third)
return info
}
private fun getOrCreateDirectory(entryName: String, map: MutableMap<String, EntryInfo>): EntryInfo {
var info = map[entryName]
if (info == null) {
val entry = ourEntryMap["$entryName/"]
if (entry != null) {
return getOrCreate(entry, map)
}
val path = splitPathAndFix(entryName)
require(entryName != path.first) {
"invalid entry name: '" + entryName + "' in " + this.file.absolutePath + "; after split: " + path
}
val parentInfo = getOrCreateDirectory(path.first, map)
info = store(map, parentInfo, path.second, true, DEFAULT_LENGTH, DEFAULT_TIMESTAMP, path.third)
}
return info
}
private fun store(
map: MutableMap<String, EntryInfo>,
parentInfo: EntryInfo?,
shortName: CharSequence,
isDirectory: Boolean,
size: Long,
time: Long,
entryName: String
): EntryInfo {
val sequence = ByteArrayCharSequence.convertToBytesIfPossible(shortName)
val info = EntryInfo(sequence, isDirectory, size, time, parentInfo)
map[entryName] = info
return info
}
override fun contentsToByteArray(relativePath: String): ByteArray {
if (relativePath == MANIFEST_PATH) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath")
val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath")
return cachedOpenFileHandles[file].use {
synchronized(it) {
it.get().contentsToByteArray(zipEntryDescription)
}
}
}
}
private const val MANIFEST_PATH = "META-INF/MANIFEST.MF"
private val cachedOpenFileHandles: FileAccessorCache<File, BufferedRandomAccessFile> =
object : FileAccessorCache<File, BufferedRandomAccessFile>(20, 10) {
@Throws(IOException::class)
override fun createAccessor(file: File): BufferedRandomAccessFile {
return BufferedRandomAccessFile(RandomAccessFile(file, "r"))
}
@Throws(IOException::class)
override fun disposeAccessor(fileAccessor: BufferedRandomAccessFile) {
fileAccessor.close()
}
override fun isEqual(val1: File, val2: File): Boolean {
return val1 == val2 // reference equality to handle different jars for different ZipHandlers on the same path
}
}
@@ -0,0 +1,105 @@
/*
* Copyright 2010-2021 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.cli.jvm.compiler.jarfs
import com.intellij.openapi.util.Couple
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileSystem
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
internal class FastJarVirtualFile(
private val myHandler: FastJarHandler,
private val myName: CharSequence,
private val myLength: Long,
private val myTimestamp: Long,
parent: FastJarVirtualFile?
) : VirtualFile() {
private val myParent: VirtualFile? = parent
private var myChildren = EMPTY_ARRAY
fun setChildren(children: Array<VirtualFile>) {
myChildren = children
}
override fun getName(): String {
return myName.toString()
}
override fun getNameSequence(): CharSequence {
return myName
}
override fun getFileSystem(): VirtualFileSystem {
return myHandler.fileSystem
}
override fun getPath(): String {
if (myParent == null) {
return FileUtil.toSystemIndependentName(myHandler.file.path) + "!/"
}
val parentPath = myParent.path
val answer = StringBuilder(parentPath.length + 1 + myName.length)
answer.append(parentPath)
if (answer[answer.length - 1] != '/') {
answer.append('/')
}
answer.append(myName)
return answer.toString()
}
override fun isWritable(): Boolean {
return false
}
override fun isDirectory(): Boolean {
return myLength < 0
}
override fun isValid(): Boolean {
return true
}
override fun getParent(): VirtualFile? {
return myParent
}
override fun getChildren(): Array<VirtualFile> {
return myChildren
}
@Throws(IOException::class)
override fun getOutputStream(requestor: Any, newModificationStamp: Long, newTimeStamp: Long): OutputStream {
throw UnsupportedOperationException("JarFileSystem is read-only")
}
@Throws(IOException::class)
override fun contentsToByteArray(): ByteArray {
val pair: Couple<String> = FastJarFileSystem.splitPath(
path
)
return myHandler.contentsToByteArray(pair.second)
}
override fun getTimeStamp(): Long {
return myTimestamp
}
override fun getLength(): Long {
return myLength
}
override fun refresh(asynchronous: Boolean, recursive: Boolean, postRunnable: Runnable?) {}
@Throws(IOException::class)
override fun getInputStream(): InputStream {
return BufferExposingByteArrayInputStream(contentsToByteArray())
}
override fun getModificationStamp(): Long {
return 0
}
}
@@ -0,0 +1,242 @@
/*
* Copyright 2010-2021 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.cli.jvm.compiler.jarfs
import java.io.File
import java.io.RandomAccessFile
import java.util.zip.Inflater
class ZipEntryDescription(
val relativePath: String,
val compressedSize: Int,
val uncompressedSize: Int,
val offsetInFile: Int,
val compressionKind: CompressionKind,
val fileNameSize: Int
) {
enum class CompressionKind {
PLAIN, DEFLATE
}
val isDirectory get() = uncompressedSize == 0
}
private const val END_OF_CENTRAL_DIR_SIZE = 22
private const val LOCAL_FILE_HEADER_EXTRA_OFFSET = 28
private const val LOCAL_FILE_HEADER_SIZE = LOCAL_FILE_HEADER_EXTRA_OFFSET + 2
fun File.contentsToByteArray(zipEntryDescription: ZipEntryDescription): ByteArray {
return RandomAccessFile(this, "r").use { randomAccessFile ->
val bufferedRandomAccessFile = BufferedRandomAccessFile(randomAccessFile)
bufferedRandomAccessFile.contentsToByteArray(zipEntryDescription)
}
}
fun BufferedRandomAccessFile.contentsToByteArray(
zipEntryDescription: ZipEntryDescription
): ByteArray {
val extraSize =
readShortLittleEndianFromOffset((zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_EXTRA_OFFSET).toLong())
seek(
(zipEntryDescription.offsetInFile + LOCAL_FILE_HEADER_SIZE + zipEntryDescription.fileNameSize + extraSize).toLong()
)
val compressed = ByteArray(zipEntryDescription.compressedSize + 1)
readFully(compressed, zipEntryDescription.compressedSize)
return when (zipEntryDescription.compressionKind) {
ZipEntryDescription.CompressionKind.DEFLATE -> {
val inflater = Inflater(true)
inflater.setInput(compressed, 0, zipEntryDescription.compressedSize)
val result = ByteArray(zipEntryDescription.uncompressedSize)
inflater.inflate(result)
result
}
ZipEntryDescription.CompressionKind.PLAIN -> compressed.copyOf(zipEntryDescription.compressedSize)
}
}
fun File.parseCentralDirectory(): List<ZipEntryDescription> {
return RandomAccessFile(this, "r").use { randomAccessFile ->
BufferedRandomAccessFile(randomAccessFile).parseCentralDirectory()
}
}
fun BufferedRandomAccessFile.parseCentralDirectory(): List<ZipEntryDescription> {
val randomAccessFile = randomAccessFile
val endOfCentralDirectoryOffset = (randomAccessFile.length() - END_OF_CENTRAL_DIR_SIZE downTo 0).first { offset ->
// header of "End of central directory"
randomAccessFile.readIntLittleEndianFromOffset(offset) == 0x06054b50
}
val entriesNumber = randomAccessFile.readShortLittleEndianFromOffset(endOfCentralDirectoryOffset + 10)
val offsetOfCentralDirectory = randomAccessFile.readIntLittleEndianFromOffset(endOfCentralDirectoryOffset + 16).toLong()
var currentOffset = offsetOfCentralDirectory
val result = mutableListOf<ZipEntryDescription>()
for (i in 0 until entriesNumber) {
val headerConst = readIntLittleEndianFromOffset(currentOffset)
require(headerConst == 0x02014b50) {
"$i: $headerConst"
}
val versionNeededToExtract =
readShortLittleEndianFromOffset(currentOffset + 6)
val compressionMethod = readShortLittleEndianFromOffset(currentOffset + 10)
val compressedSize = readIntLittleEndianFromOffset(currentOffset + 20)
val uncompressedSize = readIntLittleEndian()
val fileNameLength = readShortLittleEndian()
val extraLength = readShortLittleEndian()
val fileCommentLength = readShortLittleEndian()
val offsetOfFileData = readIntLittleEndianFromOffset(currentOffset + 42)
val name = readString(fileNameLength)
currentOffset += 46 + fileNameLength + extraLength + fileCommentLength
require(versionNeededToExtract == 10 || versionNeededToExtract == 20) {
"Unexpected versionNeededToExtract ($versionNeededToExtract) at $name"
}
val compressionKind = when (compressionMethod) {
0 -> ZipEntryDescription.CompressionKind.PLAIN
8 -> ZipEntryDescription.CompressionKind.DEFLATE
else -> error("Unexpected compression method ($compressionMethod) at $name")
}
result += ZipEntryDescription(
name, compressedSize, uncompressedSize, offsetOfFileData, compressionKind,
fileNameLength
)
}
return result
}
class BufferedRandomAccessFile(
val randomAccessFile: RandomAccessFile
) {
private val buffer = ByteArray(BUFFER_SIZE)
private var offsetInBuffer: Int = -1
private var currentBegin: Long = -1L
fun seek(globalOffset: Long, force: Boolean = false) {
if (!force && currentBegin != -1L && globalOffset >= currentBegin && globalOffset < currentBegin + BUFFER_SIZE) {
offsetInBuffer = (globalOffset - currentBegin).toInt()
return
}
offsetInBuffer = 0
currentBegin = globalOffset
randomAccessFile.seek(globalOffset)
randomAccessFile.readFully(buffer, 0, minOf(randomAccessFile.length() - globalOffset, BUFFER_SIZE.toLong()).toInt())
}
fun read(): Int {
require(offsetInBuffer < BUFFER_SIZE)
return buffer[offsetInBuffer++].let {
if (offsetInBuffer == BUFFER_SIZE && currentBegin + BUFFER_SIZE < randomAccessFile.length()) {
seek(currentBegin + BUFFER_SIZE)
}
java.lang.Byte.toUnsignedInt(it)
}
}
fun readString(length: Int): String {
if (length > BUFFER_SIZE) {
randomAccessFile.seek(currentBegin + offsetInBuffer)
val byteArray = ByteArray(length)
randomAccessFile.readFully(byteArray)
return String(byteArray)
}
if (length > BUFFER_SIZE - offsetInBuffer) {
seek(currentBegin + offsetInBuffer, force = true)
}
return String(buffer, offsetInBuffer, length)
}
fun readFully(dst: ByteArray, length: Int) {
if (length > BUFFER_SIZE) {
randomAccessFile.seek(currentBegin + offsetInBuffer)
randomAccessFile.readFully(dst, 0, length)
return
}
if (length > BUFFER_SIZE - offsetInBuffer) {
seek(currentBegin + offsetInBuffer, force = true)
}
System.arraycopy(buffer, offsetInBuffer, dst, 0, length)
}
fun close() {
randomAccessFile.close()
}
companion object {
private const val BUFFER_SIZE = 50000
}
}
private fun RandomAccessFile.readIntLittleEndianFromOffset(offset: Long): Int {
seek(offset)
return readIntLittleEndian()
}
private fun RandomAccessFile.readIntLittleEndian(): Int {
val a = this.read()
val b = this.read()
val c = this.read()
val d = this.read()
return (d shl 24) + (c shl 16) + (b shl 8) + a
}
private fun RandomAccessFile.readShortLittleEndianFromOffset(offset: Long): Int {
seek(offset)
return readShortLittleEndian()
}
private fun RandomAccessFile.readShortLittleEndian(): Int {
val a = this.read()
val b = this.read()
return (b shl 8) + a
}
private fun BufferedRandomAccessFile.readIntLittleEndianFromOffset(offset: Long): Int {
seek(offset)
return readIntLittleEndian()
}
private fun BufferedRandomAccessFile.readIntLittleEndian(): Int {
val a = this.read()
val b = this.read()
val c = this.read()
val d = this.read()
return (d shl 24) + (c shl 16) + (b shl 8) + a
}
private fun BufferedRandomAccessFile.readShortLittleEndianFromOffset(offset: Long): Int {
seek(offset)
return readShortLittleEndian()
}
private fun BufferedRandomAccessFile.readShortLittleEndian(): Int {
val a = this.read()
val b = this.read()
return (b shl 8) + a
}
@@ -280,11 +280,16 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr
put(JVMConfigurationKeys.USE_TYPE_TABLE, arguments.useTypeTable)
put(JVMConfigurationKeys.SKIP_RUNTIME_VERSION_CHECK, arguments.skipRuntimeVersionCheck)
put(JVMConfigurationKeys.USE_PSI_CLASS_FILES_READING, arguments.useOldClassFilesReading)
put(JVMConfigurationKeys.USE_FAST_JAR_FILE_SYSTEM, arguments.useFastJarFileSystem)
if (arguments.useOldClassFilesReading) {
messageCollector.report(INFO, "Using the old java class files reading implementation")
}
if (arguments.useFastJarFileSystem) {
messageCollector.report(INFO, "Using fast Jar FS implementation")
}
put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
put(JVMConfigurationKeys.USE_SINGLE_MODULE, arguments.singleModule)
put(JVMConfigurationKeys.USE_OLD_SPILLED_VAR_TYPE_ANALYSIS, arguments.useOldSpilledVarTypeAnalysis)
@@ -99,6 +99,9 @@ public class JVMConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> USE_PSI_CLASS_FILES_READING =
CompilerConfigurationKey.create("use a slower (PSI-based) class files reading implementation");
public static final CompilerConfigurationKey<Boolean> USE_FAST_JAR_FILE_SYSTEM =
CompilerConfigurationKey.create("use a faster JAR filesystem implementation");
public static final CompilerConfigurationKey<Boolean> USE_JAVAC =
CompilerConfigurationKey.create("use javac [experimental]");
+1
View File
@@ -135,6 +135,7 @@ where advanced options include:
Suppress the "cannot access built-in declaration" error (useful with -no-stdlib)
-Xtype-enhancement-improvements-strict-mode
Enable strict mode for some improvements in the type enhancement for loaded Java types based on nullability annotations,including freshly supported reading of the type use annotations from class files. See KT-45671 for more details
-Xuse-fast-jar-file-system Use fast implementation on Jar FS. This may speed up compilation time, but currently it's an experimental mode
-Xuse-ir Use the IR backend. This option has no effect unless the language version less than 1.5 is used
-Xuse-javac Use javac for Java source and class files analysis
-Xuse-old-backend Use the old JVM backend
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.ApplicationEnvironmentDisposer
import org.jetbrains.kotlin.test.TestInfrastructureInternals
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import java.io.File
@@ -98,6 +99,10 @@ open class CompilerConfigurationProviderImpl(
val configuration = CompilerConfiguration()
configuration[CommonConfigurationKeys.MODULE_NAME] = module.name
if (module.frontendKind == FrontendKinds.FIR) {
configuration[CommonConfigurationKeys.USE_FIR] = true
}
configuration[CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY] = object : MessageCollector {
override fun clear() {}