Rewrite unfoldValueParameters to take into account recursive calls

The workflow of evaluation value arguments is now like this:
1. Calculate not null arguments
2. Add them into intermediate stack
3. Calculate defaults for those parameters that have no argument
This flow allow us to avoid early addition of variable onto stack.
This commit is contained in:
Ivan Kylchik
2021-04-12 14:04:01 +03:00
committed by TeamCityServer
parent 6411d09579
commit b4463b948c
4 changed files with 61 additions and 83 deletions
@@ -83,84 +83,67 @@ private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack)
}
private fun unfoldCall(call: IrCall, callStack: CallStack) {
val function = call.symbol.owner
// new sub frame is used to store value arguments, in case then they are used in default args evaluation
callStack.newSubFrame(call, listOf())
callStack.addInstruction(SimpleInstruction(call))
unfoldValueParameters(call, callStack)
// must save receivers in memory in case then they are used in default args evaluation
call.extensionReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
call.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldConstructorCall(constructorCall: IrFunctionAccessExpression, callStack: CallStack) {
val constructor = constructorCall.symbol.owner
callStack.newSubFrame(constructorCall, listOf()) // used to store value arguments, in case then they are use as default args
unfoldValueParameters(constructorCall, callStack)
// this variable is used to create object once
callStack.addVariable(Variable(constructorCall.getThisReceiver(), Common(constructor.parentAsClass)))
callStack.addInstruction(SimpleInstruction(constructorCall))
unfoldValueParameters(constructorCall, callStack)
constructorCall.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(constructor.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldEnumConstructorCall(enumConstructorCall: IrEnumConstructorCall, callStack: CallStack) {
callStack.newSubFrame(enumConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args
callStack.addInstruction(SimpleInstruction(enumConstructorCall))
unfoldValueParameters(enumConstructorCall, callStack)
}
private fun unfoldDelegatingConstructorCall(delegatingConstructorCall: IrFunctionAccessExpression, callStack: CallStack) {
callStack.newSubFrame(delegatingConstructorCall, listOf()) // used to store value arguments, in case then they are use as default args
callStack.addInstruction(SimpleInstruction(delegatingConstructorCall))
unfoldValueParameters(delegatingConstructorCall, callStack)
}
private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) {
fun IrValueParameter.getDefault(): IrExpressionBody? {
return defaultValue
?: (this.parent as? IrSimpleFunction)?.overriddenSymbols
?.map { it.owner.valueParameters[this.index].getDefault() }
?.firstNotNullOfOrNull { it }
}
val irFunction = expression.symbol.owner
// new sub frame is used to store value arguments, in case then they are used in default args evaluation
callStack.newSubFrame(expression, listOf(SimpleInstruction(expression)))
fun getDefaultForParameterAt(index: Int): IrExpression? {
return expression.symbol.owner.valueParameters[index].getDefault()?.expression
}
val irFunction = expression.symbol.owner
val valueParametersSymbols = irFunction.valueParameters.map { it.symbol }
val valueArguments = (0 until expression.valueArgumentsCount).map { expression.getValueArgument(it) }
for (i in valueParametersSymbols.indices.reversed()) {
callStack.addInstruction(SimpleInstruction(valueParametersSymbols[i].owner))
val arg = valueArguments[i] ?: getDefaultForParameterAt(i)
when {
arg != null -> callStack.addInstruction(CompoundInstruction(arg))
else ->
// case when value parameter is vararg and it is missing
expression.getVarargType(i)?.let {
callStack.addInstruction(SimpleInstruction(IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it)))
}
fun IrValueParameter.getDefault(): IrExpressionBody? {
return defaultValue
?: (this.parent as? IrSimpleFunction)?.overriddenSymbols
?.map { it.owner.valueParameters[this.index].getDefault() }
?.firstNotNullOfOrNull { it }
}
return irFunction.valueParameters[index].getDefault()?.expression
}
// hack for extension receiver in lambda
if (expression.valueArgumentsCount != irFunction.valueParameters.size) {
val extensionReceiver = irFunction.getExtensionReceiver() ?: return
callStack.addInstruction(SimpleInstruction(extensionReceiver.owner))
valueArguments[0]?.let { callStack.addInstruction(CompoundInstruction(it)) }
val valueParametersSymbols = irFunction.valueParameters.map { it.symbol }
// interpret defaults for null arguments (at the end)
(expression.valueArgumentsCount - 1 downTo 0).forEach { index ->
if (expression.getValueArgument(index) != null) return@forEach
callStack.addInstruction(SimpleInstruction(valueParametersSymbols[index].owner))
getDefaultForParameterAt(index)
?.let { callStack.addInstruction(CompoundInstruction(it)) }
?: expression.getVarargType(index)?.let { // case when value parameter is vararg and it is missing
callStack.addInstruction(SimpleInstruction(IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, it)))
}
}
// interpret not null arguments (after evaluation of arguments)
expression.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(irFunction.dispatchReceiverParameter!!)) }
expression.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(irFunction.extensionReceiverParameter!!)) }
(0 until expression.valueArgumentsCount).forEach {
if (expression.getValueArgument(it) != null) callStack.addInstruction(SimpleInstruction(irFunction.valueParameters[it]))
}
// evaluate arguments
(expression.valueArgumentsCount - 1 downTo 0).forEach {
callStack.addInstruction(CompoundInstruction(expression.getValueArgument(it)))
}
expression.extensionReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
expression.dispatchReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
}
private fun unfoldInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall, callStack: CallStack) {
@@ -360,7 +343,7 @@ private fun unfoldStringConcatenation(expression: IrStringConcatenation, environ
toStringCall.dispatchReceiver = IrConstImpl.constNull(0, 0, receiver.type) // just stub receiver
callStack.newSubFrame(toStringCall, listOf(SimpleInstruction(toStringCall)))
callStack.pushState(state)
callStack.addVariable(Variable(receiver.symbol, state))
}
}
}
@@ -382,22 +365,21 @@ private fun unfoldComposite(element: IrComposite, callStack: CallStack) {
private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: CallStack) {
val function = reference.symbol.owner
callStack.newSubFrame(reference, listOf())
callStack.addInstruction(SimpleInstruction(reference))
callStack.newSubFrame(reference, listOf(SimpleInstruction(reference)))
reference.extensionReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
reference.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
reference.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!)) }
reference.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!)) }
reference.extensionReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
reference.dispatchReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
}
private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(propertyReference))
callStack.addInstruction(CompoundInstruction(propertyReference.dispatchReceiver))
propertyReference.dispatchReceiver?.let {
callStack.addInstruction(SimpleInstruction(propertyReference.getter!!.owner.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
}
private fun unfoldClassReference(classReference: IrClassReference, callStack: CallStack) {
@@ -163,14 +163,14 @@ class IrInterpreter private constructor(
//must add value argument in current stack because it can be used later as default argument
callStack.addVariable(Variable(valueParameter.symbol, state))
callStack.pushState(state)
}
private fun interpretCall(call: IrCall) {
val owner = call.symbol.owner
// 1. load evaluated arguments from stack
val valueArguments = call.symbol.owner.valueParameters.map { callStack.popState() }.reversed()
val extensionReceiver = call.extensionReceiver?.let { callStack.popState() }
var dispatchReceiver = call.dispatchReceiver?.let { callStack.popState() }
var dispatchReceiver = call.dispatchReceiver?.let { callStack.getVariable(owner.getDispatchReceiver()!!) }?.state
val extensionReceiver = call.extensionReceiver?.let { callStack.getVariable(owner.getExtensionReceiver()!!) }?.state
val valueArguments = owner.valueParameters.map { callStack.getVariable(it.symbol).state }
// 2. get correct function for interpretation
val irFunction = dispatchReceiver?.getIrFunctionByIrCall(call) ?: call.symbol.owner
@@ -179,13 +179,13 @@ class IrInterpreter private constructor(
else -> dispatchReceiver
}
callStack.dropSubFrame()
callStack.dropSubFrame() // drop intermediate frame that contains variables for default arg evaluation
callStack.newFrame(irFunction, listOf(SimpleInstruction(irFunction)))
// TODO: if using KTypeState then it's class must be corresponding
// `call.type` is used in check cast and emptyArray
callStack.addVariable(Variable(irFunction.symbol, KTypeState(call.type, irBuiltIns.anyClass.owner)))
// 3. store arguments in memory
// 3. store arguments in memory (remap args on actual names)
val args = mutableListOf<Variable>()
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> args.add(Variable(it, receiver)) } }
irFunction.getExtensionReceiver()?.let { args.add(Variable(it, extensionReceiver ?: valueArguments.first())) }
@@ -241,12 +241,12 @@ class IrInterpreter private constructor(
}
private fun interpretConstructorCall(constructorCall: IrFunctionAccessExpression) {
val valueArguments = constructorCall.symbol.owner.valueParameters.map { callStack.popState() }.reversed()
val valueArguments = constructorCall.symbol.owner.valueParameters.map { callStack.getVariable(it.symbol).state }
val constructor = constructorCall.symbol.owner
val irClass = constructor.parentAsClass
val objectVar = callStack.getVariable(constructorCall.getThisReceiver())
if (irClass.isLocal) callStack.storeUpValues(objectVar.state as StateWithClosure)
val outerClass = if (irClass.isInner) callStack.popState() else null
val outerClass = if (irClass.isInner) callStack.getVariable(constructor.dispatchReceiverParameter!!.symbol).state else null
callStack.dropSubFrame()
callStack.newFrame(constructor, listOf(SimpleInstruction(constructor)))
@@ -564,8 +564,8 @@ class IrInterpreter private constructor(
val function = KFunctionState(reference)
val irFunction = function.irFunction
val extensionReceiver = reference.extensionReceiver?.let { callStack.popState() }
val dispatchReceiver = reference.dispatchReceiver?.let { callStack.popState() }
val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getVariable(irFunction.getDispatchReceiver()!!) }?.state
val extensionReceiver = reference.extensionReceiver?.let { callStack.getVariable(irFunction.getExtensionReceiver()!!) }?.state
callStack.dropSubFrame()
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> function.fields.add(Variable(it, receiver)) } }
@@ -576,7 +576,8 @@ class IrInterpreter private constructor(
}
private fun interpretPropertyReference(propertyReference: IrPropertyReference) {
val dispatchReceiver = propertyReference.dispatchReceiver?.let { callStack.popState() }
val dispatchReceiverSymbol = propertyReference.getter?.owner?.getDispatchReceiver()
val dispatchReceiver = propertyReference.dispatchReceiver?.let { callStack.getVariable(dispatchReceiverSymbol!!) }?.state
// it is impossible to get KProperty2 through ::, so extension receiver is always null
val propertyState = KPropertyState(propertyReference, dispatchReceiver)
callStack.pushState(propertyState)
@@ -179,11 +179,6 @@ fun IrFunctionAccessExpression.getVarargType(index: Int): IrType? {
}
}
internal fun State?.extractNonLocalDeclarations(): List<Variable> {
this ?: return listOf()
return this.fields.filter { it.symbol !is IrFieldSymbol }
}
internal fun IrFunction.getCapitalizedFileName() = this.file.name.replace(".kt", "Kt").capitalizeAsciiOnly()
internal fun IrType.isUnsigned() = this.isUByte() || this.isUShort() || this.isUInt() || this.isULong()
@@ -187,7 +187,7 @@ internal class CallStack {
}
fun copyUpValuesFromPreviousFrame() {
frames[frames.size - 2].getAll().forEach { addVariable(it) }
frames[frames.size - 2].getAll().forEach { if (!containsVariable(it.symbol)) addVariable(it) }
}
fun getStackTrace(): List<String> {