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