diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index 906dd125d05..57a8ea56be4 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -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 -> { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt index 93ecc43abea..03ed3436723 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt @@ -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 + } +} \ No newline at end of file diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt index 73010806a49..f67ba72bee5 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt @@ -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)) } } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/proxy/Proxy.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/proxy/Proxy.kt index d33bb3bd199..16025376a16 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/proxy/Proxy.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/proxy/Proxy.kt @@ -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.wrap(callInterceptor: CallInterceptor, irFunction: IrFunction, methodType: MethodType? = null): List { - 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."; information about type will be lost - val unwrapArrays = name?.let { (it == "kotlin.Array.set" && index != 0) || it == "kotlin.Pair." } ?: false + val unwrapArrays = (name == "kotlin.Array.set" && index != 0) || name == "kotlin.Pair." || name == "kotlin.internal.ir.CHECK_NOT_NULL" state.wrap(callInterceptor, unwrapArrays, methodType?.parameterType(index)) } } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt index feceabb679e..f9d4802fda7 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/State.kt @@ -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)