Skip to main content

luau_vm/thread/
auxiliary.rs

1use luau_common::{BStr, BString, ByteSlice};
2use luau_printf::Arg;
3
4use super::stack::RawStackAccess;
5use super::{LUA_BUFFER_SIZE, LUA_TNONE, Thread};
6use crate::debug::LuaDebug;
7use crate::metamethod::MetamethodRuntime;
8use crate::state::ThreadState;
9use crate::string::{LuaString, printf_error_message};
10use crate::types::{
11    LUA_TBOOLEAN, LUA_TBUFFER, LUA_TINTEGER, LUA_TNIL, LUA_TNUMBER, LUA_TSTRING, LUA_TVECTOR,
12};
13use crate::{VmError, VmErrorResult, VmResult};
14
15// Auxiliary error context
16fn current_native_function_name(thread: &Thread) -> Option<LuaString> {
17    if unsafe { thread.current_call_info() == thread.base_call_info() } {
18        return None;
19    }
20
21    let closure = unsafe { thread.current_function() };
22    if unsafe { !closure.is_native() } {
23        return None;
24    }
25
26    let debug_name = unsafe { closure.native_debug_name() }?;
27
28    if debug_name.as_bytes() == b"__namecall" {
29        unsafe { thread.name_call() }.map(LuaString::from_interned)
30    } else {
31        Some(debug_name)
32    }
33}
34
35/// `tag_error`
36fn tag_error<T>(thread: &Thread, argument: i32, tag: i32) -> VmErrorResult<T> {
37    let expected = unsafe { thread.type_name(tag) };
38    unsafe { thread.lua_type_error(argument, expected.as_bytes()) }
39}
40
41fn where_bytes(thread: &Thread, level: i32) -> VmErrorResult<Option<BString>> {
42    let mut ar = LuaDebug::default();
43    if unsafe { thread.get_info(level, "sl", &mut ar)? } == 0 || ar.currentline <= 0 {
44        return Ok(None);
45    }
46
47    Ok(Some(luau_printf::sprintf!(
48        "%s:%d: ",
49        ar.short_src().as_bstr(),
50        ar.currentline
51    )))
52}
53
54// Error construction
55impl Thread {
56    /// `luaL_where`
57    pub unsafe fn push_where(&self, level: i32) -> VmErrorResult {
58        unsafe {
59            self.raw_check_stack(1)?;
60
61            if let Some(prefix) = where_bytes(self, level)? {
62                self.push_string(prefix.as_slice())?;
63            } else {
64                self.push_string("")?;
65            }
66        }
67        Ok(())
68    }
69
70    /// `luaL_error`
71    pub unsafe fn lua_error<'a, T>(
72        &self,
73        format: impl AsRef<[u8]>,
74        mut args: impl AsMut<[Arg<'a>]>,
75    ) -> VmErrorResult<T> {
76        let format = format.as_ref();
77        let args = args.as_mut();
78        let mut formatted = Vec::new();
79        let message_bytes = if args.is_empty() {
80            format
81        } else {
82            formatted.reserve(format.len());
83            match luau_printf::printf_c_locale(&mut formatted, luau_printf::BStr::new(format), args)
84            {
85                Ok(_) => formatted.as_slice(),
86                Err(error) => printf_error_message(&error),
87            }
88        };
89        let message_len = message_bytes
90            .iter()
91            .position(|byte| *byte == 0)
92            .unwrap_or(message_bytes.len())
93            .min(LUA_BUFFER_SIZE - 1);
94
95        let mut message = match where_bytes(self, 1) {
96            Ok(Some(message)) => message,
97            Ok(None) => BString::default(),
98            Err(error) => return Err(error),
99        };
100        message.extend_from_slice(&message_bytes[..message_len]);
101
102        unsafe { self.raw_check_stack(1) }?;
103        unsafe { self.push_string(message.as_slice()) }?;
104        Err(VmError::Runtime)
105    }
106
107    /// `luaL_typeerror`
108    pub unsafe fn lua_type_error<T>(
109        &self,
110        argument: i32,
111        expected_type: impl AsRef<[u8]>,
112    ) -> VmErrorResult<T> {
113        unsafe {
114            let function_name = current_native_function_name(self);
115            let object = self.to_object(argument);
116            let expected_type = expected_type.as_ref();
117
118            if object.is_none() {
119                if let Some(name) = function_name {
120                    crate::error!(
121                        self,
122                        "missing argument #%d to '%s' (%s expected)",
123                        argument,
124                        &name,
125                        expected_type
126                    )
127                } else {
128                    crate::error!(
129                        self,
130                        "missing argument #%d (%s expected)",
131                        argument,
132                        expected_type
133                    )
134                }
135            } else {
136                let got = self.obj_type_name(object.unwrap_unchecked());
137                if let Some(name) = function_name {
138                    crate::error!(
139                        self,
140                        "invalid argument #%d to '%s' (%s expected, got %s)",
141                        argument,
142                        &name,
143                        expected_type,
144                        &got
145                    )
146                } else {
147                    crate::error!(
148                        self,
149                        "invalid argument #%d (%s expected, got %s)",
150                        argument,
151                        expected_type,
152                        &got
153                    )
154                }
155            }
156        }
157    }
158
159    /// `luaL_argexpected`
160    pub unsafe fn lua_arg_expected(
161        &self,
162        condition: bool,
163        argument: i32,
164        expected_type: &str,
165    ) -> VmErrorResult {
166        if !condition {
167            return unsafe { self.lua_type_error(argument, expected_type) };
168        }
169        Ok(())
170    }
171
172    /// `luaL_argerror`
173    pub unsafe fn lua_arg_error<T>(
174        &self,
175        argument: i32,
176        message: impl AsRef<[u8]>,
177    ) -> VmErrorResult<T> {
178        let function_name = current_native_function_name(self);
179        let message = message.as_ref().as_bstr();
180
181        if let Some(name) = function_name {
182            unsafe {
183                crate::error!(
184                    self,
185                    "invalid argument #%d to '%s' (%s)",
186                    argument,
187                    &name,
188                    message
189                )
190            }
191        } else {
192            unsafe { crate::error!(self, "invalid argument #%d (%s)", argument, message) }
193        }
194    }
195}
196
197// Argument validation
198impl Thread {
199    /// `luaL_checkstack`
200    pub unsafe fn lua_check_stack(&self, size: i32, message: Option<&str>) -> VmErrorResult {
201        unsafe {
202            if self.check_stack(size) == 0 {
203                if let Some(message) = message {
204                    crate::error!(self, "stack overflow (%s)", message)
205                } else {
206                    crate::error!(self, "stack overflow")
207                }
208            } else {
209                Ok(())
210            }
211        }
212    }
213
214    /// `luaL_checktype`
215    pub unsafe fn check_type(&self, argument: i32, expected_type: i32) -> VmErrorResult {
216        if unsafe { self.type_of(argument) } != expected_type {
217            tag_error(self, argument, expected_type)
218        } else {
219            Ok(())
220        }
221    }
222
223    /// `luaL_checkany`
224    pub unsafe fn check_any(&self, argument: i32) -> VmErrorResult {
225        if unsafe { self.type_of(argument) } == LUA_TNONE {
226            unsafe { crate::error!(self, "missing argument #%d", argument) }
227        } else {
228            Ok(())
229        }
230    }
231
232    /// `luaL_checklstring`
233    pub unsafe fn check_string(&self, argument: i32) -> VmErrorResult<&BStr> {
234        if let Some(bytes) = unsafe { self.to_string(argument)? } {
235            Ok(bytes)
236        } else {
237            tag_error(self, argument, LUA_TSTRING)
238        }
239    }
240
241    /// `luaL_optlstring`
242    pub unsafe fn opt_string(&self, argument: i32) -> VmErrorResult<Option<&BStr>> {
243        let tag = unsafe { self.type_of(argument) };
244        if tag == LUA_TNONE || tag == LUA_TNIL {
245            Ok(None)
246        } else {
247            Ok(Some(unsafe { self.check_string(argument)? }))
248        }
249    }
250
251    /// `luaL_checkbuffer`
252    pub unsafe fn check_buffer(&self, argument: i32) -> VmErrorResult<(*mut u8, usize)> {
253        if let Some(buffer) = unsafe { self.to_buffer(argument) } {
254            Ok(buffer)
255        } else {
256            tag_error(self, argument, LUA_TBUFFER)
257        }
258    }
259
260    /// `luaL_checkudata`
261    pub unsafe fn check_userdata(&self, argument: i32, type_name: &str) -> VmErrorResult<*mut ()> {
262        unsafe {
263            let userdata = self.to_userdata(argument);
264            if !userdata.is_null() && self.get_metatable(argument)? != 0 {
265                self.raw_get_field(crate::thread::LUA_REGISTRY_INDEX, type_name)?;
266
267                if self.raw_equal(-1, -2) != 0 {
268                    self.pop(2);
269                    return Ok(userdata);
270                }
271            }
272
273            self.lua_type_error(argument, type_name)
274        }
275    }
276
277    /// `luaL_checkudatatagged`
278    pub unsafe fn check_userdata_tagged(&self, argument: i32, tag: i32) -> VmErrorResult<*mut ()> {
279        let userdata = unsafe { self.to_userdata_tagged(argument, tag) };
280        if !userdata.is_null() {
281            return Ok(userdata);
282        }
283
284        let type_name = unsafe { self.get_userdata_name(tag) };
285        unsafe { self.lua_type_error(argument, type_name.as_bytes()) }
286    }
287
288    /// `luaL_checknumber`
289    pub unsafe fn check_number(&self, argument: i32) -> VmErrorResult<f64> {
290        if let Some(value) = unsafe { self.to_number(argument) } {
291            Ok(value)
292        } else {
293            tag_error(self, argument, LUA_TNUMBER)
294        }
295    }
296
297    /// `luaL_optnumber`
298    pub unsafe fn opt_number(&self, argument: i32, default: f64) -> VmErrorResult<f64> {
299        let tag = unsafe { self.type_of(argument) };
300        if tag == LUA_TNONE || tag == LUA_TNIL {
301            Ok(default)
302        } else {
303            unsafe { self.check_number(argument) }
304        }
305    }
306
307    /// `luaL_checkboolean`
308    pub unsafe fn check_boolean(&self, argument: i32) -> VmErrorResult<i32> {
309        if unsafe { self.type_of(argument) } != LUA_TBOOLEAN {
310            tag_error(self, argument, LUA_TBOOLEAN)
311        } else {
312            Ok(unsafe { self.to_boolean(argument) })
313        }
314    }
315
316    /// `luaL_optboolean`
317    pub unsafe fn opt_boolean(&self, argument: i32, default: i32) -> VmErrorResult<i32> {
318        let tag = unsafe { self.type_of(argument) };
319        if tag == LUA_TNONE || tag == LUA_TNIL {
320            Ok(default)
321        } else {
322            unsafe { self.check_boolean(argument) }
323        }
324    }
325
326    /// `luaL_checkinteger`
327    pub unsafe fn check_integer(&self, argument: i32) -> VmErrorResult<i32> {
328        if let Some(value) = unsafe { self.to_integer(argument) } {
329            Ok(value)
330        } else {
331            tag_error(self, argument, LUA_TNUMBER)
332        }
333    }
334
335    /// `luaL_optinteger`
336    pub unsafe fn opt_integer(&self, argument: i32, default: i32) -> VmErrorResult<i32> {
337        let tag = unsafe { self.type_of(argument) };
338        if tag == LUA_TNONE || tag == LUA_TNIL {
339            Ok(default)
340        } else {
341            unsafe { self.check_integer(argument) }
342        }
343    }
344
345    /// `luaL_checkinteger64`
346    pub unsafe fn check_integer64(&self, argument: i32) -> VmErrorResult<i64> {
347        if let Some(value) = unsafe { self.to_integer64(argument) } {
348            Ok(value)
349        } else {
350            tag_error(self, argument, LUA_TINTEGER)
351        }
352    }
353
354    /// `luaL_optinteger64`
355    pub unsafe fn opt_integer64(&self, argument: i32, default: i64) -> VmErrorResult<i64> {
356        let tag = unsafe { self.type_of(argument) };
357        if tag == LUA_TNONE || tag == LUA_TNIL {
358            Ok(default)
359        } else {
360            unsafe { self.check_integer64(argument) }
361        }
362    }
363
364    /// `luaL_checkunsigned`
365    pub unsafe fn check_unsigned(&self, argument: i32) -> VmErrorResult<u32> {
366        if let Some(value) = unsafe { self.to_unsigned(argument) } {
367            Ok(value)
368        } else {
369            tag_error(self, argument, LUA_TNUMBER)
370        }
371    }
372
373    /// `luaL_optunsigned`
374    pub unsafe fn opt_unsigned(&self, argument: i32, default: u32) -> VmErrorResult<u32> {
375        let tag = unsafe { self.type_of(argument) };
376        if tag == LUA_TNONE || tag == LUA_TNIL {
377            Ok(default)
378        } else {
379            unsafe { self.check_unsigned(argument) }
380        }
381    }
382
383    /// `luaL_checkvector`
384    pub unsafe fn check_vector(
385        &self,
386        argument: i32,
387    ) -> VmErrorResult<[f32; crate::types::LUA_VECTOR_SIZE]> {
388        if let Some(vector) = unsafe { self.to_vector(argument) } {
389            Ok(vector)
390        } else {
391            tag_error(self, argument, LUA_TVECTOR)
392        }
393    }
394
395    /// `luaL_optvector`
396    pub unsafe fn opt_vector(
397        &self,
398        argument: i32,
399    ) -> VmErrorResult<Option<[f32; crate::types::LUA_VECTOR_SIZE]>> {
400        let tag = unsafe { self.type_of(argument) };
401        if tag == LUA_TNONE || tag == LUA_TNIL {
402            Ok(None)
403        } else {
404            Ok(Some(unsafe { self.check_vector(argument)? }))
405        }
406    }
407
408    /// `luaL_checkoption`
409    pub unsafe fn check_option(
410        &self,
411        argument: i32,
412        default: Option<&str>,
413        options: &[&str],
414    ) -> VmErrorResult<i32> {
415        unsafe {
416            let name = if let Some(default) = default {
417                let tag = self.type_of(argument);
418                if tag == LUA_TNONE || tag == LUA_TNIL {
419                    default.as_bytes()
420                } else {
421                    self.check_string(argument)?.as_bytes()
422                }
423            } else {
424                self.check_string(argument)?.as_bytes()
425            };
426
427            for (index, option) in options.iter().enumerate() {
428                if option.as_bytes() == name {
429                    return Ok(index as i32);
430                }
431            }
432
433            let message = luau_printf::sprintf!("invalid option '%s'", name.as_bstr());
434            self.lua_arg_error(argument, message.as_bstr())
435        }
436    }
437}
438
439// Value display conversion
440impl Thread {
441    fn append_pointer_hex(bytes: &mut BString, mut value: usize) {
442        const HEX: &[u8; 16] = b"0123456789abcdef";
443
444        let mut buffer = [0u8; 2 * core::mem::size_of::<usize>()];
445        for digit in buffer.iter_mut().rev() {
446            *digit = HEX[value & 0xf];
447            value >>= 4;
448        }
449
450        bytes.extend_from_slice(&buffer);
451    }
452
453    /// `luaL_typename`
454    pub unsafe fn lua_type_name(&self, index: i32) -> LuaString {
455        let object = unsafe { self.to_object(index) };
456        if object.is_none() {
457            LuaString::from_static(b"no value".as_bstr())
458        } else {
459            unsafe { self.obj_type_name(object.unwrap_unchecked()) }
460        }
461    }
462
463    /// `luaL_tolstring`
464    pub unsafe fn lua_to_string(&self, index: i32) -> VmResult<&BStr> {
465        unsafe {
466            if self.call_meta(index, "__tostring")? != 0 {
467                let Some(result) = self.to_string(-1)? else {
468                    return crate::error!(self, "'__tostring' must return a string")
469                        .map_err(Into::into);
470                };
471
472                return Ok(result);
473            }
474
475            match self.type_of(index) {
476                LUA_TNIL => self.push_string("nil")?,
477                LUA_TBOOLEAN => self.push_string(if self.to_boolean(index) != 0 {
478                    "true"
479                } else {
480                    "false"
481                })?,
482                LUA_TNUMBER => {
483                    let mut buffer = [0u8; crate::number::LUAI_MAXNUM2STR];
484                    let number_len = crate::number::num_to_str(
485                        &mut buffer,
486                        self.to_number(index).unwrap_or(0.0),
487                    );
488                    self.push_string(&buffer[..number_len])?;
489                }
490                LUA_TVECTOR => {
491                    let vector = self.to_vector(index).unwrap_unchecked();
492                    let mut bytes = BString::new(Vec::with_capacity(
493                        crate::number::LUAI_MAXNUM2STR * crate::types::LUA_VECTOR_SIZE,
494                    ));
495
496                    for (component_index, component) in vector.iter().enumerate() {
497                        if component_index != 0 {
498                            bytes.extend_from_slice(b", ");
499                        }
500
501                        let mut component_buffer = [0u8; crate::number::LUAI_MAXNUM2STR];
502                        let component_len =
503                            crate::number::num_to_str(&mut component_buffer, f64::from(*component));
504                        bytes.extend_from_slice(&component_buffer[..component_len]);
505                    }
506
507                    self.push_string(bytes.as_slice())?;
508                }
509                LUA_TSTRING => self.push_value(index)?,
510                LUA_TINTEGER => {
511                    let mut buffer = [0u8; crate::number::LUAI_MAXINT2STR];
512                    let integer_len = crate::number::int_to_str(
513                        &mut buffer,
514                        self.to_integer64(index).unwrap_or(0),
515                    );
516                    self.push_string(&buffer[..integer_len])?;
517                }
518                _ => {
519                    let type_name = self.lua_type_name(index);
520                    let mut bytes = BString::new(Vec::new());
521                    bytes.extend_from_slice(type_name.as_bytes());
522                    bytes.extend_from_slice(b": 0x");
523                    let encoded = self.encode_pointer(self.to_pointer(index) as usize);
524                    Self::append_pointer_hex(&mut bytes, encoded);
525                    self.push_string(bytes.as_slice())?;
526                }
527            }
528
529            Ok(self.to_string(-1)?.unwrap_unchecked())
530        }
531    }
532}