use std::{
marker::PhantomData,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
};
use super::VM;
use crate::{
heap::ContainsHeap,
resource::{ResourceError, ResourceTracker},
};
impl<'h, T: ResourceTracker> VM<'h, T> {
pub(crate) fn recursion_guard(&mut self) -> Result<RecursionGuard<'_, 'h, T>, ResourceError> {
self.incr_recursion()?;
Ok(RecursionGuard { vm: self })
}
pub(crate) fn recursion_token(&mut self) -> Result<RecursionToken, ResourceError> {
self.incr_recursion()?;
Ok(RecursionToken(()))
}
#[inline]
pub(crate) fn incr_recursion(&mut self) -> Result<(), ResourceError> {
self.heap.tracker().check_recursion_depth(self.recursion_depth)?;
self.recursion_depth += 1;
Ok(())
}
#[inline]
pub(crate) fn decr_recursion(&mut self) {
debug_assert!(self.recursion_depth > 0, "decr_recursion called when depth is 0");
self.recursion_depth -= 1;
}
}
pub(crate) struct RecursionGuard<'a, 'h, T: ResourceTracker> {
vm: &'a mut VM<'h, T>,
}
impl<'h, T: ResourceTracker> Deref for RecursionGuard<'_, 'h, T> {
type Target = VM<'h, T>;
fn deref(&self) -> &Self::Target {
self.vm
}
}
impl<T: ResourceTracker> DerefMut for RecursionGuard<'_, '_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.vm
}
}
impl<T: ResourceTracker> Drop for RecursionGuard<'_, '_, T> {
fn drop(&mut self) {
self.vm.decr_recursion();
}
}
pub(crate) const MAX_RUN_REENTRY_DEPTH: u8 = 12;
impl<T: ResourceTracker> VM<'_, T> {
#[inline]
pub(crate) fn enter_run_reentry(&mut self) -> Result<(), ResourceError> {
if let Some(new_value) = self.run_reentry_depth.checked_sub(1) {
self.run_reentry_depth = new_value;
Ok(())
} else {
Err(ResourceError::Recursion {
limit: MAX_RUN_REENTRY_DEPTH as usize,
depth: MAX_RUN_REENTRY_DEPTH as usize,
})
}
}
#[inline]
pub(crate) fn release_run_reentry(&mut self) {
debug_assert!(
self.run_reentry_depth < MAX_RUN_REENTRY_DEPTH,
"release_run_reentry called when depth is MAX_RUN_REENTRY_DEPTH"
);
self.run_reentry_depth += 1;
}
}
pub(crate) struct RunReentryGuard<'a, 'h, T: ResourceTracker> {
vm: &'a mut VM<'h, T>,
}
impl<'a, 'h, T: ResourceTracker> RunReentryGuard<'a, 'h, T> {
pub(crate) fn new(vm: &'a mut VM<'h, T>) -> Self {
Self { vm }
}
}
impl<'h, T: ResourceTracker> Deref for RunReentryGuard<'_, 'h, T> {
type Target = VM<'h, T>;
fn deref(&self) -> &Self::Target {
self.vm
}
}
impl<T: ResourceTracker> DerefMut for RunReentryGuard<'_, '_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.vm
}
}
impl<T: ResourceTracker> Drop for RunReentryGuard<'_, '_, T> {
fn drop(&mut self) {
self.vm.release_run_reentry();
}
}
pub(crate) struct RecursionToken(());
pub(crate) trait ContainsVM<'h>: ContainsHeap {
type Tracker: ResourceTracker + 'h;
fn vm(&mut self) -> &mut VM<'h, Self::Tracker>;
}
impl<'h, T: ResourceTracker> ContainsVM<'h> for VM<'h, T> {
type Tracker = T;
fn vm(&mut self) -> &mut VM<'h, Self::Tracker> {
self
}
}
pub(crate) trait DropWithVM<'h>: Sized {
fn drop_with_vm(self, container: &mut impl ContainsVM<'h>);
}
impl<'h> DropWithVM<'h> for RecursionToken {
fn drop_with_vm(self, container: &mut impl ContainsVM<'h>) {
container.vm().decr_recursion();
}
}
pub(crate) struct VmGuard<'a, 'h, C: ContainsVM<'h>, V: DropWithVM<'h>> {
value: ManuallyDrop<V>,
container: &'a mut C,
phantom: PhantomData<&'a mut VM<'h, C::Tracker>>,
}
impl<'a, 'h, C: ContainsVM<'h>, V: DropWithVM<'h>> VmGuard<'a, 'h, C, V> {
#[inline]
pub(crate) fn new(value: V, container: &'a mut C) -> Self {
Self {
value: ManuallyDrop::new(value),
container,
phantom: PhantomData,
}
}
#[inline]
pub(crate) fn as_parts(&mut self) -> (&V, &mut C) {
(&self.value, self.container)
}
#[inline]
pub(crate) fn as_parts_mut(&mut self) -> (&mut V, &mut C) {
(&mut self.value, self.container)
}
}
impl<'h, C: ContainsVM<'h>, V: DropWithVM<'h>> Drop for VmGuard<'_, 'h, C, V> {
fn drop(&mut self) {
unsafe { ManuallyDrop::take(&mut self.value) }.drop_with_vm(self.container);
}
}
#[macro_export]
macro_rules! defer_drop_vm {
($value:ident, $container:ident) => {
let mut _vm_guard = $crate::bytecode::VmGuard::new($value, $container);
#[allow(
clippy::allow_attributes,
reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
)]
#[allow(unused_variables)]
let ($value, $container) = _vm_guard.as_parts();
};
}
#[macro_export]
macro_rules! defer_drop_vm_mut {
($value:ident, $container:ident) => {
let mut _vm_guard = $crate::bytecode::VmGuard::new($value, $container);
#[allow(
clippy::allow_attributes,
reason = "the reborrowed parts may not both be used in every case, so allow unused vars to avoid warnings"
)]
#[allow(unused_variables)]
let ($value, $container) = _vm_guard.as_parts_mut();
};
}