Implement better support for default args in interpreter

Now supported default args:
1. from super class or interface;
2. with field access, declared in interface;
3. with field access, declared in outer class.
This commit is contained in:
Ivan Kylchik
2021-06-09 21:23:15 +03:00
committed by TeamCityServer
parent 42ea17b151
commit b150cc9537
4 changed files with 86 additions and 9 deletions
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException
import org.jetbrains.kotlin.ir.interpreter.exceptions.verify
@@ -21,6 +22,7 @@ import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
internal fun IrExpression.handleAndDropResult(callStack: CallStack, dropOnlyUnit: Boolean = false) {
val dropResult = fun() {
@@ -131,11 +133,31 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, callSt
callStack.addInstruction(SimpleInstruction(expression))
fun getDefaultForParameterAt(index: Int): IrExpression? {
fun IrExpressionBody.replaceGetValueFromOtherClass(): IrExpressionBody {
return this.deepCopyWithSymbols(irFunction).transform(
object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val parameter = expression.symbol.owner as? IrValueParameter ?: return super.visitGetValue(expression)
if (parameter.parent == irFunction) return super.visitGetValue(expression)
val newParameter = when (val indexInParameters = parameter.index) {
-1 -> (irFunction.dispatchReceiverParameter ?: irFunction.extensionReceiverParameter)!!
else -> irFunction.valueParameters[indexInParameters]
}
return IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, newParameter.symbol)
}
}, null
)
}
fun IrValueParameter.getDefault(): IrExpressionBody? {
return defaultValue
?: (this.parent as? IrSimpleFunction)?.overriddenSymbols
?.map { it.owner.valueParameters[this.index].getDefault() }
?.firstNotNullOfOrNull { it }
if (defaultValue != null) return defaultValue
val overriddenDefault = (this.parent as? IrSimpleFunction)?.overriddenSymbols
?.map { it.owner.valueParameters[this.index] }
?.firstNotNullOfOrNull { it.getDefault() }
if (overriddenDefault == null || overriddenDefault.expression is IrConst<*>) return overriddenDefault
return overriddenDefault.replaceGetValueFromOtherClass()
}
return irFunction.valueParameters[index].getDefault()?.expression
@@ -167,6 +167,9 @@ 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))
// outer classes can be used in default args evaluation
if (isReceiver() && state is Complex) state.loadOuterClassesInto(callStack)
}
private fun interpretCall(call: IrCall) {
@@ -204,9 +207,7 @@ class IrInterpreter private constructor(
.forEach { callStack.addVariable(Variable(it.symbol, KTypeState(call.getTypeArgument(it.index)!!, irBuiltIns.anyClass.owner))) }
// 5. load outer class object
if (dispatchReceiver is Complex && irFunction.parentClassOrNull?.isInner == true) {
generateSequence(dispatchReceiver.outerClass) { (it.state as? Complex)?.outerClass }.forEach { callStack.addVariable(it) }
}
if (dispatchReceiver is Complex && irFunction.parentClassOrNull?.isInner == true) dispatchReceiver.loadOuterClassesInto(callStack)
// 6. load up values onto stack
if (irFunction.isLocal) callStack.copyUpValuesFromPreviousFrame()
@@ -277,7 +278,7 @@ class IrInterpreter private constructor(
callStack.addVariable(Variable(receiverSymbol, outerClass))
}
// used to get information from outer class
generateSequence(outerClassVar) { (it.state as? Complex)?.outerClass }.forEach { callStack.addVariable(it) }
objectState.loadOuterClassesInto(callStack)
}
if (irClass.isLocal) callStack.loadUpValues(objectState as StateWithClosure)
@@ -12,8 +12,8 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.*
@@ -51,4 +51,8 @@ internal interface Complex: State {
val irFunction = getIrFunctionFromGivenClass(receiver, expression.symbol) ?: return null
return getOverridden(irFunction as IrSimpleFunction)
}
fun loadOuterClassesInto(callStack: CallStack) {
generateSequence(outerClass) { (it.state as? Complex)?.outerClass }.forEach { callStack.addVariable(it) }
}
}