[IR] Separate IrConstTransformer into two distinct classes

The first one `IrConstExpressionTransformer` is responsible for
transforming ordinary expressions and the second one
`IrConstAnnotationTransformer` is responsible for annotations.

#KT-57812
This commit is contained in:
Ivan Kylchik
2023-04-19 11:31:30 +02:00
committed by Space Team
parent ccbca7a019
commit 55cac9caa6
5 changed files with 224 additions and 156 deletions
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.checker.IrConstTransformer
import org.jetbrains.kotlin.ir.interpreter.checker.transformConst
import org.jetbrains.kotlin.ir.util.KotlinMangler
import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl
import org.jetbrains.kotlin.ir.visitors.acceptVoid
@@ -431,12 +431,7 @@ class Fir2IrConverter(
val interpreter = IrInterpreter(IrInterpreterEnvironment(irModuleFragment.irBuiltins, configuration))
val mode = if (intrinsicConstEvaluation) EvaluationMode.ONLY_INTRINSIC_CONST else EvaluationMode.ONLY_BUILTINS
irModuleFragment.files.forEach {
val transformer = IrConstTransformer(
interpreter, it,
mode = mode,
evaluatedConstTracker = fir2IrConfiguration.evaluatedConstTracker
)
it.transformChildren(transformer, null)
it.transformConst(interpreter, mode = mode, evaluatedConstTracker = fir2IrConfiguration.evaluatedConstTracker)
}
}
@@ -15,7 +15,7 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.checker.IrConstTransformer
import org.jetbrains.kotlin.ir.interpreter.checker.transformConst
class ConstEvaluationLowering(
val context: CommonBackendContext,
@@ -28,13 +28,9 @@ class ConstEvaluationLowering(
private val evaluatedConstTracker = context.configuration[CommonConfigurationKeys.EVALUATED_CONST_TRACKER]
override fun lower(irFile: IrFile) {
val transformer = IrConstTransformer(
interpreter, irFile,
mode = EvaluationMode.ONLY_INTRINSIC_CONST,
evaluatedConstTracker,
onWarning, onError, suppressErrors
irFile.transformConst(
interpreter, mode = EvaluationMode.ONLY_INTRINSIC_CONST, evaluatedConstTracker, onWarning, onError, suppressErrors
)
irFile.transformChildren(transformer, null)
}
}
@@ -0,0 +1,87 @@
/*
* Copyright 2010-2023 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.ir.interpreter.checker
import org.jetbrains.kotlin.constant.EvaluatedConstTracker
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
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.IrVarargImpl
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray
import org.jetbrains.kotlin.ir.interpreter.toIrConst
import org.jetbrains.kotlin.ir.types.*
internal class IrConstAnnotationTransformer(
interpreter: IrInterpreter,
irFile: IrFile,
mode: EvaluationMode,
evaluatedConstTracker: EvaluatedConstTracker?,
onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
suppressExceptions: Boolean,
) : IrConstTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions) {
override fun visitField(declaration: IrField): IrStatement {
transformAnnotations(declaration)
return super.visitField(declaration)
}
override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement {
transformAnnotations(declaration)
return super.visitDeclaration(declaration)
}
private fun transformAnnotations(annotationContainer: IrAnnotationContainer) {
annotationContainer.annotations.forEach { annotation ->
transformAnnotation(annotation)
}
}
private fun transformAnnotation(annotation: IrConstructorCall) {
for (i in 0 until annotation.valueArgumentsCount) {
val arg = annotation.getValueArgument(i) ?: continue
when (arg) {
is IrVararg -> annotation.putValueArgument(i, arg.transformVarArg())
else -> annotation.putValueArgument(i, arg.transformSingleArg(annotation.symbol.owner.valueParameters[i].type))
}
}
}
private fun IrVararg.transformVarArg(): IrVararg {
if (elements.isEmpty()) return this
val newIrVararg = IrVarargImpl(this.startOffset, this.endOffset, this.type, this.varargElementType)
for (element in this.elements) {
when (val arg = (element as? IrSpreadElement)?.expression ?: element) {
is IrVararg -> arg.transformVarArg().elements.forEach { newIrVararg.addElement(it) }
is IrExpression -> newIrVararg.addElement(arg.transformSingleArg(this.varargElementType))
else -> newIrVararg.addElement(arg)
}
}
return newIrVararg
}
private fun IrExpression.transformSingleArg(expectedType: IrType): IrExpression {
if (this.canBeInterpreted(configuration = interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) {
return this.interpret(failAsError = true).convertToConstIfPossible(expectedType)
} else if (this is IrConstructorCall) {
transformAnnotation(this)
}
return this
}
private fun IrExpression.convertToConstIfPossible(type: IrType): IrExpression {
return when {
this !is IrConst<*> || type is IrErrorType -> this
type.isArray() -> this.convertToConstIfPossible((type as IrSimpleType).arguments.single().typeOrNull!!)
type.isPrimitiveArray() -> this.convertToConstIfPossible(this.type)
else -> this.value.toIrConst(type, this.startOffset, this.endOffset)
}
}
}
@@ -0,0 +1,98 @@
/*
* Copyright 2010-2023 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.ir.interpreter.checker
import org.jetbrains.kotlin.constant.EvaluatedConstTracker
import org.jetbrains.kotlin.ir.IrElement
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.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import kotlin.math.max
import kotlin.math.min
internal class IrConstExpressionTransformer(
interpreter: IrInterpreter,
irFile: IrFile,
mode: EvaluationMode,
evaluatedConstTracker: EvaluatedConstTracker?,
onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
suppressExceptions: Boolean,
) : IrConstTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions) {
override fun visitCall(expression: IrCall): IrExpression {
if (expression.canBeInterpreted()) {
return expression.interpret(failAsError = false)
}
return super.visitCall(expression)
}
override fun visitField(declaration: IrField): IrStatement {
val initializer = declaration.initializer
val expression = initializer?.expression ?: return declaration
val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true
if (!isConst) return super.visitField(declaration)
if (expression.canBeInterpreted(declaration, interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) {
initializer.expression = expression.interpret(failAsError = true)
}
return super.visitField(declaration)
}
override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression {
fun IrExpression.wrapInStringConcat(): IrExpression = IrStringConcatenationImpl(
this.startOffset, this.endOffset, expression.type, listOf(this@wrapInStringConcat)
)
fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = false)
// here `StringBuilder`'s list is used to optimize memory, everything works without it
val folded = mutableListOf<IrExpression>()
val buildersList = mutableListOf<StringBuilder>()
for (next in expression.arguments) {
val last = folded.lastOrNull()
when {
!next.wrapInStringConcat().canBeInterpreted() -> {
folded += next
buildersList.add(StringBuilder())
}
last == null || !last.wrapInStringConcat().canBeInterpreted() -> {
val result = next.wrapInToStringConcatAndInterpret()
folded += result
buildersList.add(StringBuilder((result as? IrConst<*>)?.value?.toString() ?: ""))
}
else -> {
val nextAsConst = next.wrapInToStringConcatAndInterpret()
if (nextAsConst !is IrConst<*>) {
folded += next
buildersList.add(StringBuilder())
} else {
folded[folded.size - 1] = IrConstImpl.string(
// Inlined strings may have `last.startOffset > next.endOffset`
min(last.startOffset, next.startOffset), max(last.endOffset, next.endOffset), expression.type, ""
)
buildersList.last().append(nextAsConst.value.toString())
}
}
}
}
val foldedConst = folded.singleOrNull() as? IrConst<*>
if (foldedConst != null) {
return IrConstImpl.string(expression.startOffset, expression.endOffset, expression.type, buildersList.single().toString())
}
folded.zip(buildersList).forEach {
@Suppress("UNCHECKED_CAST")
(it.first as? IrConst<String>)?.value = it.second.toString()
}
return IrStringConcatenationImpl(expression.startOffset, expression.endOffset, expression.type, folded)
}
}
@@ -8,29 +8,45 @@ package org.jetbrains.kotlin.ir.interpreter.checker
import org.jetbrains.kotlin.constant.ErrorValue
import org.jetbrains.kotlin.constant.EvaluatedConstTracker
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.name
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrErrorExpression
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.interpreter.*
import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.ir.interpreter.toConstantValue
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.name.Name
import kotlin.math.max
import kotlin.math.min
class IrConstTransformer(
private val interpreter: IrInterpreter,
fun IrFile.transformConst(
interpreter: IrInterpreter,
mode: EvaluationMode,
evaluatedConstTracker: EvaluatedConstTracker?,
onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> },
onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> },
suppressExceptions: Boolean = false,
) {
val irConstExpressionTransformer = IrConstExpressionTransformer(
interpreter, this, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions
)
val irConstAnnotationTransformer = IrConstAnnotationTransformer(
interpreter, this, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions
)
this.transform(irConstExpressionTransformer, null)
this.transform(irConstAnnotationTransformer, null)
}
internal abstract class IrConstTransformer(
protected val interpreter: IrInterpreter,
private val irFile: IrFile,
private val mode: EvaluationMode,
private val evaluatedConstTracker: EvaluatedConstTracker? = null,
private val onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> },
private val onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> },
private val suppressExceptions: Boolean = false,
private val onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit,
private val onError: (IrFile, IrElement, IrErrorExpression) -> Unit,
private val suppressExceptions: Boolean,
) : IrElementTransformerVoid() {
private fun IrExpression.warningIfError(original: IrExpression): IrExpression {
if (this is IrErrorExpression) {
@@ -52,7 +68,7 @@ class IrConstTransformer(
return this
}
private fun IrExpression.canBeInterpreted(
protected fun IrExpression.canBeInterpreted(
containingDeclaration: IrElement? = null,
configuration: IrInterpreterConfiguration = interpreter.environment.configuration
): Boolean {
@@ -66,7 +82,7 @@ class IrConstTransformer(
}
}
private fun IrExpression.interpret(failAsError: Boolean): IrExpression {
protected fun IrExpression.interpret(failAsError: Boolean): IrExpression {
val result = try {
interpreter.interpret(this, irFile)
} catch (e: Throwable) {
@@ -83,128 +99,4 @@ class IrConstTransformer(
)
return if (failAsError) result.reportIfError(this) else result.warningIfError(this)
}
override fun visitCall(expression: IrCall): IrExpression {
if (expression.canBeInterpreted()) {
return expression.interpret(failAsError = false)
}
return super.visitCall(expression)
}
override fun visitField(declaration: IrField): IrStatement {
transformAnnotations(declaration)
val initializer = declaration.initializer
val expression = initializer?.expression ?: return declaration
val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true
if (!isConst) return super.visitField(declaration)
if (expression.canBeInterpreted(declaration, interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) {
initializer.expression = expression.interpret(failAsError = true)
}
return super.visitField(declaration)
}
override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression {
fun IrExpression.wrapInStringConcat(): IrExpression = IrStringConcatenationImpl(
this.startOffset, this.endOffset, expression.type, listOf(this@wrapInStringConcat)
)
fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = false)
// here `StringBuilder`'s list is used to optimize memory, everything works without it
val folded = mutableListOf<IrExpression>()
val buildersList = mutableListOf<StringBuilder>()
for (next in expression.arguments) {
val last = folded.lastOrNull()
when {
!next.wrapInStringConcat().canBeInterpreted() -> {
folded += next
buildersList.add(StringBuilder())
}
last == null || !last.wrapInStringConcat().canBeInterpreted() -> {
val result = next.wrapInToStringConcatAndInterpret()
folded += result
buildersList.add(StringBuilder((result as? IrConst<*>)?.value?.toString() ?: ""))
}
else -> {
val nextAsConst = next.wrapInToStringConcatAndInterpret()
if (nextAsConst !is IrConst<*>) {
folded += next
buildersList.add(StringBuilder())
} else {
folded[folded.size - 1] = IrConstImpl.string(
// Inlined strings may have `last.startOffset > next.endOffset`
min(last.startOffset, next.startOffset), max(last.endOffset, next.endOffset), expression.type, ""
)
buildersList.last().append(nextAsConst.value.toString())
}
}
}
}
val foldedConst = folded.singleOrNull() as? IrConst<*>
if (foldedConst != null) {
return IrConstImpl.string(expression.startOffset, expression.endOffset, expression.type, buildersList.single().toString())
}
folded.zip(buildersList).forEach {
@Suppress("UNCHECKED_CAST")
(it.first as? IrConst<String>)?.value = it.second.toString()
}
return IrStringConcatenationImpl(expression.startOffset, expression.endOffset, expression.type, folded)
}
override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement {
transformAnnotations(declaration)
return super.visitDeclaration(declaration)
}
private fun transformAnnotations(annotationContainer: IrAnnotationContainer) {
annotationContainer.annotations.forEach { annotation ->
transformAnnotation(annotation)
}
}
private fun transformAnnotation(annotation: IrConstructorCall) {
for (i in 0 until annotation.valueArgumentsCount) {
val arg = annotation.getValueArgument(i) ?: continue
when (arg) {
is IrVararg -> annotation.putValueArgument(i, arg.transformVarArg())
else -> annotation.putValueArgument(i, arg.transformSingleArg(annotation.symbol.owner.valueParameters[i].type))
}
}
}
private fun IrVararg.transformVarArg(): IrVararg {
if (elements.isEmpty()) return this
val newIrVararg = IrVarargImpl(this.startOffset, this.endOffset, this.type, this.varargElementType)
for (element in this.elements) {
when (val arg = (element as? IrSpreadElement)?.expression ?: element) {
is IrVararg -> arg.transformVarArg().elements.forEach { newIrVararg.addElement(it) }
is IrExpression -> newIrVararg.addElement(arg.transformSingleArg(this.varargElementType))
else -> newIrVararg.addElement(arg)
}
}
return newIrVararg
}
private fun IrExpression.transformSingleArg(expectedType: IrType): IrExpression {
if (this.canBeInterpreted(configuration = interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) {
return this.interpret(failAsError = true).convertToConstIfPossible(expectedType)
} else if (this is IrConstructorCall) {
transformAnnotation(this)
}
return this
}
private fun IrExpression.convertToConstIfPossible(type: IrType): IrExpression {
return when {
this !is IrConst<*> || type is IrErrorType -> this
type.isArray() -> this.convertToConstIfPossible((type as IrSimpleType).arguments.single().typeOrNull!!)
type.isPrimitiveArray() -> this.convertToConstIfPossible(this.type)
else -> this.value.toIrConst(type, this.startOffset, this.endOffset)
}
}
}
}