[IR] Handle each interpreter checker one by one

This wy we can interpret all expressions like `A::a.name` at first and
after that evaluate all complex expressions like
`A::a.name + A::b.name`.
This commit is contained in:
Ivan Kylchik
2023-04-28 14:28:50 +02:00
committed by Space Team
parent e58e20fc7b
commit 70560fc3eb
10 changed files with 80 additions and 66 deletions
@@ -9,11 +9,10 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
interface IrInterpreterChecker : IrElementVisitor<Boolean, IrInterpreterCheckerData> { interface IrInterpreterChecker : IrElementVisitor<Boolean, IrInterpreterCheckerData>
val mode: EvaluationMode
}
class IrInterpreterCheckerData( class IrInterpreterCheckerData(
val mode: EvaluationMode,
val irBuiltIns: IrBuiltIns, val irBuiltIns: IrBuiltIns,
val interpreterConfiguration: IrInterpreterConfiguration val interpreterConfiguration: IrInterpreterConfiguration,
) )
@@ -17,9 +17,7 @@ import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.statements import org.jetbrains.kotlin.ir.util.statements
class IrInterpreterCommonChecker( class IrInterpreterCommonChecker : IrInterpreterChecker {
override val mode: EvaluationMode
) : IrInterpreterChecker {
private val visitedStack = mutableListOf<IrElement>() private val visitedStack = mutableListOf<IrElement>()
private inline fun IrElement.asVisited(crossinline block: () -> Boolean): Boolean { private inline fun IrElement.asVisited(crossinline block: () -> Boolean): Boolean {
@@ -42,14 +40,14 @@ class IrInterpreterCommonChecker(
private fun visitConstructor(expression: IrFunctionAccessExpression, data: IrInterpreterCheckerData): Boolean { private fun visitConstructor(expression: IrFunctionAccessExpression, data: IrInterpreterCheckerData): Boolean {
val constructor = expression.symbol.owner val constructor = expression.symbol.owner
if (!mode.canEvaluateFunction(constructor)) return false if (!data.mode.canEvaluateFunction(constructor)) return false
if (!visitValueArguments(expression, data)) return false if (!visitValueArguments(expression, data)) return false
return visitBodyIfNeeded(constructor, data) && return visitBodyIfNeeded(constructor, data) &&
constructor.parentAsClass.declarations.filterIsInstance<IrAnonymousInitializer>().all { it.accept(this, data) } constructor.parentAsClass.declarations.filterIsInstance<IrAnonymousInitializer>().all { it.accept(this, data) }
} }
private fun visitBodyIfNeeded(irFunction: IrFunction, data: IrInterpreterCheckerData): Boolean { private fun visitBodyIfNeeded(irFunction: IrFunction, data: IrInterpreterCheckerData): Boolean {
if (!mode.mustCheckBodyOf(irFunction)) return true if (!data.mode.mustCheckBodyOf(irFunction)) return true
return irFunction.asVisited { irFunction.body?.accept(this@IrInterpreterCommonChecker, data) ?: true } return irFunction.asVisited { irFunction.body?.accept(this@IrInterpreterCommonChecker, data) ?: true }
} }
@@ -58,10 +56,10 @@ class IrInterpreterCommonChecker(
} }
override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean { override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateExpression(expression)) return false if (!data.mode.canEvaluateExpression(expression)) return false
val owner = expression.symbol.owner val owner = expression.symbol.owner
if (!mode.canEvaluateFunction(owner)) return false if (!data.mode.canEvaluateFunction(owner)) return false
// We disable `toFloat` folding on K/JS till `toFloat` is fixed (KT-35422) // We disable `toFloat` folding on K/JS till `toFloat` is fixed (KT-35422)
// This check must be placed here instead of CallInterceptor because we still // This check must be placed here instead of CallInterceptor because we still
@@ -101,7 +99,7 @@ class IrInterpreterCommonChecker(
} }
override fun visitBlock(expression: IrBlock, data: IrInterpreterCheckerData): Boolean { override fun visitBlock(expression: IrBlock, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateBlock(expression)) return false if (!data.mode.canEvaluateBlock(expression)) return false
// `IrReturnableBlock` will be created from IrCall after inline. We should do basically the same check as for IrCall. // `IrReturnableBlock` will be created from IrCall after inline. We should do basically the same check as for IrCall.
if (expression is IrReturnableBlock) { if (expression is IrReturnableBlock) {
@@ -113,7 +111,7 @@ class IrInterpreterCommonChecker(
} }
override fun visitComposite(expression: IrComposite, data: IrInterpreterCheckerData): Boolean { override fun visitComposite(expression: IrComposite, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateComposite(expression)) return false if (!data.mode.canEvaluateComposite(expression)) return false
return visitStatements(expression.statements, data) return visitStatements(expression.statements, data)
} }
@@ -125,7 +123,7 @@ class IrInterpreterCommonChecker(
override fun visitConst(expression: IrConst<*>, data: IrInterpreterCheckerData): Boolean { override fun visitConst(expression: IrConst<*>, data: IrInterpreterCheckerData): Boolean {
if (expression.type.getUnsignedType() != null) { if (expression.type.getUnsignedType() != null) {
val constructor = expression.type.classOrNull?.owner?.constructors?.singleOrNull() ?: return false val constructor = expression.type.classOrNull?.owner?.constructors?.singleOrNull() ?: return false
return mode.canEvaluateFunction(constructor) return data.mode.canEvaluateFunction(constructor)
} }
return true return true
} }
@@ -146,7 +144,7 @@ class IrInterpreterCommonChecker(
.filterIsInstance<IrSimpleFunction>() .filterIsInstance<IrSimpleFunction>()
.single { it.name.asString() == "toString" && it.valueParameters.isEmpty() && it.extensionReceiverParameter == null } .single { it.name.asString() == "toString" && it.valueParameters.isEmpty() && it.extensionReceiverParameter == null }
mode.canEvaluateFunction(toString) && visitBodyIfNeeded(toString, data) data.mode.canEvaluateFunction(toString) && visitBodyIfNeeded(toString, data)
} }
else -> arg.accept(this, data) else -> arg.accept(this, data)
@@ -160,7 +158,7 @@ class IrInterpreterCommonChecker(
} }
override fun visitGetEnumValue(expression: IrGetEnumValue, data: IrInterpreterCheckerData): Boolean { override fun visitGetEnumValue(expression: IrGetEnumValue, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateEnumValue(expression)) return false if (!data.mode.canEvaluateEnumValue(expression)) return false
// we want to avoid recursion in cases like "enum class E(val srt: String) { OK(OK.name) }" // we want to avoid recursion in cases like "enum class E(val srt: String) { OK(OK.name) }"
if (visitedStack.contains(expression)) return true if (visitedStack.contains(expression)) return true
@@ -242,20 +240,20 @@ class IrInterpreterCommonChecker(
} }
override fun visitFunctionReference(expression: IrFunctionReference, data: IrInterpreterCheckerData): Boolean { override fun visitFunctionReference(expression: IrFunctionReference, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateCallableReference(expression)) return false if (!data.mode.canEvaluateCallableReference(expression)) return false
val owner = expression.symbol.owner val owner = expression.symbol.owner
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true
if (!mode.canEvaluateFunction(owner)) return false if (!data.mode.canEvaluateFunction(owner)) return false
val bodyComputable = visitBodyIfNeeded(owner, data) val bodyComputable = visitBodyIfNeeded(owner, data)
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
} }
override fun visitFunctionExpression(expression: IrFunctionExpression, data: IrInterpreterCheckerData): Boolean { override fun visitFunctionExpression(expression: IrFunctionExpression, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateFunctionExpression(expression)) return false if (!data.mode.canEvaluateFunctionExpression(expression)) return false
val body = expression.function.body ?: return false val body = expression.function.body ?: return false
return expression.function.asVisited { body.accept(this, data) } return expression.function.asVisited { body.accept(this, data) }
@@ -275,7 +273,7 @@ class IrInterpreterCommonChecker(
} }
override fun visitWhen(expression: IrWhen, data: IrInterpreterCheckerData): Boolean { override fun visitWhen(expression: IrWhen, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateExpression(expression)) return false if (!data.mode.canEvaluateExpression(expression)) return false
return expression.branches.all { it.accept(this, data) } return expression.branches.all { it.accept(this, data) }
} }
@@ -297,7 +295,7 @@ class IrInterpreterCommonChecker(
} }
override fun visitTry(aTry: IrTry, data: IrInterpreterCheckerData): Boolean { override fun visitTry(aTry: IrTry, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateExpression(aTry)) return false if (!data.mode.canEvaluateExpression(aTry)) return false
if (!aTry.tryResult.accept(this, data)) return false if (!aTry.tryResult.accept(this, data)) return false
if (aTry.finallyExpression != null && aTry.finallyExpression?.accept(this, data) == false) return false if (aTry.finallyExpression != null && aTry.finallyExpression?.accept(this, data) == false) return false
@@ -314,22 +312,22 @@ class IrInterpreterCommonChecker(
} }
override fun visitThrow(expression: IrThrow, data: IrInterpreterCheckerData): Boolean { override fun visitThrow(expression: IrThrow, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateExpression(expression)) return false if (!data.mode.canEvaluateExpression(expression)) return false
return expression.value.accept(this, data) return expression.value.accept(this, data)
} }
override fun visitPropertyReference(expression: IrPropertyReference, data: IrInterpreterCheckerData): Boolean { override fun visitPropertyReference(expression: IrPropertyReference, data: IrInterpreterCheckerData): Boolean {
if (!mode.canEvaluateCallableReference(expression)) return false if (!data.mode.canEvaluateCallableReference(expression)) return false
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true
val getterIsComputable = expression.getter?.let { mode.canEvaluateFunction(it.owner) } ?: true val getterIsComputable = expression.getter?.let { data.mode.canEvaluateFunction(it.owner) } ?: true
return dispatchReceiverComputable && extensionReceiverComputable && getterIsComputable return dispatchReceiverComputable && extensionReceiverComputable && getterIsComputable
} }
override fun visitClassReference(expression: IrClassReference, data: IrInterpreterCheckerData): Boolean { override fun visitClassReference(expression: IrClassReference, data: IrInterpreterCheckerData): Boolean {
return mode.canEvaluateClassReference(expression) return data.mode.canEvaluateClassReference(expression)
} }
} }
@@ -16,14 +16,12 @@ import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
import org.jetbrains.kotlin.ir.util.isSubclassOf import org.jetbrains.kotlin.ir.util.isSubclassOf
class IrInterpreterNameChecker( class IrInterpreterNameChecker : IrInterpreterChecker {
override val mode: EvaluationMode,
) : IrInterpreterChecker {
override fun visitElement(element: IrElement, data: IrInterpreterCheckerData) = false override fun visitElement(element: IrElement, data: IrInterpreterCheckerData) = false
override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean { override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean {
val owner = expression.symbol.owner val owner = expression.symbol.owner
if (!mode.canEvaluateFunction(owner)) return false if (!data.mode.canEvaluateFunction(owner)) return false
return expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName() return expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName()
} }
@@ -8,8 +8,10 @@ package org.jetbrains.kotlin.ir.interpreter.preprocessor
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrCallableReference import org.jetbrains.kotlin.ir.expressions.IrCallableReference
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterNameChecker.Companion.isKCallableNameCall import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterNameChecker.Companion.isKCallableNameCall
import org.jetbrains.kotlin.name.SpecialNames
// Note: this class still will not allow us to evaluate things like `A()::a.name + `A()::b.name`. // Note: this class still will not allow us to evaluate things like `A()::a.name + `A()::b.name`.
// This code will be optimized but not completely turned into "ab" result. // This code will be optimized but not completely turned into "ab" result.
@@ -22,8 +24,10 @@ class IrInterpreterKCallableNamePreprocessor : IrInterpreterPreprocessor {
// receiver is needed for bound callable reference // receiver is needed for bound callable reference
val receiver = callableReference.dispatchReceiver ?: callableReference.extensionReceiver ?: return expression val receiver = callableReference.dispatchReceiver ?: callableReference.extensionReceiver ?: return expression
callableReference.dispatchReceiver = null callableReference.dispatchReceiver = null
callableReference.extensionReceiver = null callableReference.extensionReceiver = null
if (receiver is IrGetValue && receiver.symbol.owner.name == SpecialNames.THIS) return expression
return IrCompositeImpl( return IrCompositeImpl(
expression.startOffset, expression.endOffset, expression.type, origin = null, statements = listOf(receiver, expression) expression.startOffset, expression.endOffset, expression.type, origin = null, statements = listOf(receiver, expression)
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterChecker
import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray
import org.jetbrains.kotlin.ir.interpreter.toIrConst import org.jetbrains.kotlin.ir.interpreter.toIrConst
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
@@ -22,11 +23,12 @@ internal abstract class IrConstAnnotationTransformer(
interpreter: IrInterpreter, interpreter: IrInterpreter,
irFile: IrFile, irFile: IrFile,
mode: EvaluationMode, mode: EvaluationMode,
checker: IrInterpreterChecker,
evaluatedConstTracker: EvaluatedConstTracker?, evaluatedConstTracker: EvaluatedConstTracker?,
onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
onError: (IrFile, IrElement, IrErrorExpression) -> Unit, onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
suppressExceptions: Boolean, suppressExceptions: Boolean,
) : IrConstTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions) { ) : IrConstTransformer(interpreter, irFile, mode, checker, evaluatedConstTracker, onWarning, onError, suppressExceptions) {
protected fun transformAnnotations(annotationContainer: IrAnnotationContainer) { protected fun transformAnnotations(annotationContainer: IrAnnotationContainer) {
annotationContainer.annotations.forEach { annotation -> annotationContainer.annotations.forEach { annotation ->
transformAnnotation(annotation) transformAnnotation(annotation)
@@ -13,20 +13,21 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrErrorExpression import org.jetbrains.kotlin.ir.expressions.IrErrorExpression
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterChecker
import org.jetbrains.kotlin.ir.util.primaryConstructor import org.jetbrains.kotlin.ir.util.primaryConstructor
internal class IrConstDeclarationAnnotationTransformer( internal class IrConstDeclarationAnnotationTransformer(
interpreter: IrInterpreter, interpreter: IrInterpreter,
irFile: IrFile, irFile: IrFile,
mode: EvaluationMode, mode: EvaluationMode,
checker: IrInterpreterChecker,
evaluatedConstTracker: EvaluatedConstTracker?, evaluatedConstTracker: EvaluatedConstTracker?,
onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
onError: (IrFile, IrElement, IrErrorExpression) -> Unit, onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
suppressExceptions: Boolean, suppressExceptions: Boolean,
) : IrConstAnnotationTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions) { ) : IrConstAnnotationTransformer(interpreter, irFile, mode, checker, evaluatedConstTracker, onWarning, onError, suppressExceptions) {
override fun visitFile(declaration: IrFile, data: Nothing?): IrFile { override fun visitFile(declaration: IrFile, data: Nothing?): IrFile {
transformAnnotations(declaration) transformAnnotations(declaration)
return super.visitFile(declaration, data) return super.visitFile(declaration, data)
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterChecker
import org.jetbrains.kotlin.ir.interpreter.createGetField import org.jetbrains.kotlin.ir.interpreter.createGetField
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -23,11 +24,12 @@ internal class IrConstExpressionTransformer(
interpreter: IrInterpreter, interpreter: IrInterpreter,
irFile: IrFile, irFile: IrFile,
mode: EvaluationMode, mode: EvaluationMode,
checker: IrInterpreterChecker,
evaluatedConstTracker: EvaluatedConstTracker?, evaluatedConstTracker: EvaluatedConstTracker?,
onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
onError: (IrFile, IrElement, IrErrorExpression) -> Unit, onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
suppressExceptions: Boolean, suppressExceptions: Boolean,
) : IrConstTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions) { ) : IrConstTransformer(interpreter, irFile, mode, checker, evaluatedConstTracker, onWarning, onError, suppressExceptions) {
override fun visitCall(expression: IrCall, data: Nothing?): IrElement { override fun visitCall(expression: IrCall, data: Nothing?): IrElement {
if (expression.canBeInterpreted()) { if (expression.canBeInterpreted()) {
return expression.interpret(failAsError = false) return expression.interpret(failAsError = false)
@@ -55,27 +57,31 @@ internal class IrConstExpressionTransformer(
) )
fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = false) fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = false)
fun IrExpression.getConstStringOrEmpty(): String = if (this is IrConst<*>) value.toString() else ""
// If we have some complex expression in arguments (like some `IrComposite`) we will skip it,
// but we must visit this argument in order to apply all possible optimizations.
val transformed = super.visitStringConcatenation(expression, data) as? IrStringConcatenation ?: return expression
// here `StringBuilder`'s list is used to optimize memory, everything works without it // here `StringBuilder`'s list is used to optimize memory, everything works without it
val folded = mutableListOf<IrExpression>() val folded = mutableListOf<IrExpression>()
val buildersList = mutableListOf<StringBuilder>() val buildersList = mutableListOf<StringBuilder>()
for (next in expression.arguments) { for (next in transformed.arguments) {
val last = folded.lastOrNull() val last = folded.lastOrNull()
when { when {
!next.wrapInStringConcat().canBeInterpreted() -> { !next.wrapInStringConcat().canBeInterpreted() -> {
folded += next folded += next
buildersList.add(StringBuilder()) buildersList.add(StringBuilder(next.getConstStringOrEmpty()))
} }
last == null || !last.wrapInStringConcat().canBeInterpreted() -> { last == null || !last.wrapInStringConcat().canBeInterpreted() -> {
val result = next.wrapInToStringConcatAndInterpret() val result = next.wrapInToStringConcatAndInterpret()
folded += result folded += result
buildersList.add(StringBuilder((result as? IrConst<*>)?.value?.toString() ?: "")) buildersList.add(StringBuilder(result.getConstStringOrEmpty()))
} }
else -> { else -> {
val nextAsConst = next.wrapInToStringConcatAndInterpret() val nextAsConst = next.wrapInToStringConcatAndInterpret()
if (nextAsConst !is IrConst<*>) { if (nextAsConst !is IrConst<*>) {
folded += next folded += next
buildersList.add(StringBuilder()) buildersList.add(StringBuilder(next.getConstStringOrEmpty()))
} else { } else {
folded[folded.size - 1] = IrConstImpl.string( folded[folded.size - 1] = IrConstImpl.string(
// Inlined strings may have `last.startOffset > next.endOffset` // Inlined strings may have `last.startOffset > next.endOffset`
@@ -16,18 +16,13 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode import org.jetbrains.kotlin.ir.interpreter.checker.*
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterCheckerData
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterCommonChecker
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterNameChecker
import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor
import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterPreprocessorData import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterPreprocessorData
import org.jetbrains.kotlin.ir.interpreter.toConstantValue import org.jetbrains.kotlin.ir.interpreter.toConstantValue
import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
private val preprocessors = setOf(IrInterpreterKCallableNamePreprocessor())
fun IrFile.transformConst( fun IrFile.transformConst(
interpreter: IrInterpreter, interpreter: IrInterpreter,
mode: EvaluationMode, mode: EvaluationMode,
@@ -36,22 +31,30 @@ fun IrFile.transformConst(
onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> }, onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> },
suppressExceptions: Boolean = false, suppressExceptions: Boolean = false,
) { ) {
val preprocessedFile = preprocessors.fold(this) { acc, preprocessor -> val preprocessors = setOf(IrInterpreterKCallableNamePreprocessor())
preprocessor.preprocess(acc, IrInterpreterPreprocessorData(mode, interpreter.irBuiltIns)) val preprocessedFile = preprocessors.fold(this) { file, preprocessor ->
preprocessor.preprocess(file, IrInterpreterPreprocessorData(mode, interpreter.irBuiltIns))
} }
val irConstExpressionTransformer = IrConstExpressionTransformer( val checkers = setOf(
interpreter, preprocessedFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions IrInterpreterNameChecker(),
IrInterpreterCommonChecker(),
) )
val irConstDeclarationAnnotationTransformer = IrConstDeclarationAnnotationTransformer(
interpreter, preprocessedFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions checkers.fold(preprocessedFile) { file, checker ->
) val irConstExpressionTransformer = IrConstExpressionTransformer(
val irConstTypeAnnotationTransformer = IrConstTypeAnnotationTransformer( interpreter, file, mode, checker, evaluatedConstTracker, onWarning, onError, suppressExceptions
interpreter, preprocessedFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions )
) val irConstDeclarationAnnotationTransformer = IrConstDeclarationAnnotationTransformer(
preprocessedFile.transform(irConstExpressionTransformer, null) interpreter, file, mode, checker, evaluatedConstTracker, onWarning, onError, suppressExceptions
preprocessedFile.transform(irConstDeclarationAnnotationTransformer, null) )
preprocessedFile.transform(irConstTypeAnnotationTransformer, null) val irConstTypeAnnotationTransformer = IrConstTypeAnnotationTransformer(
interpreter, file, mode, checker, evaluatedConstTracker, onWarning, onError, suppressExceptions
)
file.transform(irConstExpressionTransformer, null)
file.transform(irConstDeclarationAnnotationTransformer, null)
file.transform(irConstTypeAnnotationTransformer, null)
}
} }
// Note: We are using `IrElementTransformer` here instead of `IrElementTransformerVoid` to avoid conflicts with `IrTypeVisitorVoid` // Note: We are using `IrElementTransformer` here instead of `IrElementTransformerVoid` to avoid conflicts with `IrTypeVisitorVoid`
@@ -60,16 +63,12 @@ internal abstract class IrConstTransformer(
protected val interpreter: IrInterpreter, protected val interpreter: IrInterpreter,
private val irFile: IrFile, private val irFile: IrFile,
private val mode: EvaluationMode, private val mode: EvaluationMode,
private val checker: IrInterpreterChecker,
private val evaluatedConstTracker: EvaluatedConstTracker? = null, private val evaluatedConstTracker: EvaluatedConstTracker? = null,
private val onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, private val onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
private val onError: (IrFile, IrElement, IrErrorExpression) -> Unit, private val onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
private val suppressExceptions: Boolean, private val suppressExceptions: Boolean,
) : IrElementTransformer<Nothing?> { ) : IrElementTransformer<Nothing?> {
private val checkers = setOf(
IrInterpreterCommonChecker(mode),
IrInterpreterNameChecker(mode)
)
private fun IrExpression.warningIfError(original: IrExpression): IrExpression { private fun IrExpression.warningIfError(original: IrExpression): IrExpression {
if (this is IrErrorExpression) { if (this is IrErrorExpression) {
onWarning(irFile, original, this) onWarning(irFile, original, this)
@@ -94,7 +93,7 @@ internal abstract class IrConstTransformer(
configuration: IrInterpreterConfiguration = interpreter.environment.configuration configuration: IrInterpreterConfiguration = interpreter.environment.configuration
): Boolean { ): Boolean {
return try { return try {
checkers.any { this.accept(it, IrInterpreterCheckerData(interpreter.irBuiltIns, configuration)) } this.accept(checker, IrInterpreterCheckerData(mode, interpreter.irBuiltIns, configuration))
} catch (e: Throwable) { } catch (e: Throwable) {
if (suppressExceptions) { if (suppressExceptions) {
return false return false
@@ -7,22 +7,26 @@ package org.jetbrains.kotlin.ir.interpreter.transformer
import org.jetbrains.kotlin.constant.EvaluatedConstTracker import org.jetbrains.kotlin.constant.EvaluatedConstTracker
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.IrErrorExpression
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterChecker
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.visitors.IrTypeTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrTypeTransformerVoid
internal class IrConstTypeAnnotationTransformer( internal class IrConstTypeAnnotationTransformer(
interpreter: IrInterpreter, interpreter: IrInterpreter,
irFile: IrFile, irFile: IrFile,
mode: EvaluationMode, mode: EvaluationMode,
checker: IrInterpreterChecker,
evaluatedConstTracker: EvaluatedConstTracker?, evaluatedConstTracker: EvaluatedConstTracker?,
onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
onError: (IrFile, IrElement, IrErrorExpression) -> Unit, onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
suppressExceptions: Boolean, suppressExceptions: Boolean,
) : IrConstAnnotationTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions), ) : IrConstAnnotationTransformer(interpreter, irFile, mode, checker, evaluatedConstTracker, onWarning, onError, suppressExceptions),
IrTypeTransformerVoid<Nothing?> { IrTypeTransformerVoid<Nothing?> {
override fun <Type : IrType?> transformType(container: IrElement, type: Type, data: Nothing?): Type { override fun <Type : IrType?> transformType(container: IrElement, type: Type, data: Nothing?): Type {
@@ -24,9 +24,12 @@ class A {
val temp = A() val temp = A()
val g = temp::b.<!EVALUATED("b")!>name<!> val g = temp::b.<!EVALUATED("b")!>name<!>
val insideStringConcat = "${temp::b.<!EVALUATED("b")!>name<!>}"
val complexExpression1 = A()::a.<!EVALUATED("a")!>name<!> + A()::b.<!EVALUATED("b")!>name<!> val complexExpression1 = A()::a.<!EVALUATED("a")!>name<!> + A()::b.<!EVALUATED("b")!>name<!>
val complexExpression2 = A::a.<!EVALUATED("a")!>name<!> <!EVALUATED("ab")!>+ A::b.<!EVALUATED("b")!>name<!><!> val complexExpression2 = A::a.<!EVALUATED("a")!>name<!> <!EVALUATED("ab")!>+ A::b.<!EVALUATED("b")!>name<!><!>
var recursive = ::test.<!EVALUATED("test")!>name<!>
} }
fun getA(): A = A() fun getA(): A = A()