use super::Builtins;
use crate::{
args::ArgValues,
bytecode::VM,
defer_drop,
exception_private::{ExcType, ExcTypeExt, RunResult},
heap::{HeapData, HeapId, HeapRead, HeapReadOutput},
types::{PyTrait, Tuple, Type},
value::Value,
};
pub fn builtin_isinstance(vm: &mut VM<'_>, args: ArgValues) -> RunResult<Value> {
let (obj, classinfo) = args.get_two_args("isinstance", vm.heap)?;
defer_drop!(obj, vm);
defer_drop!(classinfo, vm);
isinstance_check(obj, classinfo, vm).map(Value::Bool)
}
fn isinstance_check(obj: &Value, classinfo: &Value, vm: &mut VM<'_>) -> RunResult<bool> {
match classinfo {
Value::Builtin(Builtins::Type(t)) => Ok(obj.py_type(vm).is_instance_of(*t)),
Value::Builtin(Builtins::ExcType(handler_type)) => {
Ok(matches!(obj.py_type(vm), Type::Exception(exc_type) if exc_type.is_subclass_of(*handler_type)))
}
Value::Ref(id) if matches!(vm.heap.get(*id), HeapData::Class(_)) => Ok(instance_of_class(obj, *id, vm)),
Value::Ref(id) if matches!(vm.heap.get(*id), HeapData::NamedTupleClass(_)) => {
Ok(instance_of_namedtuple_class(obj, *id, vm))
}
Value::Ref(id) if let HeapReadOutput::Tuple(tuple) = vm.heap.read(*id) => {
isinstance_check_tuple(obj, &tuple, vm)
}
_ => Err(ExcType::isinstance_arg2_error()),
}
}
fn instance_of_class(obj: &Value, class_id: HeapId, vm: &VM<'_>) -> bool {
matches!(obj, Value::Ref(obj_id) if matches!(vm.heap.get(*obj_id), HeapData::Instance(inst) if inst.class() == class_id))
}
fn instance_of_namedtuple_class(obj: &Value, class_id: HeapId, vm: &VM<'_>) -> bool {
matches!(obj, Value::Ref(obj_id) if matches!(vm.heap.get(*obj_id), HeapData::NamedTuple(nt) if nt.class_id() == Some(class_id)))
}
fn isinstance_check_tuple<'h>(obj: &Value, tuple: &HeapRead<'h, Tuple>, vm: &mut VM<'h>) -> RunResult<bool> {
let len = tuple.get(vm.heap).as_slice().len();
let mut guard = vm.recursion_guard()?;
let vm = &mut *guard;
for i in 0..len {
match &tuple.get(vm.heap).as_slice()[i] {
Value::Builtin(Builtins::Type(t)) => {
if obj.py_type(vm).is_instance_of(*t) {
return Ok(true);
}
}
Value::Builtin(Builtins::ExcType(exc)) => {
if matches!(obj.py_type(vm), Type::Exception(et) if et.is_subclass_of(*exc)) {
return Ok(true);
}
}
Value::Ref(id) if matches!(vm.heap.get(*id), HeapData::Class(_)) => {
if instance_of_class(obj, *id, vm) {
return Ok(true);
}
}
Value::Ref(id) if matches!(vm.heap.get(*id), HeapData::NamedTupleClass(_)) => {
if instance_of_namedtuple_class(obj, *id, vm) {
return Ok(true);
}
}
Value::Ref(nested_id) if let HeapReadOutput::Tuple(nested) = vm.heap.read(*nested_id) => {
if isinstance_check_tuple(obj, &nested, vm)? {
return Ok(true);
}
}
_ => return Err(ExcType::isinstance_arg2_error()),
}
}
Ok(false)
}