Implement simple test system to check ir interpreter correctness
This commit is contained in:
committed by
TeamCityServer
parent
3b250132d0
commit
cc2d7340dc
+86
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+26
@@ -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}`"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+58
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user