use super::{CallResult, VM};
use crate::{
defer_drop,
exception_private::{ExcType, RunError, RunResult},
resource::ResourceTracker,
types::PyTrait,
value::Value,
};
impl<T: ResourceTracker> VM<'_, T> {
pub(super) fn exec_before_with(&mut self) -> RunResult<CallResult> {
let Value::Ref(ctx_id) = *self.peek() else {
return Err(not_a_context_manager(self));
};
let mut ctx = self.heap.read(ctx_id);
if ctx.py_is_context_manager(self) {
ctx.py_enter(ctx_id, self)
} else {
Err(not_a_context_manager(self))
}
}
pub(super) fn exec_with_exit(&mut self) -> RunResult<CallResult> {
let this = self;
let ctx = this.pop();
let Value::Ref(ctx_id) = ctx else {
ctx.drop_with_heap(this);
return Err(RunError::internal("WithExit: expected context-manager ref on stack"));
};
defer_drop!(ctx, this);
this.heap.read(ctx_id).py_exit(ctx_id, this, None)
}
pub(super) fn exec_with_except_start(&mut self) -> RunResult<CallResult> {
let len = self.stack.len();
let Value::Ref(exc_id) = self.stack[len - 1] else {
return Err(RunError::internal("WithExceptStart: expected exception ref on stack"));
};
let Value::Ref(ctx_id) = self.stack[len - 2] else {
return Err(RunError::internal(
"WithExceptStart: expected context-manager ref on stack",
));
};
self.heap.read(ctx_id).py_exit(ctx_id, self, Some(exc_id))
}
}
fn not_a_context_manager<T: ResourceTracker>(vm: &VM<'_, T>) -> RunError {
let ty = vm.peek().py_type_name(vm);
ExcType::type_error_not_context_manager(ty, "__exit__")
}