[JS FIR] Use FIR in JS IR invalidation tests

Related to KT-56740
This commit is contained in:
Alexander Korepanov
2023-03-07 18:51:01 +01:00
committed by Space Team
parent 17e6099b53
commit bd3eb81aee
8 changed files with 814 additions and 227 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.generators.generateTestGroupSuiteWithJUnit5
import org.jetbrains.kotlin.generators.impl.generateTestGroupSuite
import org.jetbrains.kotlin.incremental.AbstractJsIrES6InvalidationTest
import org.jetbrains.kotlin.incremental.AbstractJsIrInvalidationTest
import org.jetbrains.kotlin.incremental.AbstractJsFirInvalidationTest
import org.jetbrains.kotlin.js.test.*
import org.jetbrains.kotlin.js.test.fir.*
import org.jetbrains.kotlin.js.test.ir.*
@@ -55,6 +56,10 @@ fun main(args: Array<String>) {
testClass<AbstractJsIrES6InvalidationTest> {
model("incremental/invalidation/", pattern = "^([^_](.+))$", targetBackend = TargetBackend.JS_IR_ES6, recursive = false)
}
testClass<AbstractJsFirInvalidationTest> {
model("incremental/invalidation/", pattern = "^([^_](.+))$", targetBackend = TargetBackend.JS_IR, recursive = false)
}
}
testGroup("js/js.tests/tests-gen", "compiler/testData/binaryCompatibility", testRunnerMethodName = "runTest0") {
@@ -12,8 +12,6 @@ import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.toPhaseMap
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.js.klib.generateIrForKlibSerialization
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.*
@@ -21,16 +19,13 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.ic.*
import org.jetbrains.kotlin.ir.backend.js.SourceMapsInfo
import org.jetbrains.kotlin.ir.backend.js.codegen.JsGenerationGranularity
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.CompilationOutputs
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.test.converters.ClassicJsBackendFacade
import org.jetbrains.kotlin.js.test.utils.MODULE_EMULATION_FILE
@@ -48,9 +43,6 @@ import org.junit.ComparisonFailure
import java.io.File
import java.util.EnumSet
abstract class AbstractJsIrInvalidationTest : AbstractInvalidationTest(TargetBackend.JS_IR, "incrementalOut/invalidation")
abstract class AbstractJsIrES6InvalidationTest : AbstractInvalidationTest(TargetBackend.JS_IR_ES6, "incrementalOut/invalidationES6")
abstract class AbstractInvalidationTest(
private val targetBackend: TargetBackend,
private val workingDirPath: String
@@ -90,7 +82,9 @@ abstract class AbstractInvalidationTest(
val projectInfoFile = File(testDirectory, PROJECT_INFO_FILE)
val projectInfo = parseProjectInfo(testName, projectInfoFile)
if (projectInfo.muted) return
if (isIgnoredTest(projectInfo)){
return
}
val modulesInfos = mutableMapOf<String, ModuleInfo>()
for (module in projectInfo.modules) {
@@ -182,7 +176,8 @@ abstract class AbstractInvalidationTest(
}
}
val configuration = createConfiguration(module, projStep.language)
buildArtifact(configuration, module, moduleSourceDir, dependencies, friends, outputKlibFile)
outputKlibFile.delete()
buildKlib(configuration, module, moduleSourceDir, dependencies, friends, outputKlibFile)
}
val dtsFile = moduleStep.expectedDTS.ifNotEmpty {
@@ -369,77 +364,11 @@ abstract class AbstractInvalidationTest(
private fun String.isAllowedJsFile() = endsWith(".js") && !TEST_FILE_IGNORE_PATTERN.matches(this)
private fun File.filteredKtFiles(): Collection<File> {
protected fun File.filteredKtFiles(): Collection<File> {
assert(isDirectory && exists())
return listFiles { _, name -> name.isAllowedKtFile() }!!.toList()
}
private fun KotlinCoreEnvironment.createPsiFile(file: File): KtFile {
val psiManager = PsiManager.getInstance(project)
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem
val vFile = fileSystem.findFileByIoFile(file) ?: error("File not found: $file")
return SingleRootFileViewProvider(psiManager, vFile).allFiles.find {
it is KtFile && it.virtualFile.canonicalPath == vFile.canonicalPath
} as KtFile
}
private fun buildArtifact(
configuration: CompilerConfiguration,
moduleName: String,
sourceDir: File,
dependencies: Collection<File>,
friends: Collection<File>,
outputKlibFile: File
) {
if (outputKlibFile.exists()) outputKlibFile.delete()
val projectJs = environment.project
val sourceFiles = sourceDir.filteredKtFiles().map { environment.createPsiFile(it) }
val sourceModule = prepareAnalyzedSourceModule(
project = projectJs,
files = sourceFiles,
configuration = configuration,
dependencies = dependencies.map { it.canonicalPath },
friendDependencies = friends.map { it.canonicalPath },
analyzer = AnalyzerWithCompilerReport(configuration)
)
val moduleSourceFiles = (sourceModule.mainModule as MainModule.SourceFiles).files
val icData = sourceModule.compilerConfiguration.incrementalDataProvider?.getSerializedData(moduleSourceFiles) ?: emptyList()
val expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
val (moduleFragment, _) = generateIrForKlibSerialization(
environment.project,
moduleSourceFiles,
configuration,
sourceModule.jsFrontEndResult.jsAnalysisResult,
sortDependencies(sourceModule.moduleDependencies),
icData,
expectDescriptorToSymbol,
IrFactoryImpl,
verifySignatures = true
) {
sourceModule.getModuleDescriptor(it)
}
val metadataSerializer =
KlibMetadataIncrementalSerializer(configuration, sourceModule.project, sourceModule.jsFrontEndResult.hasErrors)
generateKLib(
sourceModule,
outputKlibFile.canonicalPath,
nopack = false,
jsOutputName = moduleName,
icData = icData,
expectDescriptorToSymbol = expectDescriptorToSymbol,
moduleFragment = moduleFragment
) { file ->
metadataSerializer.serializeScope(file, sourceModule.jsFrontEndResult.bindingContext, moduleFragment.descriptor)
}
}
private fun initializeWorkingDir(projectInfo: ProjectInfo, testDir: File, sourceDir: File, buildDir: File) {
for (module in projectInfo.modules) {
val moduleSourceDir = File(sourceDir, module).also { it.invalidateDir() }
@@ -466,4 +395,26 @@ abstract class AbstractInvalidationTest(
return dir
}
protected fun KotlinCoreEnvironment.createPsiFile(file: File): KtFile {
val psiManager = PsiManager.getInstance(project)
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem
val vFile = fileSystem.findFileByIoFile(file) ?: error("File not found: $file")
return SingleRootFileViewProvider(psiManager, vFile).allFiles.find {
it is KtFile && it.virtualFile.canonicalPath == vFile.canonicalPath
} as KtFile
}
protected open fun isIgnoredTest(projectInfo: ProjectInfo) = projectInfo.muted
protected abstract fun buildKlib(
configuration: CompilerConfiguration,
moduleName: String,
sourceDir: File,
dependencies: Collection<File>,
friends: Collection<File>,
outputKlibFile: File
)
}
@@ -0,0 +1,86 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.js.klib.compileModuleToAnalyzedFir
import org.jetbrains.kotlin.cli.js.klib.serializeFirKlib
import org.jetbrains.kotlin.cli.js.klib.transformFirToIr
import org.jetbrains.kotlin.codegen.ProjectInfo
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.test.TargetBackend
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.PrintStream
import java.nio.charset.Charset
abstract class AbstractJsFirInvalidationTest : FirAbstractInvalidationTest(TargetBackend.JS_IR, "incrementalOut/invalidationFir")
abstract class FirAbstractInvalidationTest(
targetBackend: TargetBackend,
workingDirPath: String
) : AbstractInvalidationTest(targetBackend, workingDirPath) {
private val mutedTests = setOf("constVals", "enum")
override fun isIgnoredTest(projectInfo: ProjectInfo): Boolean {
return super.isIgnoredTest(projectInfo) || projectInfo.name in mutedTests
}
override fun buildKlib(
configuration: CompilerConfiguration,
moduleName: String,
sourceDir: File,
dependencies: Collection<File>,
friends: Collection<File>,
outputKlibFile: File
) {
val outputStream = ByteArrayOutputStream()
val messageCollector = PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.PLAIN_FULL_PATHS, true)
val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter()
val libraries = dependencies.map { it.absolutePath }
val friendLibraries = friends.map { it.absolutePath }
val sourceFiles = sourceDir.filteredKtFiles().map { environment.createPsiFile(it) }
val moduleStructure = ModulesStructure(
project = environment.project,
mainModule = MainModule.SourceFiles(sourceFiles),
compilerConfiguration = configuration,
dependencies = libraries,
friendDependenciesPaths = friendLibraries
)
val outputs = compileModuleToAnalyzedFir(
moduleStructure = moduleStructure,
ktFiles = sourceFiles,
libraries = libraries,
friendLibraries = friendLibraries,
messageCollector = messageCollector,
diagnosticsReporter = diagnosticsReporter
)
if (outputs != null) {
val irResult = transformFirToIr(moduleStructure, outputs)
serializeFirKlib(
moduleStructure = moduleStructure,
firOutputs = outputs,
irResult = irResult,
outputKlibPath = outputKlibFile.absolutePath,
messageCollector = messageCollector,
diagnosticsReporter = diagnosticsReporter,
jsOutputName = moduleName
)
}
if (messageCollector.hasErrors()) {
val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8"))
throw AssertionError("The following errors occurred compiling test:\n$messages")
}
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.js.klib.generateIrForKlibSerialization
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.test.TargetBackend
import java.io.File
abstract class AbstractJsIrInvalidationTest : IrAbstractInvalidationTest(TargetBackend.JS_IR, "incrementalOut/invalidation")
abstract class AbstractJsIrES6InvalidationTest : IrAbstractInvalidationTest(TargetBackend.JS_IR_ES6, "incrementalOut/invalidationES6")
abstract class IrAbstractInvalidationTest(
targetBackend: TargetBackend,
workingDirPath: String
) : AbstractInvalidationTest(targetBackend, workingDirPath) {
override fun buildKlib(
configuration: CompilerConfiguration,
moduleName: String,
sourceDir: File,
dependencies: Collection<File>,
friends: Collection<File>,
outputKlibFile: File
) {
val projectJs = environment.project
val sourceFiles = sourceDir.filteredKtFiles().map { environment.createPsiFile(it) }
val sourceModule = prepareAnalyzedSourceModule(
project = projectJs,
files = sourceFiles,
configuration = configuration,
dependencies = dependencies.map { it.canonicalPath },
friendDependencies = friends.map { it.canonicalPath },
analyzer = AnalyzerWithCompilerReport(configuration)
)
val moduleSourceFiles = (sourceModule.mainModule as MainModule.SourceFiles).files
val icData = sourceModule.compilerConfiguration.incrementalDataProvider?.getSerializedData(moduleSourceFiles) ?: emptyList()
val expectDescriptorToSymbol = mutableMapOf<DeclarationDescriptor, IrSymbol>()
val (moduleFragment, _) = generateIrForKlibSerialization(
environment.project,
moduleSourceFiles,
configuration,
sourceModule.jsFrontEndResult.jsAnalysisResult,
sortDependencies(sourceModule.moduleDependencies),
icData,
expectDescriptorToSymbol,
IrFactoryImpl,
verifySignatures = true
) {
sourceModule.getModuleDescriptor(it)
}
val metadataSerializer =
KlibMetadataIncrementalSerializer(configuration, sourceModule.project, sourceModule.jsFrontEndResult.hasErrors)
generateKLib(
sourceModule,
outputKlibFile.canonicalPath,
nopack = false,
jsOutputName = moduleName,
icData = icData,
expectDescriptorToSymbol = expectDescriptorToSymbol,
moduleFragment = moduleFragment
) { file ->
metadataSerializer.serializeScope(file, sourceModule.jsFrontEndResult.bindingContext, moduleFragment.descriptor)
}
}
}