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}