diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInterpreterBackendHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInterpreterBackendHandler.kt new file mode 100644 index 00000000000..ec0e4483a2b --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInterpreterBackendHandler.kt @@ -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) + } +} + diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInterpreterCodeMetaInfo.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInterpreterCodeMetaInfo.kt new file mode 100644 index 00000000000..16e0a51348f --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/IrInterpreterCodeMetaInfo.kt @@ -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 = 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}`" + } + } +} \ No newline at end of file diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/preprocessors/IrInterpreterImplicitKotlinImports.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/preprocessors/IrInterpreterImplicitKotlinImports.kt new file mode 100644 index 00000000000..6283a9fdac3 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/preprocessors/IrInterpreterImplicitKotlinImports.kt @@ -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) + } +} \ No newline at end of file diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/ir/interpreter/AbstractIrInterpreterAfterFir2IrTest.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/ir/interpreter/AbstractIrInterpreterAfterFir2IrTest.kt new file mode 100644 index 00000000000..6277f973d79 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/runners/ir/interpreter/AbstractIrInterpreterAfterFir2IrTest.kt @@ -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() + } +} + diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/sourceProviders/IrInterpreterHelpersSourceFilesProvider.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/sourceProviders/IrInterpreterHelpersSourceFilesProvider.kt new file mode 100644 index 00000000000..98202867746 --- /dev/null +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/sourceProviders/IrInterpreterHelpersSourceFilesProvider.kt @@ -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 = + listOf(AdditionalFilesDirectives) + + private fun getTestFilesForEachDirectory(vararg directories: String): List { + 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 { + return getTestFilesForEachDirectory(HELPERS_PATH, UNSIGNED_PATH, *RUNTIME_PATHS, *ANNOTATIONS_PATHS, REFLECT_PATH) + } +} \ No newline at end of file