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) { 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) 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) { private fun unfoldConstructorCall(constructorCall: IrFunctionAccessExpression, callStack: CallStack) {
val constructor = constructorCall.symbol.owner 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 // this variable is used to create object once
callStack.addVariable(Variable(constructorCall.getThisReceiver(), Common(constructor.parentAsClass))) 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) { 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) unfoldValueParameters(enumConstructorCall, callStack)
} }
private fun unfoldDelegatingConstructorCall(delegatingConstructorCall: IrFunctionAccessExpression, callStack: 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) unfoldValueParameters(delegatingConstructorCall, callStack)
} }
private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) { private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callStack: CallStack) {
fun IrValueParameter.getDefault(): IrExpressionBody? { val irFunction = expression.symbol.owner
return defaultValue // new sub frame is used to store value arguments, in case then they are used in default args evaluation
?: (this.parent as? IrSimpleFunction)?.overriddenSymbols callStack.newSubFrame(expression, listOf(SimpleInstruction(expression)))
?.map { it.owner.valueParameters[this.index].getDefault() }
?.firstNotNullOfOrNull { it }
}
fun getDefaultForParameterAt(index: Int): IrExpression? { fun getDefaultForParameterAt(index: Int): IrExpression? {
return expression.symbol.owner.valueParameters[index].getDefault()?.expression fun IrValueParameter.getDefault(): IrExpressionBody? {
} return defaultValue
?: (this.parent as? IrSimpleFunction)?.overriddenSymbols
val irFunction = expression.symbol.owner ?.map { it.owner.valueParameters[this.index].getDefault() }
val valueParametersSymbols = irFunction.valueParameters.map { it.symbol } ?.firstNotNullOfOrNull { it }
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)))
}
} }
return irFunction.valueParameters[index].getDefault()?.expression
} }
// hack for extension receiver in lambda val valueParametersSymbols = irFunction.valueParameters.map { it.symbol }
if (expression.valueArgumentsCount != irFunction.valueParameters.size) {
val extensionReceiver = irFunction.getExtensionReceiver() ?: return // interpret defaults for null arguments (at the end)
callStack.addInstruction(SimpleInstruction(extensionReceiver.owner)) (expression.valueArgumentsCount - 1 downTo 0).forEach { index ->
valueArguments[0]?.let { callStack.addInstruction(CompoundInstruction(it)) } 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) { 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 toStringCall.dispatchReceiver = IrConstImpl.constNull(0, 0, receiver.type) // just stub receiver
callStack.newSubFrame(toStringCall, listOf(SimpleInstruction(toStringCall))) 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) { private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: CallStack) {
val function = reference.symbol.owner val function = reference.symbol.owner
callStack.newSubFrame(reference, listOf()) callStack.newSubFrame(reference, listOf(SimpleInstruction(reference)))
callStack.addInstruction(SimpleInstruction(reference))
reference.extensionReceiver?.let { reference.dispatchReceiver?.let { callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!)) }
callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!)) reference.extensionReceiver?.let { callStack.addInstruction(SimpleInstruction(function.extensionReceiverParameter!!)) }
callStack.addInstruction(CompoundInstruction(it))
} reference.extensionReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
reference.dispatchReceiver?.let { reference.dispatchReceiver?.let { callStack.addInstruction(CompoundInstruction(it)) }
callStack.addInstruction(SimpleInstruction(function.dispatchReceiverParameter!!))
callStack.addInstruction(CompoundInstruction(it))
}
} }
private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) { private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) {
callStack.addInstruction(SimpleInstruction(propertyReference)) 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) { 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 //must add value argument in current stack because it can be used later as default argument
callStack.addVariable(Variable(valueParameter.symbol, state)) callStack.addVariable(Variable(valueParameter.symbol, state))
callStack.pushState(state)
} }
private fun interpretCall(call: IrCall) { private fun interpretCall(call: IrCall) {
val owner = call.symbol.owner
// 1. load evaluated arguments from stack // 1. load evaluated arguments from stack
val valueArguments = call.symbol.owner.valueParameters.map { callStack.popState() }.reversed() var dispatchReceiver = call.dispatchReceiver?.let { callStack.getVariable(owner.getDispatchReceiver()!!) }?.state
val extensionReceiver = call.extensionReceiver?.let { callStack.popState() } val extensionReceiver = call.extensionReceiver?.let { callStack.getVariable(owner.getExtensionReceiver()!!) }?.state
var dispatchReceiver = call.dispatchReceiver?.let { callStack.popState() } val valueArguments = owner.valueParameters.map { callStack.getVariable(it.symbol).state }
// 2. get correct function for interpretation // 2. get correct function for interpretation
val irFunction = dispatchReceiver?.getIrFunctionByIrCall(call) ?: call.symbol.owner val irFunction = dispatchReceiver?.getIrFunctionByIrCall(call) ?: call.symbol.owner
@@ -179,13 +179,13 @@ class IrInterpreter private constructor(
else -> dispatchReceiver else -> dispatchReceiver
} }
callStack.dropSubFrame() callStack.dropSubFrame() // drop intermediate frame that contains variables for default arg evaluation
callStack.newFrame(irFunction, listOf(SimpleInstruction(irFunction))) callStack.newFrame(irFunction, listOf(SimpleInstruction(irFunction)))
// TODO: if using KTypeState then it's class must be corresponding // TODO: if using KTypeState then it's class must be corresponding
// `call.type` is used in check cast and emptyArray // `call.type` is used in check cast and emptyArray
callStack.addVariable(Variable(irFunction.symbol, KTypeState(call.type, irBuiltIns.anyClass.owner))) 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>() val args = mutableListOf<Variable>()
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> args.add(Variable(it, receiver)) } } irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> args.add(Variable(it, receiver)) } }
irFunction.getExtensionReceiver()?.let { args.add(Variable(it, extensionReceiver ?: valueArguments.first())) } irFunction.getExtensionReceiver()?.let { args.add(Variable(it, extensionReceiver ?: valueArguments.first())) }
@@ -241,12 +241,12 @@ class IrInterpreter private constructor(
} }
private fun interpretConstructorCall(constructorCall: IrFunctionAccessExpression) { 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 constructor = constructorCall.symbol.owner
val irClass = constructor.parentAsClass val irClass = constructor.parentAsClass
val objectVar = callStack.getVariable(constructorCall.getThisReceiver()) val objectVar = callStack.getVariable(constructorCall.getThisReceiver())
if (irClass.isLocal) callStack.storeUpValues(objectVar.state as StateWithClosure) 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.dropSubFrame()
callStack.newFrame(constructor, listOf(SimpleInstruction(constructor))) callStack.newFrame(constructor, listOf(SimpleInstruction(constructor)))
@@ -564,8 +564,8 @@ class IrInterpreter private constructor(
val function = KFunctionState(reference) val function = KFunctionState(reference)
val irFunction = function.irFunction val irFunction = function.irFunction
val extensionReceiver = reference.extensionReceiver?.let { callStack.popState() } val dispatchReceiver = reference.dispatchReceiver?.let { callStack.getVariable(irFunction.getDispatchReceiver()!!) }?.state
val dispatchReceiver = reference.dispatchReceiver?.let { callStack.popState() } val extensionReceiver = reference.extensionReceiver?.let { callStack.getVariable(irFunction.getExtensionReceiver()!!) }?.state
callStack.dropSubFrame() callStack.dropSubFrame()
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> function.fields.add(Variable(it, receiver)) } } 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) { 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 // it is impossible to get KProperty2 through ::, so extension receiver is always null
val propertyState = KPropertyState(propertyReference, dispatchReceiver) val propertyState = KPropertyState(propertyReference, dispatchReceiver)
callStack.pushState(propertyState) 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 IrFunction.getCapitalizedFileName() = this.file.name.replace(".kt", "Kt").capitalizeAsciiOnly()
internal fun IrType.isUnsigned() = this.isUByte() || this.isUShort() || this.isUInt() || this.isULong() internal fun IrType.isUnsigned() = this.isUByte() || this.isUShort() || this.isUInt() || this.isULong()
@@ -187,7 +187,7 @@ internal class CallStack {
} }
fun copyUpValuesFromPreviousFrame() { 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> { fun getStackTrace(): List<String> {