spore_vm/val/
native_function.rs

1use compact_str::CompactString;
2
3use crate::{error::VmResult, Vm};
4
5use super::{custom::CustomVal, CustomType, StructVal, UnsafeVal, Val};
6
7/// A function that can be executed by the Spore VM. Native functions can be registered with
8/// [Vm::with_native_function].
9///
10/// # Argument
11/// Native functions take a [NativeFunctionContext] as an argument. This contains the state of the
12/// VM.
13///
14/// # Return Value
15/// VmResult<[ValBuilder]> is used to build a return value and insert it into the VM.
16///
17/// ```rust
18/// fn my_magic_string(ctx: spore_vm::val::NativeFunctionContext) -> spore_vm::error::VmResult<spore_vm::val::ValBuilder> {
19///     Ok(ctx.new_string("42".into()))
20/// }
21pub type NativeFunction =
22    for<'a> fn(NativeFunctionContext<'a>, &[Val<'a>]) -> VmResult<ValBuilder<'a>>;
23
24/// Builds a value for the VM to consume.
25///
26/// - This is often returned by [NativeFunction].
27/// - `ValBuilder` objects may be built from [NativeFunctionContext] objects.
28///
29/// ```rust
30/// fn my_magic_string(ctx: spore_vm::val::NativeFunctionContext) -> spore_vm::error::VmResult<spore_vm::val::ValBuilder> {
31///     Ok(ctx.new_string("42".into()))
32/// }
33/// ```
34#[derive(Debug)]
35pub struct ValBuilder<'a> {
36    val: Val<'a>,
37}
38
39impl ValBuilder<'static> {
40    /// Create a new `ValBuilder` from a static [Val].
41    ///
42    /// ```rust
43    /// spore_vm::val::ValBuilder::new(().into());   // void
44    /// spore_vm::val::ValBuilder::new(true.into()); // bool
45    /// spore_vm::val::ValBuilder::new(0i64.into()); // int
46    /// spore_vm::val::ValBuilder::new(0.0.into());  // float
47    /// ```
48    pub fn new(val: Val<'static>) -> ValBuilder<'static> {
49        ValBuilder { val }
50    }
51}
52
53impl From<Val<'static>> for ValBuilder<'static> {
54    fn from(val: Val) -> ValBuilder {
55        ValBuilder { val }
56    }
57}
58
59/// The input parameter to native Spore VM functions registered with [Vm::with_native_function].
60///
61/// ```rust
62/// fn my_magic_string(ctx: spore_vm::val::NativeFunctionContext) -> spore_vm::error::VmResult<spore_vm::val::ValBuilder> {
63///     Ok(ctx.new_string("42".into()))
64/// }
65pub struct NativeFunctionContext<'a> {
66    /// The Vm for the native function.
67    ///
68    /// # Safety
69    /// Do not run anything that may remove references or call the garbage collector.
70    vm: &'a mut Vm,
71}
72
73impl<'a> NativeFunctionContext<'a> {
74    /// # Safety
75    /// - Stack start must be less than or equal to the Vm's stack length.
76    pub(crate) fn new(vm: &mut Vm) -> NativeFunctionContext {
77        NativeFunctionContext { vm }
78    }
79
80    /// Get the underlying VM.
81    pub fn vm(&self) -> &Vm {
82        self.vm
83    }
84
85    /// Get a mutable reference to the underlying VM.
86    ///
87    /// # Safety
88    /// Any operations that triger GC or evaluation will cause undefined behavior.
89    pub unsafe fn vm_mut(&mut self) -> &mut Vm {
90        self.vm
91    }
92}
93
94impl<'a> NativeFunctionContext<'a> {
95    /// Create a new value from an internal.
96    ///
97    /// Consumes the `self` to ensure that the value isn't garbage collected.
98    ///
99    /// # Safety
100    /// `InternalVal` must be a valid value that has not been garbage collected.
101    pub unsafe fn with_unsafe_val(self, val: UnsafeVal) -> ValBuilder<'a> {
102        ValBuilder {
103            val: Val::from_unsafe_val(val),
104        }
105    }
106
107    /// Create a new `string` value.
108    ///
109    /// Consumes `self` to ensure that the value isn't garbage collected.
110    ///
111    /// ```rust
112    /// fn my_magic_string(ctx: spore_vm::val::NativeFunctionContext) -> spore_vm::error::VmResult<spore_vm::val::ValBuilder> {
113    ///     Ok(ctx.new_string("42".into()))
114    /// }
115    /// ```
116    pub fn new_string(self, s: CompactString) -> ValBuilder<'a> {
117        let string_id = self.vm.objects.insert_string(s);
118        ValBuilder {
119            // Unsafe OK: String was just created so it does not have a chance to garbage collect.
120            val: unsafe { Val::from_unsafe_val(string_id.into()) },
121        }
122    }
123
124    /// Create a new box from the unsafe val.
125    ///
126    /// Consumes the `self` to ensure that the value isn't garbage collected.
127    ///
128    /// # Safety
129    /// `v` must be a valid value within the vm.
130    pub unsafe fn new_mutable_box(self, v: Val<'a>) -> ValBuilder<'a> {
131        let id = self.vm.objects.insert_mutable_box(v.as_unsafe_val());
132        ValBuilder {
133            // Unsafe OK: Box is just created so it does not have a chance to garbage collect.
134            val: Val::from_unsafe_val(id.into()),
135        }
136    }
137
138    /// Create a new list from `ListVal`.
139    ///
140    /// Consumes the self to ensure that the value isn't garbage collected.
141    ///
142    /// # Safety
143    /// `list` must contain valid values within the vm.
144    pub unsafe fn new_list(self, list: &[Val]) -> ValBuilder<'a> {
145        // Unsafe OK: Will be inserting into VM.
146        let unsafe_list = Val::as_unsafe_val_slice(list);
147        let list_id = self.vm.objects.insert_list(unsafe_list.to_vec());
148        ValBuilder {
149            val: Val::from_unsafe_val(list_id.into()),
150        }
151    }
152
153    /// Create a new struct from `StructVal`.
154    ///
155    /// Consumes the self to ensure that the value isn't garbage collected.
156    ///
157    /// # Safety
158    /// `strct` must contain valid values within the vm.
159    pub unsafe fn new_struct(self, strct: StructVal) -> ValBuilder<'a> {
160        let struct_id = self.vm.objects.insert_struct(strct);
161        ValBuilder {
162            val: Val::from_unsafe_val(struct_id.into()),
163        }
164    }
165
166    /// Create a new custom value from `obj`.
167    pub fn new_custom(self, obj: impl CustomType) -> ValBuilder<'a> {
168        let custom_val = CustomVal::new(obj);
169        let custom_id = self.vm.objects.insert_custom(custom_val);
170        ValBuilder {
171            // Unsafe OK: Custom is just created so it does not have a chance to garbage collect.
172            val: unsafe { Val::from_unsafe_val(custom_id.into()) },
173        }
174    }
175}
176
177impl<'a> ValBuilder<'a> {
178    /// # Safety
179    /// The garbage collector may clean up the value. This value must be discarded or inserted into
180    /// the VM immediately.
181    pub(crate) unsafe fn build(self) -> UnsafeVal {
182        self.val.inner
183    }
184}