Skip to main content

luau_vm/
native.rs

1use std::cell::{Ref, RefCell, RefMut};
2use std::ops::{Deref, DerefMut};
3
4use luau_common::BStr;
5use luau_printf::Arg;
6
7use crate::thread::Thread;
8use crate::userdata::{TypedUserdataAccess, UserdataTypeRegistryAccess};
9use crate::{VmErrorResult, VmResult};
10
11pub type NativeCallResult = VmResult<usize>;
12
13pub type RawNativeFunction = for<'call> fn(NativeCallContext<'call>) -> NativeCallResult;
14
15pub type RawNativeContinuation = for<'call> fn(NativeCallContext<'call>, i32) -> NativeCallResult;
16
17// Native function registration
18#[derive(Clone, Copy)]
19pub struct NativeFunction {
20    pub name: &'static str,
21    pub function: RawNativeFunction,
22}
23
24#[derive(Clone, Copy)]
25pub struct NativeModule {
26    pub name: Option<&'static str>,
27    pub functions: &'static [NativeFunction],
28}
29
30impl NativeModule {
31    #[inline]
32    pub const fn new(name: Option<&'static str>, functions: &'static [NativeFunction]) -> Self {
33        Self { name, functions }
34    }
35}
36
37// Native call context
38#[derive(PartialEq, Eq)]
39#[repr(transparent)]
40pub struct NativeCallContext<'call> {
41    thread: &'call Thread,
42}
43
44impl<'call> NativeCallContext<'call> {
45    /// The thread must be the active thread for a VM-native call or continuation.
46    #[inline]
47    pub(crate) const fn new(thread: &'call Thread) -> Self {
48        Self { thread }
49    }
50
51    #[inline]
52    pub const fn raw_thread(&self) -> &'call Thread {
53        self.thread
54    }
55
56    #[inline]
57    pub fn arg_count(&self) -> i32 {
58        unsafe { self.thread.get_top() }
59    }
60
61    #[inline]
62    pub fn top(&self) -> i32 {
63        self.arg_count()
64    }
65
66    #[inline]
67    pub fn pop(&self, count: i32) {
68        unsafe { self.thread.pop(count) }
69    }
70
71    #[inline]
72    pub fn arg(&self, index: i32) -> NativeArgument<'_, 'call> {
73        NativeArgument {
74            context: self,
75            index,
76        }
77    }
78
79    #[inline]
80    pub fn argument(&self, index: i32) -> NativeArgument<'_, 'call> {
81        self.arg(index)
82    }
83
84    #[inline]
85    pub fn upvalue(&self, index: i32) -> NativeArgument<'_, 'call> {
86        NativeArgument {
87            context: self,
88            index: crate::thread::upvalue_index(index),
89        }
90    }
91
92    #[inline]
93    pub fn args(&self) -> NativeArguments<'_, 'call> {
94        NativeArguments {
95            context: self,
96            next: 1,
97            end: self.arg_count(),
98        }
99    }
100
101    #[inline]
102    pub fn push_number(&self, value: f64) -> VmErrorResult {
103        unsafe { self.thread.push_number(value) }
104    }
105
106    #[inline]
107    pub fn push_nil(&self) -> VmErrorResult {
108        unsafe { self.thread.push_nil() }
109    }
110
111    #[inline]
112    pub fn push_integer(&self, value: i32) -> VmErrorResult {
113        unsafe { self.thread.push_integer(value) }
114    }
115
116    #[inline]
117    pub fn push_integer64(&self, value: i64) -> VmErrorResult {
118        unsafe { self.thread.push_integer64(value) }
119    }
120
121    #[inline]
122    pub fn push_unsigned(&self, value: u32) -> VmErrorResult {
123        unsafe { self.thread.push_unsigned(value) }
124    }
125
126    #[inline]
127    pub fn push_vector(&self, components: [f32; crate::types::LUA_VECTOR_SIZE]) -> VmErrorResult {
128        unsafe { self.thread.push_vector(components) }
129    }
130
131    #[inline]
132    pub fn push_boolean(&self, value: bool) -> VmErrorResult {
133        unsafe { self.thread.push_boolean(i32::from(value)) }
134    }
135
136    #[inline]
137    pub fn push_bool(&self, value: bool) -> VmErrorResult {
138        self.push_boolean(value)
139    }
140
141    #[inline]
142    pub fn push_string(&self, bytes: impl AsRef<[u8]>) -> VmErrorResult {
143        unsafe { self.thread.push_string(bytes) }
144    }
145
146    #[inline]
147    pub fn push_userdata<T: 'static>(&self, value: T) -> VmErrorResult {
148        let Some(registration) = (unsafe { self.thread.userdata_type::<T>() }) else {
149            return self.error("userdata type is not registered", []);
150        };
151        unsafe { self.thread.push_typed_userdata(value, &registration) }
152    }
153
154    #[inline]
155    pub fn lua_error<'a, T>(
156        &self,
157        format: impl AsRef<[u8]>,
158        args: impl AsMut<[Arg<'a>]>,
159    ) -> VmErrorResult<T> {
160        unsafe { self.thread.lua_error(format, args) }
161    }
162
163    #[inline]
164    pub fn error<'a, T>(
165        &self,
166        format: impl AsRef<[u8]>,
167        args: impl AsMut<[Arg<'a>]>,
168    ) -> VmErrorResult<T> {
169        self.lua_error(format, args)
170    }
171}
172
173// Native arguments
174#[derive(Clone, Copy)]
175pub struct NativeArgument<'ctx, 'call> {
176    pub(super) context: &'ctx NativeCallContext<'call>,
177    pub(super) index: i32,
178}
179
180impl<'ctx, 'call> NativeArgument<'ctx, 'call> {
181    #[inline]
182    pub const fn index(&self) -> i32 {
183        self.index
184    }
185
186    #[inline]
187    pub fn integer(&self) -> VmErrorResult<i32> {
188        unsafe { self.context.raw_thread().check_integer(self.index) }
189    }
190
191    #[inline]
192    pub fn integer_or(&self, default: i32) -> VmErrorResult<i32> {
193        unsafe { self.context.raw_thread().opt_integer(self.index, default) }
194    }
195
196    #[inline]
197    pub fn number(&self) -> VmErrorResult<f64> {
198        unsafe { self.context.raw_thread().check_number(self.index) }
199    }
200
201    #[inline]
202    pub fn number_or(&self, default: f64) -> VmErrorResult<f64> {
203        unsafe { self.context.raw_thread().opt_number(self.index, default) }
204    }
205
206    #[inline]
207    pub fn integer64(&self) -> VmErrorResult<i64> {
208        unsafe { self.context.raw_thread().check_integer64(self.index) }
209    }
210
211    #[inline]
212    pub fn integer64_or(&self, default: i64) -> VmErrorResult<i64> {
213        unsafe { self.context.raw_thread().opt_integer64(self.index, default) }
214    }
215
216    #[inline]
217    pub fn unsigned(&self) -> VmErrorResult<u32> {
218        unsafe { self.context.raw_thread().check_unsigned(self.index) }
219    }
220
221    #[inline]
222    pub fn vector(&self) -> VmErrorResult<[f32; crate::types::LUA_VECTOR_SIZE]> {
223        unsafe { self.context.raw_thread().check_vector(self.index) }
224    }
225
226    #[inline]
227    /// Returns a borrowed string argument.
228    ///
229    /// # Safety
230    ///
231    /// The argument must remain rooted and no VM transition may pop, unroot,
232    /// or collect it for the returned borrow's lifetime.
233    pub unsafe fn string(&self) -> VmErrorResult<&'ctx BStr> {
234        unsafe { self.context.raw_thread().check_string(self.index) }
235    }
236
237    #[inline]
238    pub fn light_userdata(&self) -> VmErrorResult<*mut ()> {
239        let pointer = unsafe { self.context.raw_thread().to_light_userdata(self.index) };
240        if pointer.is_null() {
241            return self.type_error("light userdata");
242        }
243        Ok(pointer)
244    }
245
246    /// Borrows this argument as userdata of the registered type.
247    #[inline]
248    /// # Safety
249    ///
250    /// The argument must remain rooted and no VM transition may pop, unroot,
251    /// or collect it while the returned guard is alive.
252    pub unsafe fn userdata<T: 'static>(&self) -> VmErrorResult<LuaUserdataRef<'ctx, T>> {
253        let cell = unsafe { &*self.userdata_cell_ptr::<T>()? };
254        let Ok(value) = cell.try_borrow() else {
255            return self.error("userdata is already mutably borrowed");
256        };
257        let Ok(value) = Ref::filter_map(value, Option::as_ref) else {
258            return self.error("userdata has been destructed");
259        };
260        Ok(LuaUserdataRef::new(value))
261    }
262
263    /// Mutably borrows this argument as userdata of the registered type.
264    #[inline]
265    /// # Safety
266    ///
267    /// The argument must remain rooted and no VM transition may pop, unroot,
268    /// or collect it while the returned guard is alive. No other access to the
269    /// same typed value may overlap the mutable guard.
270    pub unsafe fn userdata_mut<T: 'static>(&self) -> VmErrorResult<LuaUserdataRefMut<'ctx, T>> {
271        let cell = unsafe { &*self.userdata_cell_ptr::<T>()? };
272        let Ok(value) = cell.try_borrow_mut() else {
273            return self.error("userdata is already borrowed");
274        };
275        let Ok(value) = RefMut::filter_map(value, Option::as_mut) else {
276            return self.error("userdata has been destructed");
277        };
278        Ok(LuaUserdataRefMut::new(value))
279    }
280
281    #[inline]
282    unsafe fn userdata_cell_ptr<T: 'static>(&self) -> VmErrorResult<*mut RefCell<Option<T>>> {
283        let expected_type = core::any::type_name::<T>();
284        let Some(userdata) = (unsafe { self.context.raw_thread().typed_userdata_at(self.index) })
285        else {
286            return self.type_error(expected_type);
287        };
288        if userdata.is_destructed() {
289            return self.error("userdata has been destructed");
290        }
291        let Some(cell) = (unsafe { userdata.cell_ptr::<T>() }) else {
292            return self.type_error(expected_type);
293        };
294
295        Ok(cell)
296    }
297
298    #[inline]
299    pub fn error<T>(&self, message: impl AsRef<[u8]>) -> VmErrorResult<T> {
300        unsafe { self.context.raw_thread().lua_arg_error(self.index, message) }
301    }
302
303    #[inline]
304    pub fn expected(&self, condition: bool, expected_type: &str) -> VmErrorResult {
305        unsafe {
306            self.context
307                .raw_thread()
308                .lua_arg_expected(condition, self.index, expected_type)
309        }
310    }
311
312    #[inline]
313    pub fn type_error<T>(&self, expected_type: &str) -> VmErrorResult<T> {
314        unsafe {
315            self.context
316                .raw_thread()
317                .lua_type_error(self.index, expected_type)
318        }
319    }
320}
321
322#[derive(Clone, Copy)]
323pub struct NativeArguments<'ctx, 'call> {
324    pub(super) context: &'ctx NativeCallContext<'call>,
325    pub(super) next: i32,
326    pub(super) end: i32,
327}
328
329impl NativeArguments<'_, '_> {
330    #[inline]
331    pub fn remaining(&self) -> i32 {
332        (self.end - self.next + 1).max(0)
333    }
334}
335
336impl<'ctx, 'call> Iterator for NativeArguments<'ctx, 'call> {
337    type Item = NativeArgument<'ctx, 'call>;
338
339    #[inline]
340    fn next(&mut self) -> Option<Self::Item> {
341        if self.next > self.end {
342            None
343        } else {
344            let index = self.next;
345            self.next += 1;
346            Some(NativeArgument {
347                context: self.context,
348                index,
349            })
350        }
351    }
352
353    #[inline]
354    fn size_hint(&self) -> (usize, Option<usize>) {
355        let remaining = self.remaining() as usize;
356        (remaining, Some(remaining))
357    }
358}
359
360impl ExactSizeIterator for NativeArguments<'_, '_> {}
361
362/// Shared borrow of a Lua-owned userdata value.
363///
364/// The borrow is released when this guard is dropped.
365pub struct LuaUserdataRef<'lua, T> {
366    value: Ref<'lua, T>,
367}
368
369impl<'lua, T> LuaUserdataRef<'lua, T> {
370    #[inline]
371    pub(super) fn new(value: Ref<'lua, T>) -> Self {
372        Self { value }
373    }
374}
375
376impl<T> Deref for LuaUserdataRef<'_, T> {
377    type Target = T;
378
379    #[inline]
380    fn deref(&self) -> &Self::Target {
381        &self.value
382    }
383}
384
385/// Mutable borrow of a Lua-owned userdata value.
386///
387/// The borrow is released when this guard is dropped.
388pub struct LuaUserdataRefMut<'lua, T> {
389    value: RefMut<'lua, T>,
390}
391
392impl<'lua, T> LuaUserdataRefMut<'lua, T> {
393    #[inline]
394    pub(super) fn new(value: RefMut<'lua, T>) -> Self {
395        Self { value }
396    }
397}
398
399impl<T> Deref for LuaUserdataRefMut<'_, T> {
400    type Target = T;
401
402    #[inline]
403    fn deref(&self) -> &Self::Target {
404        &self.value
405    }
406}
407
408impl<T> DerefMut for LuaUserdataRefMut<'_, T> {
409    #[inline]
410    fn deref_mut(&mut self) -> &mut Self::Target {
411        &mut self.value
412    }
413}