Implement simple test system to check ir interpreter correctness

This commit is contained in:
Ivan Kylchik
2021-05-19 16:38:13 +03:00
committed by TeamCityServer
parent 3b250132d0
commit cc2d7340dc
5 changed files with 256 additions and 0 deletions
@@ -0,0 +1,86 @@
/*
* 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.test.backend.handlers
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.checker.IrCompileTimeChecker
import org.jetbrains.kotlin.ir.util.copyTypeAndValueArgumentsFrom
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.GlobalMetadataInfoHandler
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.globalMetadataInfoHandler
open class IrInterpreterBackendHandler(testServices: TestServices) : AbstractIrHandler(testServices) {
private val globalMetadataInfoHandler = testServices.globalMetadataInfoHandler
override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
override fun processModule(module: TestModule, info: IrBackendInput) {
val moduleFragment = info.backendInput.irModuleFragment
val evaluator = Evaluator(IrInterpreter(moduleFragment.irBuiltins), globalMetadataInfoHandler)
for ((irFile, testFile) in moduleFragment.files.zip(module.files)) {
if (testFile.isAdditional) continue
evaluator.evaluate(irFile, testFile)
}
}
}
private class Evaluator(private val interpreter: IrInterpreter, private val globalMetadataInfoHandler: GlobalMetadataInfoHandler) {
fun evaluate(irFile: IrFile, testFile: TestFile) {
object : IrElementTransformerVoid() {
private fun IrExpression.report(original: IrExpression): IrExpression {
if (this == original) return this
val isError = this is IrErrorExpression
val message = when (this) {
is IrConst<*> -> this.value.toString()
is IrErrorExpression -> this.description
else -> TODO("unsupported type ${this::class.java}")
}
val metaInfo = IrInterpreterCodeMetaInfo(this.startOffset, this.endOffset, message, isError)
globalMetadataInfoHandler.addMetadataInfosForFile(testFile, listOf(metaInfo))
return if (this !is IrErrorExpression) this else original
}
override fun visitCall(expression: IrCall): IrExpression {
// try to calculate default args of inline function at call site
// used in `sourceLocation` test
expression.symbol.owner.valueParameters.forEachIndexed { index, parameter ->
if (expression.getValueArgument(index) != null || !expression.symbol.owner.isInline) return@forEachIndexed
val default = parameter.defaultValue?.expression as? IrCall ?: return@forEachIndexed
val callWithNewOffsets = IrCallImpl(
expression.startOffset, expression.endOffset, default.type, default.symbol,
default.typeArgumentsCount, default.valueArgumentsCount, default.origin, default.superQualifierSymbol
)
callWithNewOffsets.copyTypeAndValueArgumentsFrom(default)
interpreter.interpret(callWithNewOffsets, irFile)
.report(callWithNewOffsets)
.takeIf { it != callWithNewOffsets }
?.apply { expression.putArgument(parameter, this) }
}
return super.visitCall(expression)
}
override fun visitField(declaration: IrField): IrStatement {
val initializer = declaration.initializer
val expression = initializer?.expression ?: return declaration
if (expression is IrConst<*>) return declaration
val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true
if (isConst) initializer.expression = interpreter.interpret(expression, irFile).report(expression)
return declaration
}
}.visitFile(irFile)
}
}
@@ -0,0 +1,26 @@
/*
* 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.test.backend.handlers
import org.jetbrains.kotlin.codeMetaInfo.model.CodeMetaInfo
import org.jetbrains.kotlin.codeMetaInfo.renderConfigurations.AbstractCodeMetaInfoRenderConfiguration
class IrInterpreterCodeMetaInfo(override val start: Int, override val end: Int, val description: String, isError: Boolean) : CodeMetaInfo {
override var renderConfiguration = RenderConfiguration()
override val tag: String = if (isError) "WAS_NOT_EVALUATED" else "EVALUATED"
override val attributes: MutableList<String> = mutableListOf()
override fun asString(): String = renderConfiguration.asString(this)
class RenderConfiguration : AbstractCodeMetaInfoRenderConfiguration() {
override fun asString(codeMetaInfo: CodeMetaInfo): String {
codeMetaInfo as IrInterpreterCodeMetaInfo
return "${super.asString(codeMetaInfo)}: `${codeMetaInfo.description}`"
}
}
}
@@ -0,0 +1,31 @@
/*
* 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.test.preprocessors
import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.services.SourceFilePreprocessor
import org.jetbrains.kotlin.test.services.TestServices
class IrInterpreterImplicitKotlinImports(testServices: TestServices) : SourceFilePreprocessor(testServices) {
private fun String.addImplicitKotlinImport(fileName: String): String {
val additionalImports = when (fileName) {
"UByte.kt", "UShort.kt", "UInt.kt", "ULong.kt" -> listOf("kotlin.ranges.*")
else -> listOf("kotlin.*", "kotlin.ranges.*", "kotlin.sequences.*", "kotlin.collections.*")
}.joinToString(separator = "\n") { "import $it" }
val lines = this.split("\n").toMutableList()
when (val index = lines.indexOfFirst { it.startsWith("package ") }) {
-1 -> lines.add(0, additionalImports)
else -> lines.add(index + 1, additionalImports)
}
return lines.joinToString(separator = "\n")
}
override fun process(file: TestFile, content: String): String {
if (!file.isAdditional) return content
return content.addImplicitKotlinImport(file.name)
}
}
@@ -0,0 +1,55 @@
/*
* 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.test.runners.ir.interpreter
import org.jetbrains.kotlin.platform.jvm.JvmPlatforms
import org.jetbrains.kotlin.test.TargetBackend
import org.jetbrains.kotlin.test.backend.handlers.IrInterpreterBackendHandler
import org.jetbrains.kotlin.test.backend.ir.JvmIrBackendFacade
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.frontend.fir.Fir2IrResultsConverter
import org.jetbrains.kotlin.test.frontend.fir.FirFrontendFacade
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.preprocessors.IrInterpreterImplicitKotlinImports
import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest
import org.jetbrains.kotlin.test.services.configuration.CommonEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.JvmEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.sourceProviders.IrInterpreterHelpersSourceFilesProvider
open class AbstractIrInterpreterAfterFir2IrTest : AbstractKotlinCompilerWithTargetBackendTest(TargetBackend.JVM_IR) {
override fun TestConfigurationBuilder.configuration() {
globalDefaults {
frontend = FrontendKinds.FIR
targetPlatform = JvmPlatforms.defaultJvmPlatform
artifactKind = BinaryKind.NoArtifact
targetBackend = TargetBackend.JVM_IR
dependencyKind = DependencyKind.Source
}
defaultDirectives {
+JvmEnvironmentConfigurationDirectives.FULL_JDK
+JvmEnvironmentConfigurationDirectives.NO_RUNTIME
+JvmEnvironmentConfigurationDirectives.ALLOW_KOTLIN_PACKAGE
}
useConfigurators(
::CommonEnvironmentConfigurator,
::JvmEnvironmentConfigurator,
)
useAdditionalSourceProviders(::IrInterpreterHelpersSourceFilesProvider)
useSourcePreprocessor(::IrInterpreterImplicitKotlinImports)
useFrontendFacades(::FirFrontendFacade)
useFrontend2BackendConverters(::Fir2IrResultsConverter)
useBackendFacades(::JvmIrBackendFacade)
useBackendHandlers(::IrInterpreterBackendHandler)
enableMetaInfoHandler()
}
}
@@ -0,0 +1,58 @@
/*
* 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.test.services.sourceProviders
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 org.jetbrains.kotlin.test.services.AdditionalSourceProvider
import org.jetbrains.kotlin.test.services.TestServices
import java.io.File
class IrInterpreterHelpersSourceFilesProvider(testServices: TestServices) : AdditionalSourceProvider(testServices) {
companion object {
private const val HELPERS_PATH = "./compiler/testData/ir/interpreter/helpers"
private const val UNSIGNED_PATH = "./libraries/stdlib/unsigned/src/kotlin"
private val RUNTIME_PATHS = arrayOf(
"./core/builtins/src/kotlin",
"./libraries/stdlib/jvm/runtime/kotlin/TypeAliases.kt",
"./libraries/stdlib/jvm/runtime/kotlin/text/TypeAliases.kt",
"./libraries/stdlib/jvm/src/kotlin/collections/TypeAliases.kt",
"./libraries/stdlib/common/src/kotlin/TextH.kt",
"./libraries/stdlib/src/kotlin/text/regex/MatchResult.kt",
"./libraries/stdlib/src/kotlin/collections/Sequence.kt",
)
private val ANNOTATIONS_PATHS = arrayOf(
"./libraries/stdlib/src/kotlin/annotations",
"./libraries/stdlib/src/kotlin/internal/Annotations.kt",
"./libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmPlatformAnnotations.kt",
)
private const val REFLECT_PATH = "./libraries/stdlib/jvm/src/kotlin/reflect"
private val EXCLUDES = listOf(
"src/kotlin/UStrings.kt", "src/kotlin/UMath.kt", "src/kotlin/UNumbers.kt", "src/kotlin/reflect/TypesJVM.kt"
)
}
override val directives: List<DirectivesContainer> =
listOf(AdditionalFilesDirectives)
private fun getTestFilesForEachDirectory(vararg directories: String): List<TestFile> {
return directories.flatMap { directory ->
File(directory).walkTopDown().mapNotNull {
if (it.isDirectory) return@mapNotNull null
if (EXCLUDES.any { excludePath -> it.absolutePath.endsWith(excludePath) }) return@mapNotNull null
it.toTestFile()
}.toList()
}
}
override fun produceAdditionalFiles(globalDirectives: RegisteredDirectives, module: TestModule): List<TestFile> {
return getTestFilesForEachDirectory(HELPERS_PATH, UNSIGNED_PATH, *RUNTIME_PATHS, *ANNOTATIONS_PATHS, REFLECT_PATH)
}
}