diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 492ffbcd48d..b9c42272d1c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLamb import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering import org.jetbrains.kotlin.backend.common.phaser.* +import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.ir.backend.js.lower.* import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering import org.jetbrains.kotlin.ir.backend.js.lower.cleanup.CleanupLowering @@ -836,11 +837,16 @@ private val jsSuspendArityStorePhase = makeDeclarationTransformerPhase( ) val constEvaluationPhase = makeJsModulePhase( - { - ConstEvaluationLowering( - it, - configuration = IrInterpreterConfiguration(printOnlyExceptionMessage = true, platform = JsPlatforms.defaultJsPlatform) + { context -> + // We can't inline `const val`s because this lowering can mess up incremental compilation. + // For example, if we inline some constant located in `lib` module then we are not going to track and update its value on change. + // The only usages of `const val`s that we allow to inline are the ones that are located at the same file as declaration. + val configuration = IrInterpreterConfiguration( + printOnlyExceptionMessage = true, + platform = JsPlatforms.defaultJsPlatform, + inlineConstVal = false ) + ConstEvaluationLowering(context, configuration = configuration) }, name = "ConstEvaluationLowering", description = "Evaluate functions that are marked as `IntrinsicConstEvaluation`", diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterConfiguration.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterConfiguration.kt index 5ca85d32985..187aef56f51 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterConfiguration.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterConfiguration.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.platform.TargetPlatform * @param createNonCompileTimeObjects * 'true' - interpreter will construct object and initialize its properties despite the fact it is not marked as compile time; * 'false' - interpreter will create a representation of empty object, that can be used to get const properties + * @param inlineConstVal tell the interpreter that value of const property can be inlined instead of getter call */ // TODO maybe create some sort of builder data class IrInterpreterConfiguration( @@ -23,4 +24,5 @@ data class IrInterpreterConfiguration( val createNonCompileTimeObjects: Boolean = false, val printOnlyExceptionMessage: Boolean = false, val collapseStackTraceFromJDK: Boolean = true, + val inlineConstVal: Boolean = true, ) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt index 3ba4ab0b813..e327703d550 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt @@ -6,12 +6,14 @@ package org.jetbrains.kotlin.ir.interpreter.checker import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration import org.jetbrains.kotlin.ir.visitors.IrElementVisitor interface IrInterpreterChecker : IrElementVisitor class IrInterpreterCheckerData( + val irFile: IrFile, val mode: EvaluationMode, val irBuiltIns: IrBuiltIns, val interpreterConfiguration: IrInterpreterConfiguration, diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterCommonChecker.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterCommonChecker.kt index 01fb325760d..e467eaae257 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterCommonChecker.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterCommonChecker.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.ir.interpreter.isAccessToNotNullableObject import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor.Companion.isEnumName import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor.Companion.isKCallableNameCall import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.fileOrNull import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.statements @@ -59,22 +59,20 @@ class IrInterpreterCommonChecker : IrInterpreterChecker { override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean { val owner = expression.symbol.owner - - if (expression.dispatchReceiver.isAccessToNotNullableObject() && expression.isGetterToConstVal()) { - return visitBodyIfNeeded(owner, data) + return when { + !data.interpreterConfiguration.inlineConstVal && expression.isGetterToConstVal() && data.irFile != owner.fileOrNull -> false + expression.dispatchReceiver.isAccessToNotNullableObject() && expression.isGetterToConstVal() -> visitBodyIfNeeded(owner, data) + !data.mode.canEvaluateExpression(expression) || !data.mode.canEvaluateFunction(owner) -> false + expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName() -> true + else -> { + val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true + val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true + dispatchReceiverComputable && + extensionReceiverComputable && + visitValueArguments(expression, data) && + visitBodyIfNeeded(owner, data) + } } - - if (!data.mode.canEvaluateExpression(expression)) return false - - if (!data.mode.canEvaluateFunction(owner)) return false - - if (expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName()) return true - - val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, data) ?: true - val extensionReceiverComputable = expression.extensionReceiver?.accept(this, data) ?: true - if (!visitValueArguments(expression, data)) return false - val bodyComputable = visitBodyIfNeeded(owner, data) - return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable } override fun visitVariable(declaration: IrVariable, data: IrInterpreterCheckerData): Boolean { @@ -176,6 +174,7 @@ class IrInterpreterCommonChecker : IrInterpreterChecker { return owner.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && owner.isStatic && owner.isFinal && (owner.type.isPrimitiveType() || owner.type.isStringClassType()) } + return owner.asVisited { when { // TODO fix later; used it here because java boolean resolves very strange, diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt index 2248f564a9c..f9bd673af93 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt @@ -119,7 +119,7 @@ internal abstract class IrConstTransformer( configuration: IrInterpreterConfiguration = interpreter.environment.configuration ): Boolean { return try { - this.accept(checker, IrInterpreterCheckerData(mode, interpreter.irBuiltIns, configuration)) + this.accept(checker, IrInterpreterCheckerData(irFile, mode, interpreter.irBuiltIns, configuration)) } catch (e: Throwable) { if (suppressExceptions) { return false