Support type check for deep nested arrays in interpreter
This commit is contained in:
committed by
TeamCityServer
parent
4ad88679fd
commit
b9decc3b30
+19
-30
@@ -21,10 +21,8 @@ import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.buildSimpleType
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
internal interface Instruction {
|
||||
@@ -179,28 +177,33 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
|
||||
val irFunction = dispatchReceiver?.getIrFunctionByIrCall(call) ?: call.symbol.owner
|
||||
val args = listOfNotNull(dispatchReceiver.getThisOrSuperReceiver(irFunction), extensionReceiver) + valueArguments
|
||||
|
||||
// 3. evaluate reified type arguments; must do it here, before new frame, because outer type arguments can be loaded at this point
|
||||
val reifiedTypeArguments = irFunction.typeParameters.filter { it.isReified }
|
||||
.map {
|
||||
val reifiedType = call.getTypeArgument(it.index)!!.getTypeIfReified(callStack)
|
||||
Variable(it.symbol, KTypeState(reifiedType, irBuiltIns.anyClass.owner))
|
||||
}
|
||||
|
||||
callStack.newFrame(irFunction)
|
||||
callStack.addInstruction(SimpleInstruction(irFunction))
|
||||
|
||||
// 3. load up values onto stack; do it at first to set low priority of these variables
|
||||
// 4. load up values onto stack; do it at first to set low priority of these variables
|
||||
if (dispatchReceiver is StateWithClosure) callStack.loadUpValues(dispatchReceiver)
|
||||
if (extensionReceiver is StateWithClosure) callStack.loadUpValues(extensionReceiver)
|
||||
if (irFunction.isLocal) callStack.copyUpValuesFromPreviousFrame()
|
||||
|
||||
// 5. store arguments in memory (remap args on actual names)
|
||||
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> callStack.addVariable(Variable(it, receiver)) } }
|
||||
irFunction.getExtensionReceiver()?.let { callStack.addVariable(Variable(it, extensionReceiver ?: callStack.getState(it))) }
|
||||
irFunction.valueParameters.forEachIndexed { i, param -> callStack.addVariable(Variable(param.symbol, valueArguments[i])) }
|
||||
// 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)))
|
||||
|
||||
// 4. store arguments in memory (remap args on actual names)
|
||||
irFunction.getDispatchReceiver()?.let { dispatchReceiver?.let { receiver -> callStack.addVariable(Variable(it, receiver)) } }
|
||||
irFunction.getExtensionReceiver()?.let { callStack.addVariable(Variable(it, extensionReceiver ?: callStack.getState(it))) }
|
||||
irFunction.valueParameters.forEachIndexed { i, param -> callStack.addVariable(Variable(param.symbol, valueArguments[i])) }
|
||||
// 6. store reified type parameters
|
||||
reifiedTypeArguments.forEach { callStack.addVariable(it) }
|
||||
|
||||
// 5. store reified type parameters
|
||||
irFunction.typeParameters.filter { it.isReified }
|
||||
.forEach { callStack.addVariable(Variable(it.symbol, KTypeState(call.getTypeArgument(it.index)!!, irBuiltIns.anyClass.owner))) }
|
||||
|
||||
// 6. load outer class object
|
||||
// 7. load outer class object
|
||||
if (dispatchReceiver is Complex && irFunction.parentClassOrNull?.isInner == true) dispatchReceiver.loadOuterClassesInto(callStack)
|
||||
|
||||
callInterceptor.interceptCall(call, irFunction, args) {
|
||||
@@ -444,25 +447,10 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
|
||||
}
|
||||
|
||||
private fun interpretTypeOperatorCall(expression: IrTypeOperatorCall) {
|
||||
fun IrClassifierSymbol.getTypeFromStack(): IrType {
|
||||
return (callStack.getState(this) as KTypeState).irType
|
||||
}
|
||||
|
||||
fun IrType.replaceArgumentIfReified(): IrType {
|
||||
if (this !is IrSimpleType) return this
|
||||
val argument = this.arguments.singleOrNull()?.typeOrNull?.classifierOrNull
|
||||
return when {
|
||||
argument is IrTypeParameterSymbol && argument.owner.isReified -> {
|
||||
this.buildSimpleType { arguments = listOf(argument.getTypeFromStack() as IrTypeArgument) }
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
val typeClassifier = expression.typeOperand.classifierOrFail
|
||||
val isReified = (typeClassifier.owner as? IrTypeParameter)?.isReified == true
|
||||
val isErased = typeClassifier.owner is IrTypeParameter && !isReified
|
||||
val typeOperand = (if (isReified) typeClassifier.getTypeFromStack() else expression.typeOperand).replaceArgumentIfReified()
|
||||
val typeOperand = expression.typeOperand.getTypeIfReified(callStack)
|
||||
|
||||
val state = callStack.popState()
|
||||
when (expression.operator) {
|
||||
@@ -472,6 +460,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
|
||||
}
|
||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
|
||||
when {
|
||||
// this first check is performed inside `isSubtypeOf` but repeated here to create separate exception
|
||||
state.isNull() && !typeOperand.isNullable() -> NullPointerException().handleUserException(environment)
|
||||
!isErased && !state.isSubtypeOf(typeOperand) -> {
|
||||
val castedClassName = state.irClass.fqNameWhenAvailable
|
||||
@@ -487,11 +476,11 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
|
||||
}
|
||||
}
|
||||
IrTypeOperator.INSTANCEOF -> {
|
||||
val isInstance = state.isSubtypeOf(typeOperand) || isErased
|
||||
val isInstance = isErased || state.isSubtypeOf(typeOperand)
|
||||
callStack.pushState(isInstance.toState(irBuiltIns.booleanType))
|
||||
}
|
||||
IrTypeOperator.NOT_INSTANCEOF -> {
|
||||
val isInstance = state.isSubtypeOf(typeOperand) || isErased
|
||||
val isInstance = isErased || state.isSubtypeOf(typeOperand)
|
||||
callStack.pushState((!isInstance).toState(irBuiltIns.booleanType))
|
||||
}
|
||||
IrTypeOperator.IMPLICIT_NOTNULL -> {
|
||||
|
||||
@@ -13,10 +13,8 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException
|
||||
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
|
||||
import org.jetbrains.kotlin.ir.interpreter.proxy.wrap
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.Primitive
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.State
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.Wrapper
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.isSubtypeOf
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.reflection.KTypeState
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
@@ -247,3 +245,28 @@ internal fun IrValueParameter.getDefaultWithActualParameters(
|
||||
}
|
||||
return expression?.deepCopyWithSymbols(newParent)?.transform(transformer, null)
|
||||
}
|
||||
|
||||
internal fun IrType.getTypeIfReified(callStack: CallStack): IrType {
|
||||
return this.getTypeIfReified { (callStack.getState(it) as KTypeState).irType }
|
||||
}
|
||||
|
||||
internal fun IrType.getTypeIfReified(getType: (IrClassifierSymbol) -> IrType): IrType {
|
||||
if (this !is IrSimpleType) return this
|
||||
|
||||
val owner = this.classifierOrNull?.owner
|
||||
if (owner is IrTypeParameter && owner.isReified) {
|
||||
return (getType(owner.symbol) as IrSimpleType)
|
||||
.buildSimpleType { hasQuestionMark = this.hasQuestionMark || this@getTypeIfReified.hasQuestionMark }
|
||||
}
|
||||
|
||||
val newArguments = this.arguments.map {
|
||||
val type = it.typeOrNull ?: return@map it
|
||||
val typeOwner = type.classifierOrNull?.owner
|
||||
if (typeOwner is IrTypeParameter && !typeOwner.isReified) return@map it
|
||||
type.getTypeIfReified(getType) as IrTypeArgument
|
||||
}
|
||||
return this.buildSimpleType {
|
||||
hasQuestionMark = this.hasQuestionMark || this@getTypeIfReified.hasQuestionMark
|
||||
arguments = newArguments
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -267,8 +267,12 @@ internal object ArrayConstructor : IntrinsicBase() {
|
||||
}
|
||||
}
|
||||
|
||||
val type = environment.callStack.getState(irFunction.symbol) as KTypeState
|
||||
environment.callStack.pushState(arrayValue.toPrimitiveStateArray(type.irType))
|
||||
val state = irFunction.valueParameters.getOrNull(1)?.symbol?.let { environment.callStack.getState(it) as StateWithClosure }
|
||||
val typeFromMemory = (environment.callStack.getState(irFunction.symbol) as KTypeState).irType
|
||||
val type = state?.let {
|
||||
typeFromMemory.getTypeIfReified { typeSymbol -> (it.upValues.single { it.symbol == typeSymbol }.state as KTypeState).irType }
|
||||
}
|
||||
environment.callStack.pushState(arrayValue.toPrimitiveStateArray(type ?: typeFromMemory))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ internal fun State.wrap(callInterceptor: CallInterceptor, remainArraysAsIs: Bool
|
||||
is ExceptionState -> this
|
||||
is Wrapper -> this.value
|
||||
is Primitive<*> -> when {
|
||||
this.isNull() -> null
|
||||
this.type.isArray() || this.type.isPrimitiveArray() -> if (remainArraysAsIs) this else this.value
|
||||
else -> this.value
|
||||
}
|
||||
@@ -43,10 +44,10 @@ internal fun State.wrap(callInterceptor: CallInterceptor, remainArraysAsIs: Bool
|
||||
* Prepare state object to be passed in outer world
|
||||
*/
|
||||
internal fun List<State>.wrap(callInterceptor: CallInterceptor, irFunction: IrFunction, methodType: MethodType? = null): List<Any?> {
|
||||
val name = irFunction.fqNameWhenAvailable?.asString()
|
||||
val name = irFunction.fqNameWhenAvailable?.asString() ?: ""
|
||||
return this.mapIndexed { index, state ->
|
||||
// don't get arrays from Primitive in case of "set" and "Pair.<init>"; information about type will be lost
|
||||
val unwrapArrays = name?.let { (it == "kotlin.Array.set" && index != 0) || it == "kotlin.Pair.<init>" } ?: false
|
||||
val unwrapArrays = (name == "kotlin.Array.set" && index != 0) || name == "kotlin.Pair.<init>" || name == "kotlin.internal.ir.CHECK_NOT_NULL"
|
||||
state.wrap(callInterceptor, unwrapArrays, methodType?.parameterType(index))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.state
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException
|
||||
@@ -53,16 +50,17 @@ internal fun State.asBooleanOrNull() = (this as? Primitive<*>)?.value as? Boolea
|
||||
internal fun State.asStringOrNull() = (this as Primitive<*>).value as? String
|
||||
|
||||
internal fun State.isSubtypeOf(other: IrType): Boolean {
|
||||
if (this.isNull() && other.isNullable()) return true
|
||||
if (this is Primitive<*> && this.value == null) return other.isNullable()
|
||||
if (this is ExceptionState) return this.isSubtypeOf(other.classOrNull!!.owner)
|
||||
|
||||
if (this is Primitive<*> && this.type.isArray() && other.isArray()) {
|
||||
if (this is Primitive<*> && (this.type.isArray() || this.type.isNullableArray()) && (other.isArray() || other.isNullableArray())) {
|
||||
fun IrType.arraySubtypeCheck(other: IrType): Boolean {
|
||||
if (other !is IrSimpleType || this !is IrSimpleType) return false
|
||||
val thisArgument = this.arguments.single().typeOrNull ?: return false
|
||||
val otherArgument = other.arguments.single().typeOrNull ?: return other.arguments.single() is IrStarProjection
|
||||
if (thisArgument.isArray() && otherArgument.isArray()) return thisArgument.arraySubtypeCheck(otherArgument)
|
||||
if (otherArgument.classOrNull == null) return false
|
||||
if (otherArgument.classOrNull == null) return true
|
||||
return thisArgument.classOrNull?.isSubtypeOfClass(otherArgument.classOrNull!!) ?: false
|
||||
}
|
||||
return this.type.arraySubtypeCheck(other)
|
||||
|
||||
Reference in New Issue
Block a user