Add configuration into IrInterpreterEnvironment

This configuration will contains all necessary setting for interpreter
and will be replenished over time
This commit is contained in:
Ivan Kylchik
2021-06-10 16:31:47 +03:00
committed by TeamCityServer
parent b150cc9537
commit cc56acc2c2
4 changed files with 38 additions and 23 deletions
@@ -80,7 +80,7 @@ internal fun unfoldInstruction(element: IrElement?, environment: IrInterpreterEn
} }
private fun unfoldFunction(function: IrSimpleFunction, environment: IrInterpreterEnvironment) { 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) return StackOverflowError().handleUserException(environment)
// SimpleInstruction with function is added in IrCall // 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 // It will serve as endpoint for all possible calls, there we drop frame and copy result to new one
@@ -39,9 +39,7 @@ internal class CustomInstruction(val evaluate: () -> Unit) : Instruction {
get() = null get() = null
} }
class IrInterpreter private constructor( class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal val bodyMap: Map<IdSignature, IrBody>) {
internal val bodyMap: Map<IdSignature, IrBody>, internal val environment: IrInterpreterEnvironment
) {
val irBuiltIns: IrBuiltIns val irBuiltIns: IrBuiltIns
get() = environment.irBuiltIns get() = environment.irBuiltIns
private val callStack: CallStack private val callStack: CallStack
@@ -50,16 +48,13 @@ class IrInterpreter private constructor(
private var commandCount = 0 private var commandCount = 0
constructor(irBuiltIns: IrBuiltIns, bodyMap: Map<IdSignature, IrBody> = emptyMap()) : constructor(irBuiltIns: IrBuiltIns, bodyMap: Map<IdSignature, IrBody> = emptyMap()) :
this(bodyMap, IrInterpreterEnvironment(irBuiltIns, CallStack())) this(IrInterpreterEnvironment(irBuiltIns), bodyMap)
private constructor(environment: IrInterpreterEnvironment, bodyMap: Map<IdSignature, IrBody> = emptyMap()) : constructor(irModule: IrModuleFragment) : this(IrInterpreterEnvironment(irModule), emptyMap())
this(bodyMap, environment)
constructor(irModule: IrModuleFragment) : this(emptyMap(), IrInterpreterEnvironment(irModule))
private fun incrementAndCheckCommands() { private fun incrementAndCheckCommands() {
commandCount++ 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]!! 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 environment.mapOfObjects[objectClass.symbol] = state // must set object's state here to avoid cyclic evaluation
callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state)) callStack.addVariable(Variable(objectClass.thisReceiver!!.symbol, state))
// non compile time objects can be used in interpreter, for example, to evaluate const properties // non compile time objects can be used in interpreter, for example, to evaluate const properties or in tests
if (!objectClass.hasAnnotation(compileTimeAnnotation)) return@interceptGetObjectValue callStack.pushState(state) if (!objectClass.hasAnnotation(compileTimeAnnotation) && !environment.configuration.createNonCompileTimeObjects)
return@interceptGetObjectValue callStack.pushState(state)
val constructor = objectClass.constructors.firstOrNull() ?: return@interceptGetObjectValue callStack.pushState(state) val constructor = objectClass.constructors.firstOrNull() ?: return@interceptGetObjectValue callStack.pushState(state)
val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol) val constructorCall = IrConstructorCallImpl.fromSymbolOwner(constructor.returnType, constructor.symbol)
@@ -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,
)
@@ -14,22 +14,26 @@ import org.jetbrains.kotlin.ir.interpreter.state.Complex
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.isSubclassOf import org.jetbrains.kotlin.ir.util.isSubclassOf
internal class IrInterpreterEnvironment(val irBuiltIns: IrBuiltIns, val callStack: CallStack) { class IrInterpreterEnvironment(
val irExceptions = mutableListOf<IrClass>() val irBuiltIns: IrBuiltIns,
var mapOfEnums = mutableMapOf<IrSymbol, Complex>() val configuration: IrInterpreterConfiguration = IrInterpreterConfiguration(),
var mapOfObjects = mutableMapOf<IrSymbol, Complex>() ) {
internal val callStack: CallStack = CallStack()
internal val irExceptions = mutableListOf<IrClass>()
internal var mapOfEnums = mutableMapOf<IrSymbol, Complex>()
internal var mapOfObjects = mutableMapOf<IrSymbol, Complex>()
init { init {
mapOfObjects[irBuiltIns.unitClass] = Common(irBuiltIns.unitClass.owner) 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) irExceptions.addAll(environment.irExceptions)
mapOfEnums = environment.mapOfEnums mapOfEnums = environment.mapOfEnums
mapOfObjects = environment.mapOfObjects mapOfObjects = environment.mapOfObjects
} }
constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins, CallStack()) { constructor(irModule: IrModuleFragment) : this(irModule.irBuiltins) {
irExceptions.addAll( irExceptions.addAll(
irModule.files irModule.files
.flatMap { it.declarations } .flatMap { it.declarations }
@@ -41,9 +45,4 @@ internal class IrInterpreterEnvironment(val irBuiltIns: IrBuiltIns, val callStac
fun copyWithNewCallStack(): IrInterpreterEnvironment { fun copyWithNewCallStack(): IrInterpreterEnvironment {
return IrInterpreterEnvironment(this) return IrInterpreterEnvironment(this)
} }
companion object {
const val MAX_STACK = 10_000
const val MAX_COMMANDS = 1_000_000
}
} }