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#[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#[derive(PartialEq, Eq)]
39#[repr(transparent)]
40pub struct NativeCallContext<'call> {
41 thread: &'call Thread,
42}
43
44impl<'call> NativeCallContext<'call> {
45 #[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, ®istration) }
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#[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 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 #[inline]
248 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 #[inline]
265 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
362pub 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
385pub 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}