spore_vm/val/
protected_val.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::ops::Deref;

#[allow(unused_imports)]
use log::*;

use crate::Vm;

use super::{
    custom::{CustomValError, CustomValMut, CustomValRef},
    CustomType, Val,
};

/// Holds a value from the [Vm] that is guaranteed to not be garbage collected.
///
/// The underlying value is protected from garbage collection until `ProtectedVal` is dropped.
#[derive(Debug)]
pub struct ProtectedVal<'a> {
    pub(crate) vm: &'a mut Vm,
    pub(crate) val: Val<'a>,
}

impl<'a> ProtectedVal<'a> {
    /// Create a new `Val` from an [crate::val::UnsafeVal].
    ///
    /// # Safety
    /// `v` must be a valid from originating (and not garbage collected) from `vm`.
    pub fn new(vm: &'a mut Vm, v: Val<'a>) -> ProtectedVal<'a> {
        vm.objects.keep_reachable(v.inner);
        ProtectedVal { vm, val: v }
    }

    /// Split the protected val into its [Vm] and [Val].
    ///
    /// Despite the split, the returned `Val` will still be safe from garbage collection.
    pub fn split(&mut self) -> (&mut Vm, &Val) {
        (self.vm, &self.val)
    }

    /// Run function `f` on the [Vm] and [Val] and return the result. This is a convenient way of
    /// gaining mutable reference to the underlying vm.
    pub fn with<T>(&mut self, f: impl Fn(&mut Vm, &Val) -> T) -> T {
        let (vm, val) = self.split();
        let res = f(vm, val);
        res
    }
}

impl<'a> Drop for ProtectedVal<'a> {
    fn drop(&mut self) {
        self.vm.objects.allow_unreachable(self.val.inner);
    }
}

impl<'a> Deref for ProtectedVal<'a> {
    type Target = Val<'a>;

    fn deref(&self) -> &Self::Target {
        &self.val
    }
}

impl<'a> ProtectedVal<'a> {
    /// Get a reference to the underlying [Vm].
    pub fn vm(&self) -> &Vm {
        self.vm
    }

    /// Maps an `Option<T>` to `Option<U>` by applying a function to a contained value (if `Some`)
    /// or returns `None` (if `None`).
    pub fn map<T>(&mut self, f: impl Fn(&mut Vm, &ProtectedVal<'a>) -> T) -> T {
        let protected_val_ptr: *const ProtectedVal = self;
        // Unsafe OK: Protected val will still be safe from garbage collection as drop has not been
        // called.
        f(self.vm, unsafe { &*protected_val_ptr })
    }

    /// Try to get the string as a value or return its underlying value.
    pub fn try_str(&'a self) -> Result<&'a str, Val> {
        self.val.try_str(self.vm)
    }

    /// Try to get the custom value of type `T` or return its underlying value if `self` is not of
    /// type `T`.
    pub fn try_custom<T: CustomType>(&self) -> Result<CustomValRef<T>, CustomValError> {
        self.val.try_custom(self.vm)
    }

    /// Returns the value as a custom type of `T` or [Err] if [Self] is not of the given custom
    /// value.
    pub fn try_custom_mut<T: CustomType>(&self) -> Result<CustomValMut<T>, CustomValError> {
        self.val.try_custom_mut(self.vm)
    }

    /// Get the [Val] that the mutable box is pointing to or `Err<Val>` if `self` is not a mutable
    /// box.
    pub fn get_mutable_box_ref(&self) -> Result<Val, Val<'a>> {
        self.val.try_mutable_box_ref(self.vm)
    }
}

impl<'a> std::fmt::Display for ProtectedVal<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.val.format_quoted(self.vm).fmt(f)
    }
}