diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt index 01c2c6391e8..8f671e7ca57 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt @@ -80,7 +80,7 @@ internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEn } private fun unfoldFunction(function: IrSimpleFunction, environment: IrInterpreterEnvironment) { - if (environment.callStack.getStackCount() >= IrInterpreterEnvironment.MAX_STACK) + if (environment.callStack.getStackCount() >= environment.configuration.maxStack) return StackOverflowError().handleUserException(environment) // SimpleInstruction with function is added in IrCall // It will serve as endpoint for all possible calls, there we drop frame and copy result to new one diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 4565bf74e10..525abf77ff5 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -39,9 +39,7 @@ internal class CustomInstruction(val evaluate: () -> Unit) : Instruction { get() = null } -class IrInterpreter private constructor( - internal val bodyMap: Map, internal val environment: IrInterpreterEnvironment -) { +class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal val bodyMap: Map) { val irBuiltIns: IrBuiltIns get() = environment.irBuiltIns private val callStack: CallStack @@ -50,16 +48,13 @@ class IrInterpreter private constructor( private var commandCount = 0 constructor(irBuiltIns: IrBuiltIns, bodyMap: Map = emptyMap()) : - this(bodyMap, IrInterpreterEnvironment(irBuiltIns, CallStack())) + this(IrInterpreterEnvironment(irBuiltIns), bodyMap) - private constructor(environment: IrInterpreterEnvironment, bodyMap: Map = emptyMap()) : - this(bodyMap, environment) - - constructor(irModule: IrModuleFragment) : this(emptyMap(), IrInterpreterEnvironment(irModule)) + constructor(irModule: IrModuleFragment) : this(IrInterpreterEnvironment(irModule), emptyMap()) private fun incrementAndCheckCommands() { commandCount++ - if (commandCount >= IrInterpreterEnvironment.MAX_COMMANDS) InterpreterTimeOutError().handleUserException(environment) + if (commandCount >= environment.configuration.maxCommands) InterpreterTimeOutError().handleUserException(environment) } private fun getUnitState(): State = environment.mapOfObjects[irBuiltIns.unitClass]!! @@ -395,8 +390,9 @@ class IrInterpreter private constructor( environment.mapOfObjects[objectClass.symbol] = state // must set object's state here to avoid cyclic evaluation callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state)) - // non compile time objects can be used in interpreter, for example, to evaluate const properties - if (!objectClass.hasAnnotation(compileTimeAnnotation)) return@interceptGetObjectValue callStack.pushState(state) + // non compile time objects can be used in interpreter, for example, to evaluate const properties or in tests + if (!objectClass.hasAnnotation(compileTimeAnnotation) && !environment.configuration.createNonCompileTimeObjects) + return@interceptGetObjectValue callStack.pushState(state) val constructor = objectClass.constructors.firstOrNull() ?: return@interceptGetObjectValue callStack.pushState(state) val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol) 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 new file mode 100644 index 00000000000..8f4a0ad16d4 --- /dev/null +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterConfiguration.kt @@ -0,0 +1,20 @@ +/* + * 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.ir.interpreter + +/** + * @param maxStack describes the maximum allowed call stack size + * @param maxCommands describes the maximum allowed number of simple instructions performed + * @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 + */ +// TODO maybe create some sort of builder +class IrInterpreterConfiguration( + val maxStack: Int = 10_000, + val maxCommands: Int = 1_000_000, + val createNonCompileTimeObjects: Boolean = false, +) diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt index 87416082310..2e06bdbcbe8 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreterEnvironment.kt @@ -14,22 +14,26 @@ import org.jetbrains.kotlin.ir.interpreter.state.Complex import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.util.isSubclassOf -internal class IrInterpreterEnvironment(val irBuiltIns: IrBuiltIns, val callStack: CallStack) { - val irExceptions = mutableListOf() - var mapOfEnums = mutableMapOf() - var mapOfObjects = mutableMapOf() +class IrInterpreterEnvironment( + val irBuiltIns: IrBuiltIns, + val configuration: IrInterpreterConfiguration = IrInterpreterConfiguration(), +) { + internal val callStack: CallStack = CallStack() + internal val irExceptions = mutableListOf() + internal var mapOfEnums = mutableMapOf() + internal var mapOfObjects = mutableMapOf() init { mapOfObjects[irBuiltIns.unitClass] = Common(irBuiltIns.unitClass.owner) } - private constructor(environment: IrInterpreterEnvironment) : this(environment.irBuiltIns, CallStack()) { + private constructor(environment: IrInterpreterEnvironment) : this(environment.irBuiltIns, configuration = environment.configuration) { irExceptions.addAll(environment.irExceptions) mapOfEnums = environment.mapOfEnums mapOfObjects = environment.mapOfObjects } - constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins, CallStack()) { + constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins) { irExceptions.addAll( irModule.files .flatMap { it.declarations } @@ -41,9 +45,4 @@ internal class IrInterpreterEnvironment(val irBuiltIns: IrBuiltIns, val callStac fun copyWithNewCallStack(): IrInterpreterEnvironment { return IrInterpreterEnvironment(this) } - - companion object { - const val MAX_STACK = 10_000 - const val MAX_COMMANDS = 1_000_000 - } }