Skip to main content

luau_vm/
builtins.rs

1use luau_common::ByteSlice;
2
3use crate::call::ThreadStack;
4use crate::gc::{GcBarrier, GcRuntime};
5use crate::metamethod::MetamethodRuntime;
6use crate::number::{
7    LUAI_MAXINT2STR, LUAI_MAXNUM2STR, clamp_f, int_to_str, lerp_f, num_to_str, sign_f,
8};
9use crate::state::ThreadState;
10use crate::string::StringRuntime;
11use crate::table::TableRuntime;
12use crate::thread::Thread;
13use crate::value::{TValue, TValueCursor};
14
15use core::mem::MaybeUninit;
16use core::ptr;
17
18use crate::Table;
19use crate::buffer::Buffer;
20use crate::gc::GcObject;
21use crate::handle::RawHandle;
22use crate::metamethod::TmEvent;
23use crate::number::str_to_num;
24use crate::types::{LUA_TNIL, LUA_TTABLE, LUA_TUSERDATA};
25
26pub type LuauFastFunction = unsafe fn(&Thread, TValueCursor, TValue, i32, TValueCursor, i32) -> i32;
27// General builtins
28
29/// `luauF_missing`
30unsafe fn missing(
31    _thread: &Thread,
32    _result: TValueCursor,
33    _arg0: TValue,
34    _nresults: i32,
35    _args: TValueCursor,
36    _nparams: i32,
37) -> i32 {
38    -1
39}
40
41/// `luauF_assert`
42unsafe fn assert(
43    _thread: &Thread,
44    _result: TValueCursor,
45    arg0: TValue,
46    nresults: i32,
47    _args: TValueCursor,
48    nparams: i32,
49) -> i32 {
50    if nparams >= 1 && nresults == 0 && !arg0.is_false() {
51        0
52    } else {
53        -1
54    }
55}
56
57/// `luauF_type`
58unsafe fn type_(
59    thread: &Thread,
60    result: TValueCursor,
61    arg0: TValue,
62    nresults: i32,
63    _args: TValueCursor,
64    nparams: i32,
65) -> i32 {
66    if nparams >= 1 && nresults <= 1 {
67        unsafe {
68            result
69                .value_unchecked()
70                .set_string_value(thread.global().type_name(arg0.tt() as usize))
71        };
72        1
73    } else {
74        -1
75    }
76}
77
78/// `luauF_len`
79unsafe fn len(
80    _thread: &Thread,
81    result: TValueCursor,
82    arg0: TValue,
83    nresults: i32,
84    _args: TValueCursor,
85    nparams: i32,
86) -> i32 {
87    if nparams >= 1 && nresults <= 1 && arg0.is_string() {
88        unsafe { result.value_unchecked() }.set_number(unsafe {
89            arg0.string_value().as_ptr().as_ref().unwrap_unchecked().len as f64
90        });
91        return 1;
92    }
93    -1
94}
95
96/// `luauF_typeof`
97unsafe fn type_of(
98    thread: &Thread,
99    result: TValueCursor,
100    arg0: TValue,
101    nresults: i32,
102    _args: TValueCursor,
103    nparams: i32,
104) -> i32 {
105    if nparams >= 1 && nresults <= 1 {
106        unsafe { result.value_unchecked() }
107            .set_string_value(unsafe { thread.obj_type_name_str(arg0) });
108        1
109    } else {
110        -1
111    }
112}
113
114/// `luauF_select`
115unsafe fn select(
116    thread: &Thread,
117    result: TValueCursor,
118    arg0: TValue,
119    nresults: i32,
120    _args: TValueCursor,
121    nparams: i32,
122) -> i32 {
123    if nparams == 1 && nresults == 1 {
124        let (base, n, hash_selector) = unsafe {
125            let ci = thread.current_call_info();
126            let func = ci.function();
127            let proto = ci.proto().unwrap_unchecked();
128            let base = thread.stack_base();
129            let n = base.offset_from(func) as i32
130                - proto.as_ptr().as_ref().unwrap_unchecked().num_params as i32
131                - 1;
132            let hash_selector =
133                arg0.is_string() && arg0.string_value().as_bytes().first() == Some(&b'#');
134            (base, n, hash_selector)
135        };
136
137        if arg0.is_number() {
138            let i = arg0.number_value() as i32;
139            if ((i - 1) as u32) < (n as u32) {
140                unsafe { result.value_unchecked() }.set_obj(unsafe {
141                    base.sub(n as usize).add((i - 1) as usize).value_unchecked()
142                });
143                return 1;
144            }
145        } else if hash_selector {
146            unsafe { result.value_unchecked() }.set_number(n as f64);
147            return 1;
148        }
149    }
150    -1
151}
152
153/// `luauF_tonumber`
154unsafe fn to_number(
155    _thread: &Thread,
156    result: TValueCursor,
157    arg0: TValue,
158    nresults: i32,
159    _args: TValueCursor,
160    nparams: i32,
161) -> i32 {
162    if nparams == 1 && nresults <= 1 {
163        if arg0.is_number() {
164            unsafe { result.value_unchecked() }.set_number(arg0.number_value());
165        } else if arg0.is_string() {
166            let string = arg0.string_value();
167            if let Some(value) = str_to_num(unsafe { string.as_bytes() }) {
168                unsafe { result.value_unchecked() }.set_number(value);
169            } else {
170                unsafe { result.value_unchecked() }.set_nil();
171            }
172        } else {
173            unsafe { result.value_unchecked() }.set_nil();
174        }
175        1
176    } else {
177        -1
178    }
179}
180
181/// `luauF_tostring`
182unsafe fn to_string(
183    thread: &Thread,
184    result: TValueCursor,
185    arg0: TValue,
186    nresults: i32,
187    _args: TValueCursor,
188    nparams: i32,
189) -> i32 {
190    if nparams < 1 || nresults > 1 {
191        return -1;
192    }
193
194    unsafe {
195        if arg0.is_nil() {
196            result
197                .value_unchecked()
198                .set_string_value(thread.global().type_name(LUA_TNIL as usize));
199        } else if arg0.is_boolean() {
200            let bytes: &[u8] = if arg0.boolean_value() != 0 {
201                b"true"
202            } else {
203                b"false"
204            };
205            let Ok(s) = thread.intern_string(bytes.as_bstr()) else {
206                return -1;
207            };
208            result.value_unchecked().set_string_value(s);
209        } else if arg0.is_number() {
210            if thread.needs_gc() {
211                return -1;
212            }
213            let mut buffer = [0u8; LUAI_MAXNUM2STR];
214            let len = num_to_str(&mut buffer, arg0.number_value());
215            let Ok(s) = thread.intern_string(buffer[..len].as_bstr()) else {
216                return -1;
217            };
218            result.value_unchecked().set_string_value(s);
219        } else if arg0.is_string() {
220            result
221                .value_unchecked()
222                .set_string_value(arg0.string_value());
223        } else if arg0.is_integer() {
224            if thread.needs_gc() {
225                return -1;
226            }
227            let mut buffer = [0u8; LUAI_MAXINT2STR];
228            let len = int_to_str(&mut buffer, arg0.integer_value());
229            let Ok(s) = thread.intern_string(buffer[..len].as_bstr()) else {
230                return -1;
231            };
232            result.value_unchecked().set_string_value(s);
233        } else {
234            return -1;
235        }
236
237        1
238    }
239}
240
241// Number builtins
242
243macro_rules! unary_num {
244    ($doc:literal, $name:ident, $op:expr) => {
245        #[doc = $doc]
246        unsafe fn $name(
247            _thread: &Thread,
248            result: TValueCursor,
249            arg0: TValue,
250            nresults: i32,
251            _args: TValueCursor,
252            nparams: i32,
253        ) -> i32 {
254            if nparams >= 1 && nresults <= 1 && arg0.is_number() {
255                unsafe { result.value_unchecked() }.set_number(($op)(arg0.number_value()));
256                1
257            } else {
258                -1
259            }
260        }
261    };
262}
263
264macro_rules! binary_num {
265    ($doc:literal, $name:ident, $op:expr) => {
266        #[doc = $doc]
267        unsafe fn $name(
268            _thread: &Thread,
269            result: TValueCursor,
270            arg0: TValue,
271            nresults: i32,
272            args: TValueCursor,
273            nparams: i32,
274        ) -> i32 {
275            if nparams >= 2
276                && nresults <= 1
277                && arg0.is_number()
278                && unsafe { args.value_unchecked() }.is_number()
279            {
280                unsafe { result.value_unchecked() }.set_number(($op)(
281                    arg0.number_value(),
282                    unsafe { args.value_unchecked() }.number_value(),
283                ));
284                1
285            } else {
286                -1
287            }
288        }
289    };
290}
291
292unary_num!("`luauF_abs`", abs, f64::abs);
293unary_num!("`luauF_acos`", acos, f64::acos);
294unary_num!("`luauF_asin`", asin, f64::asin);
295binary_num!("`luauF_atan2`", atan2, f64::atan2);
296unary_num!("`luauF_atan`", atan, f64::atan);
297unary_num!("`luauF_ceil`", ceil, f64::ceil);
298unary_num!("`luauF_cosh`", cosh, f64::cosh);
299unary_num!("`luauF_cos`", cos, f64::cos);
300unary_num!("`luauF_exp`", exp, f64::exp);
301unary_num!("`luauF_floor`", floor, f64::floor);
302binary_num!("`luauF_fmod`", fmod, |a: f64, b: f64| a % b);
303binary_num!("`luauF_pow`", pow, f64::powf);
304unary_num!("`luauF_sinh`", sinh, f64::sinh);
305unary_num!("`luauF_sin`", sin, f64::sin);
306unary_num!("`luauF_sqrt`", sqrt, f64::sqrt);
307unary_num!("`luauF_tanh`", tanh, f64::tanh);
308unary_num!("`luauF_tan`", tan, f64::tan);
309
310/// `luauF_deg`
311unsafe fn deg(
312    _thread: &Thread,
313    result: TValueCursor,
314    arg0: TValue,
315    nresults: i32,
316    _args: TValueCursor,
317    nparams: i32,
318) -> i32 {
319    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
320        let radians_per_degree = std::f64::consts::PI / 180.0;
321        unsafe { result.value_unchecked() }.set_number(arg0.number_value() / radians_per_degree);
322        1
323    } else {
324        -1
325    }
326}
327
328/// `luauF_frexp`
329unsafe fn frexp(
330    _thread: &Thread,
331    result: TValueCursor,
332    arg0: TValue,
333    nresults: i32,
334    _args: TValueCursor,
335    nparams: i32,
336) -> i32 {
337    if nparams >= 1 && nresults <= 2 && arg0.is_number() {
338        let value = arg0.number_value();
339        if value == 0.0 {
340            unsafe { result.value_unchecked() }.set_number(0.0);
341            unsafe { result.add(1).value_unchecked() }.set_number(0.0);
342            return 2;
343        }
344
345        let abs = value.abs();
346        let exponent = abs.log2().floor() as i32 + 1;
347        let fraction = value / 2.0f64.powi(exponent);
348        unsafe { result.value_unchecked() }.set_number(fraction);
349        unsafe { result.add(1).value_unchecked() }.set_number(exponent as f64);
350        2
351    } else {
352        -1
353    }
354}
355
356/// `luauF_ldexp`
357unsafe fn ldexp(
358    _thread: &Thread,
359    result: TValueCursor,
360    arg0: TValue,
361    nresults: i32,
362    args: TValueCursor,
363    nparams: i32,
364) -> i32 {
365    if nparams >= 2
366        && nresults <= 1
367        && arg0.is_number()
368        && unsafe { args.value_unchecked() }.is_number()
369    {
370        unsafe { result.value_unchecked() }.set_number(
371            arg0.number_value()
372                * 2.0f64.powi(unsafe { args.value_unchecked() }.number_value() as i32),
373        );
374        1
375    } else {
376        -1
377    }
378}
379
380/// `luauF_log10`
381unsafe fn log10(
382    _thread: &Thread,
383    result: TValueCursor,
384    arg0: TValue,
385    nresults: i32,
386    _args: TValueCursor,
387    nparams: i32,
388) -> i32 {
389    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
390        unsafe { result.value_unchecked() }.set_number(arg0.number_value().log10());
391        1
392    } else {
393        -1
394    }
395}
396
397/// `luauF_log`
398unsafe fn log(
399    _thread: &Thread,
400    result: TValueCursor,
401    arg0: TValue,
402    nresults: i32,
403    args: TValueCursor,
404    nparams: i32,
405) -> i32 {
406    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
407        let a1 = arg0.number_value();
408        if nparams == 1 {
409            unsafe { result.value_unchecked() }.set_number(a1.ln());
410            return 1;
411        } else if unsafe { args.value_unchecked() }.is_number() {
412            let base = unsafe { args.value_unchecked() }.number_value();
413            let value = if base == 2.0 {
414                a1.log2()
415            } else if base == 10.0 {
416                a1.log10()
417            } else {
418                a1.ln() / base.ln()
419            };
420            unsafe { result.value_unchecked() }.set_number(value);
421            return 1;
422        }
423    }
424    -1
425}
426
427/// `luauF_max`
428unsafe fn max(
429    _thread: &Thread,
430    result: TValueCursor,
431    arg0: TValue,
432    nresults: i32,
433    args: TValueCursor,
434    nparams: i32,
435) -> i32 {
436    if nparams >= 2
437        && nresults <= 1
438        && arg0.is_number()
439        && unsafe { args.value_unchecked() }.is_number()
440    {
441        let a1 = arg0.number_value();
442        let a2 = unsafe { args.value_unchecked() }.number_value();
443        let mut r = if a2 > a1 { a2 } else { a1 };
444        for index in 3..=nparams {
445            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
446            if !arg.is_number() {
447                return -1;
448            }
449            let a = arg.number_value();
450            r = if a > r { a } else { r };
451        }
452        unsafe { result.value_unchecked() }.set_number(r);
453        1
454    } else {
455        -1
456    }
457}
458
459/// `luauF_min`
460unsafe fn min(
461    _thread: &Thread,
462    result: TValueCursor,
463    arg0: TValue,
464    nresults: i32,
465    args: TValueCursor,
466    nparams: i32,
467) -> i32 {
468    if nparams >= 2
469        && nresults <= 1
470        && arg0.is_number()
471        && unsafe { args.value_unchecked() }.is_number()
472    {
473        let a1 = arg0.number_value();
474        let a2 = unsafe { args.value_unchecked() }.number_value();
475        let mut r = if a2 < a1 { a2 } else { a1 };
476        for index in 3..=nparams {
477            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
478            if !arg.is_number() {
479                return -1;
480            }
481            let a = arg.number_value();
482            r = if a < r { a } else { r };
483        }
484        unsafe { result.value_unchecked() }.set_number(r);
485        1
486    } else {
487        -1
488    }
489}
490
491/// `luauF_modf`
492unsafe fn modf(
493    _thread: &Thread,
494    result: TValueCursor,
495    arg0: TValue,
496    nresults: i32,
497    _args: TValueCursor,
498    nparams: i32,
499) -> i32 {
500    if nparams >= 1 && nresults <= 2 && arg0.is_number() {
501        let a1 = arg0.number_value();
502        let ip = a1.trunc();
503        let fp = a1 - ip;
504        unsafe { result.value_unchecked() }.set_number(ip);
505        unsafe { result.add(1).value_unchecked() }.set_number(fp);
506        2
507    } else {
508        -1
509    }
510}
511
512/// `luauF_rad`
513unsafe fn rad(
514    _thread: &Thread,
515    result: TValueCursor,
516    arg0: TValue,
517    nresults: i32,
518    _args: TValueCursor,
519    nparams: i32,
520) -> i32 {
521    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
522        let radians_per_degree = std::f64::consts::PI / 180.0;
523        unsafe { result.value_unchecked() }.set_number(arg0.number_value() * radians_per_degree);
524        1
525    } else {
526        -1
527    }
528}
529
530/// `luauF_clamp`
531unsafe fn clamp(
532    _thread: &Thread,
533    result: TValueCursor,
534    arg0: TValue,
535    nresults: i32,
536    args: TValueCursor,
537    nparams: i32,
538) -> i32 {
539    if nparams >= 3
540        && nresults <= 1
541        && arg0.is_number()
542        && unsafe { args.value_unchecked() }.is_number()
543        && unsafe { args.add(1).value_unchecked().is_number() }
544    {
545        let value = arg0.number_value();
546        let min = unsafe { args.value_unchecked() }.number_value();
547        let max = unsafe { args.add(1).value_unchecked().number_value() };
548        if min <= max {
549            let value = if value < min { min } else { value };
550            unsafe { result.value_unchecked() }.set_number(if value > max { max } else { value });
551            return 1;
552        }
553    }
554
555    -1
556}
557
558/// `luauF_sign`
559unsafe fn sign(
560    _thread: &Thread,
561    result: TValueCursor,
562    arg0: TValue,
563    nresults: i32,
564    _args: TValueCursor,
565    nparams: i32,
566) -> i32 {
567    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
568        let value = arg0.number_value();
569        unsafe { result.value_unchecked() }.set_number(if value > 0.0 {
570            1.0
571        } else if value < 0.0 {
572            -1.0
573        } else {
574            0.0
575        });
576        return 1;
577    }
578
579    -1
580}
581
582/// `luauF_round`
583unsafe fn round(
584    _thread: &Thread,
585    result: TValueCursor,
586    arg0: TValue,
587    nresults: i32,
588    _args: TValueCursor,
589    nparams: i32,
590) -> i32 {
591    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
592        unsafe { result.value_unchecked() }.set_number(arg0.number_value().round());
593        return 1;
594    }
595
596    -1
597}
598
599/// `luauF_isnan`
600unsafe fn is_nan(
601    _thread: &Thread,
602    result: TValueCursor,
603    arg0: TValue,
604    nresults: i32,
605    _args: TValueCursor,
606    nparams: i32,
607) -> i32 {
608    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
609        unsafe { result.value_unchecked() }.set_boolean(i32::from(arg0.number_value().is_nan()));
610        return 1;
611    }
612    -1
613}
614
615/// `luauF_isinf`
616unsafe fn is_inf(
617    _thread: &Thread,
618    result: TValueCursor,
619    arg0: TValue,
620    nresults: i32,
621    _args: TValueCursor,
622    nparams: i32,
623) -> i32 {
624    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
625        unsafe { result.value_unchecked() }
626            .set_boolean(i32::from(arg0.number_value().is_infinite()));
627        return 1;
628    }
629    -1
630}
631
632/// `luauF_isfinite`
633unsafe fn is_finite(
634    _thread: &Thread,
635    result: TValueCursor,
636    arg0: TValue,
637    nresults: i32,
638    _args: TValueCursor,
639    nparams: i32,
640) -> i32 {
641    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
642        unsafe { result.value_unchecked() }.set_boolean(i32::from(arg0.number_value().is_finite()));
643        return 1;
644    }
645    -1
646}
647
648// Bitwise builtins
649
650/// `luauF_arshift`
651unsafe fn ar_shift(
652    _thread: &Thread,
653    result: TValueCursor,
654    arg0: TValue,
655    nresults: i32,
656    args: TValueCursor,
657    nparams: i32,
658) -> i32 {
659    if nparams >= 2
660        && nresults <= 1
661        && arg0.is_number()
662        && unsafe { args.value_unchecked() }.is_number()
663    {
664        let u = (arg0.number_value() as i64) as u32;
665        let s = unsafe { args.value_unchecked() }.number_value() as i32;
666        if (s as u32) < 32 {
667            unsafe { result.value_unchecked() }.set_number(((u as i32) >> s) as u32 as f64);
668            return 1;
669        }
670    }
671    -1
672}
673
674/// `luauF_band`
675unsafe fn band(
676    _thread: &Thread,
677    result: TValueCursor,
678    arg0: TValue,
679    nresults: i32,
680    args: TValueCursor,
681    nparams: i32,
682) -> i32 {
683    if nparams >= 2
684        && nresults <= 1
685        && arg0.is_number()
686        && unsafe { args.value_unchecked() }.is_number()
687    {
688        let mut r = ((arg0.number_value() as i64) as u32)
689            & ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
690        for index in 3..=nparams {
691            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
692            if !arg.is_number() {
693                return -1;
694            }
695            r &= (arg.number_value() as i64) as u32;
696        }
697        unsafe { result.value_unchecked() }.set_number(r as f64);
698        1
699    } else {
700        -1
701    }
702}
703
704/// `luauF_bnot`
705unsafe fn bnot(
706    _thread: &Thread,
707    result: TValueCursor,
708    arg0: TValue,
709    nresults: i32,
710    _args: TValueCursor,
711    nparams: i32,
712) -> i32 {
713    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
714        unsafe { result.value_unchecked() }
715            .set_number((!((arg0.number_value() as i64) as u32)) as f64);
716        1
717    } else {
718        -1
719    }
720}
721
722/// `luauF_bor`
723unsafe fn bor(
724    _thread: &Thread,
725    result: TValueCursor,
726    arg0: TValue,
727    nresults: i32,
728    args: TValueCursor,
729    nparams: i32,
730) -> i32 {
731    if nparams >= 2
732        && nresults <= 1
733        && arg0.is_number()
734        && unsafe { args.value_unchecked() }.is_number()
735    {
736        let mut r = ((arg0.number_value() as i64) as u32)
737            | ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
738        for index in 3..=nparams {
739            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
740            if !arg.is_number() {
741                return -1;
742            }
743            r |= (arg.number_value() as i64) as u32;
744        }
745        unsafe { result.value_unchecked() }.set_number(r as f64);
746        1
747    } else {
748        -1
749    }
750}
751
752/// `luauF_bxor`
753unsafe fn bxor(
754    _thread: &Thread,
755    result: TValueCursor,
756    arg0: TValue,
757    nresults: i32,
758    args: TValueCursor,
759    nparams: i32,
760) -> i32 {
761    if nparams >= 2
762        && nresults <= 1
763        && arg0.is_number()
764        && unsafe { args.value_unchecked() }.is_number()
765    {
766        let mut r = ((arg0.number_value() as i64) as u32)
767            ^ ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
768        for index in 3..=nparams {
769            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
770            if !arg.is_number() {
771                return -1;
772            }
773            r ^= (arg.number_value() as i64) as u32;
774        }
775        unsafe { result.value_unchecked() }.set_number(r as f64);
776        1
777    } else {
778        -1
779    }
780}
781
782/// `luauF_btest`
783unsafe fn btest(
784    _thread: &Thread,
785    result: TValueCursor,
786    arg0: TValue,
787    nresults: i32,
788    args: TValueCursor,
789    nparams: i32,
790) -> i32 {
791    if nparams >= 2
792        && nresults <= 1
793        && arg0.is_number()
794        && unsafe { args.value_unchecked() }.is_number()
795    {
796        let mut r = ((arg0.number_value() as i64) as u32)
797            & ((unsafe { args.value_unchecked() }.number_value() as i64) as u32);
798        for index in 3..=nparams {
799            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
800            if !arg.is_number() {
801                return -1;
802            }
803            r &= (arg.number_value() as i64) as u32;
804        }
805        unsafe { result.value_unchecked() }.set_boolean(i32::from(r != 0));
806        1
807    } else {
808        -1
809    }
810}
811
812/// `luauF_countlz`
813unsafe fn count_lz(
814    _thread: &Thread,
815    result: TValueCursor,
816    arg0: TValue,
817    nresults: i32,
818    _args: TValueCursor,
819    nparams: i32,
820) -> i32 {
821    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
822        let n = (arg0.number_value() as i64) as u32;
823        let r = if n == 0 { 32 } else { n.leading_zeros() };
824        unsafe { result.value_unchecked() }.set_number(r as f64);
825        1
826    } else {
827        -1
828    }
829}
830
831/// `luauF_countrz`
832unsafe fn count_rz(
833    _thread: &Thread,
834    result: TValueCursor,
835    arg0: TValue,
836    nresults: i32,
837    _args: TValueCursor,
838    nparams: i32,
839) -> i32 {
840    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
841        let n = (arg0.number_value() as i64) as u32;
842        let r = if n == 0 { 32 } else { n.trailing_zeros() };
843        unsafe { result.value_unchecked() }.set_number(r as f64);
844        1
845    } else {
846        -1
847    }
848}
849
850/// `luauF_byteswap`
851unsafe fn byte_swap(
852    _thread: &Thread,
853    result: TValueCursor,
854    arg0: TValue,
855    nresults: i32,
856    _args: TValueCursor,
857    nparams: i32,
858) -> i32 {
859    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
860        unsafe { result.value_unchecked() }
861            .set_number(((arg0.number_value() as i64) as u32).swap_bytes() as f64);
862        1
863    } else {
864        -1
865    }
866}
867
868/// `luauF_extract`
869unsafe fn extract(
870    _thread: &Thread,
871    result: TValueCursor,
872    arg0: TValue,
873    nresults: i32,
874    args: TValueCursor,
875    nparams: i32,
876) -> i32 {
877    if nparams >= 2
878        && nresults <= 1
879        && arg0.is_number()
880        && unsafe { args.value_unchecked() }.is_number()
881    {
882        let n = (arg0.number_value() as i64) as u32;
883        let field = unsafe { args.value_unchecked() }.number_value() as i32;
884
885        if nparams == 2 {
886            if (field as u32) < 32 {
887                unsafe { result.value_unchecked() }.set_number(((n >> field) & 1) as f64);
888                return 1;
889            }
890        } else if unsafe { args.add(1).value_unchecked() }.is_number() {
891            let width = unsafe { args.add(1).value_unchecked().number_value() } as i32;
892            if field >= 0 && width > 0 && field + width <= 32 {
893                let mask = !(0xfffffffe_u32 << (width - 1));
894                unsafe { result.value_unchecked() }.set_number(((n >> field) & mask) as f64);
895                return 1;
896            }
897        }
898    }
899    -1
900}
901
902/// `luauF_lrotate`
903unsafe fn rotate_left(
904    _thread: &Thread,
905    result: TValueCursor,
906    arg0: TValue,
907    nresults: i32,
908    args: TValueCursor,
909    nparams: i32,
910) -> i32 {
911    if nparams >= 2
912        && nresults <= 1
913        && arg0.is_number()
914        && unsafe { args.value_unchecked() }.is_number()
915    {
916        let value = (arg0.number_value() as i64) as u32;
917        let shift = unsafe { args.value_unchecked() }.number_value() as i32;
918        unsafe { result.value_unchecked() }
919            .set_number(value.rotate_left((shift & 31) as u32) as f64);
920        return 1;
921    }
922    -1
923}
924
925/// `luauF_lshift`
926unsafe fn shift_left(
927    _thread: &Thread,
928    result: TValueCursor,
929    arg0: TValue,
930    nresults: i32,
931    args: TValueCursor,
932    nparams: i32,
933) -> i32 {
934    if nparams >= 2
935        && nresults <= 1
936        && arg0.is_number()
937        && unsafe { args.value_unchecked() }.is_number()
938    {
939        let value = (arg0.number_value() as i64) as u32;
940        let shift = unsafe { args.value_unchecked() }.number_value() as i32;
941        if (shift as u32) < 32 {
942            unsafe { result.value_unchecked() }.set_number((value << shift) as f64);
943            return 1;
944        }
945    }
946    -1
947}
948
949/// `luauF_replace`
950unsafe fn replace(
951    _thread: &Thread,
952    result: TValueCursor,
953    arg0: TValue,
954    nresults: i32,
955    args: TValueCursor,
956    nparams: i32,
957) -> i32 {
958    unsafe {
959        let field_arg = args.add(1).value_unchecked();
960        if nparams >= 3
961            && nresults <= 1
962            && arg0.is_number()
963            && args.value_unchecked().is_number()
964            && field_arg.is_number()
965        {
966            let number = (arg0.number_value() as i64) as u32;
967            let value = (args.value_unchecked().number_value() as i64) as u32;
968            let field = field_arg.number_value() as i32;
969
970            if nparams == 3 {
971                if (field as u32) < 32 {
972                    let mask = 1u32;
973                    let replaced = (number & !(mask << field)) | ((value & mask) << field);
974                    result.value_unchecked().set_number(replaced as f64);
975                    return 1;
976                }
977            } else {
978                let width_arg = args.add(2).value_unchecked();
979                if width_arg.is_number() {
980                    let width = width_arg.number_value() as i32;
981                    if field >= 0 && width > 0 && field + width <= 32 {
982                        let mask = !(0xfffffffe_u32 << (width - 1));
983                        let replaced = (number & !(mask << field)) | ((value & mask) << field);
984                        result.value_unchecked().set_number(replaced as f64);
985                        return 1;
986                    }
987                }
988            }
989        }
990    }
991    -1
992}
993
994/// `luauF_rrotate`
995unsafe fn r_rotate(
996    _thread: &Thread,
997    result: TValueCursor,
998    arg0: TValue,
999    nresults: i32,
1000    args: TValueCursor,
1001    nparams: i32,
1002) -> i32 {
1003    if nparams >= 2
1004        && nresults <= 1
1005        && arg0.is_number()
1006        && unsafe { args.value_unchecked() }.is_number()
1007    {
1008        let value = (arg0.number_value() as i64) as u32;
1009        let shift = unsafe { args.value_unchecked() }.number_value() as i32;
1010        unsafe { result.value_unchecked() }
1011            .set_number(value.rotate_right((shift & 31) as u32) as f64);
1012        return 1;
1013    }
1014    -1
1015}
1016
1017/// `luauF_rshift`
1018unsafe fn r_shift(
1019    _thread: &Thread,
1020    result: TValueCursor,
1021    arg0: TValue,
1022    nresults: i32,
1023    args: TValueCursor,
1024    nparams: i32,
1025) -> i32 {
1026    if nparams >= 2
1027        && nresults <= 1
1028        && arg0.is_number()
1029        && unsafe { args.value_unchecked() }.is_number()
1030    {
1031        let value = (arg0.number_value() as i64) as u32;
1032        let shift = unsafe { args.value_unchecked() }.number_value() as i32;
1033        if (shift as u32) < 32 {
1034            unsafe { result.value_unchecked() }.set_number((value >> shift) as f64);
1035            return 1;
1036        }
1037    }
1038    -1
1039}
1040
1041/// `luauF_extractk`
1042unsafe fn extract_k(
1043    _thread: &Thread,
1044    result: TValueCursor,
1045    arg0: TValue,
1046    nresults: i32,
1047    args: TValueCursor,
1048    nparams: i32,
1049) -> i32 {
1050    if nparams >= 2
1051        && nresults <= 1
1052        && arg0.is_number()
1053        && unsafe { args.value_unchecked() }.is_number()
1054    {
1055        let number = (arg0.number_value() as i64) as u32;
1056        let packed = unsafe { args.value_unchecked() }.number_value() as i32;
1057        let field = packed & 31;
1058        let width_minus_one = packed >> 5;
1059        let mask = !(0xfffffffe_u32 << width_minus_one);
1060        unsafe { result.value_unchecked() }.set_number(((number >> field) & mask) as f64);
1061        return 1;
1062    }
1063    -1
1064}
1065
1066// String builtins
1067
1068/// `luauF_byte`
1069unsafe fn byte(
1070    _thread: &Thread,
1071    result: TValueCursor,
1072    arg0: TValue,
1073    nresults: i32,
1074    args: TValueCursor,
1075    nparams: i32,
1076) -> i32 {
1077    if nparams >= 2 && arg0.is_string() && unsafe { args.value_unchecked() }.is_number() {
1078        unsafe {
1079            let string = arg0.string_value();
1080            let start = args.value_unchecked().number_value() as i32;
1081            let end = if nparams >= 3 {
1082                if !args.add(1).value_unchecked().is_number() {
1083                    return -1;
1084                }
1085                args.add(1).value_unchecked().number_value() as i32
1086            } else {
1087                start
1088            };
1089
1090            let len = string.as_ptr().as_ref().unwrap_unchecked().len as i32;
1091            if start >= 1 && end >= start && end <= len {
1092                let count = end - start + 1;
1093                let expected = if nresults < 0 { 1 } else { nresults };
1094                if count == expected {
1095                    let bytes = string.as_bytes();
1096                    for index in 0..count as usize {
1097                        result
1098                            .add(index)
1099                            .value_unchecked()
1100                            .set_number(bytes[start as usize + index - 1] as f64);
1101                    }
1102                    return count;
1103                }
1104            }
1105        }
1106    }
1107    -1
1108}
1109
1110/// `luauF_char`
1111unsafe fn char(
1112    thread: &Thread,
1113    result: TValueCursor,
1114    arg0: TValue,
1115    nresults: i32,
1116    args: TValueCursor,
1117    nparams: i32,
1118) -> i32 {
1119    let mut buffer = [0u8; 8];
1120    if nparams < buffer.len() as i32 && nresults <= 1 {
1121        unsafe {
1122            if thread.needs_gc() {
1123                return -1;
1124            }
1125
1126            if nparams >= 1 {
1127                if !arg0.is_number() {
1128                    return -1;
1129                }
1130                let ch = arg0.number_value() as i32;
1131                if (ch as u8) as i32 != ch {
1132                    return -1;
1133                }
1134                buffer[0] = ch as u8;
1135            }
1136
1137            for index in 2..=nparams {
1138                let arg = args.add((index - 2) as usize);
1139                if !arg.value_unchecked().is_number() {
1140                    return -1;
1141                }
1142                let ch = arg.value_unchecked().number_value() as i32;
1143                if (ch as u8) as i32 != ch {
1144                    return -1;
1145                }
1146                buffer[index as usize - 1] = ch as u8;
1147            }
1148
1149            buffer[nparams as usize] = 0;
1150            let Ok(string) = thread.intern_string(buffer[..nparams as usize].as_bstr()) else {
1151                return -1;
1152            };
1153            result.value_unchecked().set_string_value(string);
1154        }
1155        return 1;
1156    }
1157
1158    -1
1159}
1160
1161/// `luauF_sub`
1162unsafe fn sub(
1163    thread: &Thread,
1164    result: TValueCursor,
1165    arg0: TValue,
1166    nresults: i32,
1167    args: TValueCursor,
1168    nparams: i32,
1169) -> i32 {
1170    unsafe {
1171        if nparams >= 3
1172            && nresults <= 1
1173            && arg0.is_string()
1174            && args.value_unchecked().is_number()
1175            && args.add(1).value_unchecked().is_number()
1176        {
1177            if thread.needs_gc() {
1178                return -1;
1179            }
1180
1181            let string = arg0.string_value();
1182            let start = args.value_unchecked().number_value() as i32;
1183            let end = args.add(1).value_unchecked().number_value() as i32;
1184            let len = string.as_ptr().as_ref().unwrap_unchecked().len as i32;
1185
1186            if start >= 1 && end >= start && ((end - 1) as u32) < (len as u32) {
1187                let bytes = string.as_bytes();
1188                let Ok(slice) =
1189                    thread.intern_string(bytes[(start - 1) as usize..end as usize].as_bstr())
1190                else {
1191                    return -1;
1192                };
1193                result.value_unchecked().set_string_value(slice);
1194                return 1;
1195            }
1196        }
1197
1198        -1
1199    }
1200}
1201
1202// Table builtins
1203/// `luauF_rawequal`
1204unsafe fn raw_equal(
1205    _thread: &Thread,
1206    result: TValueCursor,
1207    arg0: TValue,
1208    nresults: i32,
1209    args: TValueCursor,
1210    nparams: i32,
1211) -> i32 {
1212    if nparams >= 2 && nresults <= 1 {
1213        unsafe { result.value_unchecked() }
1214            .set_boolean(i32::from(arg0.raw_equal(unsafe { args.value_unchecked() })));
1215        1
1216    } else {
1217        -1
1218    }
1219}
1220
1221/// `luauF_rawget`
1222unsafe fn raw_get(
1223    _thread: &Thread,
1224    result: TValueCursor,
1225    arg0: TValue,
1226    nresults: i32,
1227    args: TValueCursor,
1228    nparams: i32,
1229) -> i32 {
1230    if nparams >= 2 && nresults <= 1 && arg0.is_table() {
1231        unsafe { result.value_unchecked() }
1232            .set_obj(unsafe { arg0.table_value().get(args.value_unchecked()) });
1233        1
1234    } else {
1235        -1
1236    }
1237}
1238
1239/// `luauF_rawset`
1240unsafe fn raw_set(
1241    thread: &Thread,
1242    result: TValueCursor,
1243    arg0: TValue,
1244    nresults: i32,
1245    args: TValueCursor,
1246    nparams: i32,
1247) -> i32 {
1248    if nparams >= 3 && nresults <= 1 && arg0.is_table() {
1249        unsafe {
1250            let key = args;
1251            let key_value = key.value_unchecked();
1252            if key_value.is_nil()
1253                || (key_value.is_number() && key_value.number_value().is_nan())
1254                || (key_value.is_vector() && key_value.vector_value().iter().any(|v| v.is_nan()))
1255            {
1256                return -1;
1257            }
1258
1259            let table = arg0.table_value();
1260            if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
1261                return -1;
1262            }
1263
1264            result.value_unchecked().set_obj(arg0);
1265            let Ok(slot) = thread.set(table, key_value) else {
1266                return -1;
1267            };
1268            let value = args.add(1);
1269            slot.set_obj(value.value_unchecked());
1270            if value.value_unchecked().is_collectable() {
1271                let child = value.value_unchecked().gc_value();
1272                let table_object: GcObject = table.into();
1273                if table_object.is_black() && child.is_white() {
1274                    thread.barrier_table(table, child);
1275                }
1276            }
1277            1
1278        }
1279    } else {
1280        -1
1281    }
1282}
1283
1284/// `luauF_tinsert`
1285unsafe fn t_insert(
1286    thread: &Thread,
1287    _result: TValueCursor,
1288    arg0: TValue,
1289    nresults: i32,
1290    args: TValueCursor,
1291    nparams: i32,
1292) -> i32 {
1293    if nparams == 2 && nresults <= 0 && arg0.is_table() {
1294        unsafe {
1295            let table = arg0.table_value();
1296            if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
1297                return -1;
1298            }
1299
1300            let pos = table.getn() + 1;
1301            let slot = if let Some(slot) = table.array_slot_for_key(pos) {
1302                slot
1303            } else {
1304                let Ok(slot) = thread.set_num(table, pos) else {
1305                    return -1;
1306                };
1307                slot
1308            };
1309            let value = args.value_unchecked();
1310            slot.set_obj(value);
1311            if value.is_collectable() {
1312                let child = value.gc_value();
1313                let table_object: GcObject = table.into();
1314                if table_object.is_black() && child.is_white() {
1315                    thread.barrier_table(table, child);
1316                }
1317            }
1318            0
1319        }
1320    } else {
1321        -1
1322    }
1323}
1324
1325/// `luauF_tunpack`
1326unsafe fn t_unpack(
1327    thread: &Thread,
1328    result: TValueCursor,
1329    arg0: TValue,
1330    nresults: i32,
1331    args: TValueCursor,
1332    nparams: i32,
1333) -> i32 {
1334    if nparams >= 1 && nresults < 0 && arg0.is_table() {
1335        unsafe {
1336            let table = arg0.table_value();
1337            let mut n = -1;
1338            if nparams == 1 {
1339                n = table.getn();
1340            } else if nparams == 3
1341                && args.value_unchecked().is_number()
1342                && args.add(1).value_unchecked().is_number()
1343                && args.value_unchecked().number_value() == 1.0
1344            {
1345                n = args.add(1).value_unchecked().number_value() as i32;
1346            }
1347
1348            let stack_last = thread.stack_last();
1349            let stack_room = stack_last.offset_from(result) as i32;
1350            if n >= 0
1351                && n <= table.as_ptr().as_ref().unwrap_unchecked().size_array
1352                && stack_room >= n
1353                && n + nparams <= 8000
1354            {
1355                let array = table.array_cursor();
1356                for index in 0..n as usize {
1357                    result
1358                        .add(index)
1359                        .value_unchecked()
1360                        .set_obj(array.add(index).value_unchecked());
1361                }
1362                thread.expand_stack_limit(result.add(n as usize));
1363                return n;
1364            }
1365        }
1366    }
1367    -1
1368}
1369
1370/// `luauF_rawlen`
1371unsafe fn raw_len(
1372    _thread: &Thread,
1373    result: TValueCursor,
1374    arg0: TValue,
1375    nresults: i32,
1376    _args: TValueCursor,
1377    nparams: i32,
1378) -> i32 {
1379    if nparams >= 1 && nresults <= 1 {
1380        if arg0.is_table() {
1381            unsafe { result.value_unchecked() }
1382                .set_number(unsafe { arg0.table_value().getn() as f64 });
1383            return 1;
1384        } else if arg0.is_string() {
1385            unsafe { result.value_unchecked() }.set_number(unsafe {
1386                arg0.string_value().as_ptr().as_ref().unwrap_unchecked().len as f64
1387            });
1388            return 1;
1389        }
1390    }
1391    -1
1392}
1393
1394/// `luauF_getmetatable`
1395unsafe fn get_metatable(
1396    thread: &Thread,
1397    result: TValueCursor,
1398    arg0: TValue,
1399    nresults: i32,
1400    _args: TValueCursor,
1401    nparams: i32,
1402) -> i32 {
1403    if nparams >= 1 && nresults <= 1 {
1404        let (mt, mtv) = unsafe {
1405            match arg0.tt() {
1406                x if x == LUA_TTABLE => arg0.table_value().metatable(),
1407                x if x == LUA_TUSERDATA => arg0.userdata_value().metatable(),
1408                tag => thread.global().metatable(tag as usize),
1409            }
1410            .map_or_else(
1411                || (None, crate::value::nil_object()),
1412                |mt: Table| {
1413                    let name = thread.global().tm_name(TmEvent::MetaTable as usize);
1414                    (Some(mt), mt.get_str(name))
1415                },
1416            )
1417        };
1418
1419        if !mtv.is_nil() {
1420            unsafe { result.value_unchecked() }.set_obj(mtv);
1421        } else if let Some(mt) = mt {
1422            unsafe { result.value_unchecked() }.set_table_value(mt);
1423        } else {
1424            unsafe { result.value_unchecked() }.set_nil();
1425        }
1426        return 1;
1427    }
1428    -1
1429}
1430
1431/// `luauF_setmetatable`
1432unsafe fn set_metatable(
1433    thread: &Thread,
1434    result: TValueCursor,
1435    arg0: TValue,
1436    nresults: i32,
1437    args: TValueCursor,
1438    nparams: i32,
1439) -> i32 {
1440    unsafe {
1441        if nparams >= 2 && nresults <= 1 && arg0.is_table() && args.value_unchecked().is_table() {
1442            let table = arg0.table_value();
1443            if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0
1444                || table.metatable().is_some()
1445            {
1446                return -1;
1447            }
1448
1449            let mt = args.value_unchecked().table_value();
1450            table.set_metatable(Some(mt));
1451            let object: GcObject = table.into();
1452            let child: GcObject = mt.into();
1453            if object.is_black() && child.is_white() {
1454                thread.barrier_forward(object, child);
1455            }
1456            result.value_unchecked().set_table_value(table);
1457            return 1;
1458        }
1459    }
1460    -1
1461}
1462
1463// Vector builtins
1464
1465unsafe fn set_vector(result: TValueCursor, x: f32, y: f32, z: f32, w: f32) {
1466    #[cfg(feature = "vector4")]
1467    unsafe { result.value_unchecked() }.set_vector([x, y, z, w]);
1468
1469    #[cfg(not(feature = "vector4"))]
1470    let _ = w;
1471    #[cfg(not(feature = "vector4"))]
1472    unsafe { result.value_unchecked() }.set_vector([x, y, z]);
1473}
1474
1475/// `luauF_vector`
1476unsafe fn vector(
1477    _thread: &Thread,
1478    result: TValueCursor,
1479    arg0: TValue,
1480    nresults: i32,
1481    args: TValueCursor,
1482    nparams: i32,
1483) -> i32 {
1484    unsafe {
1485        if nparams >= 2 && nresults <= 1 && arg0.is_number() && args.value_unchecked().is_number() {
1486            let x = arg0.number_value() as f32;
1487            let y = args.value_unchecked().number_value() as f32;
1488            let mut z = 0.0f32;
1489            if nparams >= 3 {
1490                if !args.add(1).value_unchecked().is_number() {
1491                    return -1;
1492                }
1493                z = args.add(1).value_unchecked().number_value() as f32;
1494            }
1495            #[cfg(feature = "vector4")]
1496            {
1497                let mut w = 0.0f32;
1498                if nparams >= 4 {
1499                    if !args.add(2).value_unchecked().is_number() {
1500                        return -1;
1501                    }
1502                    w = args.add(2).value_unchecked().number_value() as f32;
1503                }
1504                set_vector(result, x, y, z, w);
1505            }
1506            #[cfg(not(feature = "vector4"))]
1507            {
1508                set_vector(result, x, y, z, 0.0);
1509            }
1510            1
1511        } else {
1512            -1
1513        }
1514    }
1515}
1516
1517/// `luauF_vectormagnitude`
1518unsafe fn vector_magnitude(
1519    _thread: &Thread,
1520    result: TValueCursor,
1521    arg0: TValue,
1522    nresults: i32,
1523    _args: TValueCursor,
1524    nparams: i32,
1525) -> i32 {
1526    if nparams >= 1 && nresults <= 1 && arg0.is_vector() {
1527        let vector = arg0.vector_value();
1528        let mut sum = 0.0f32;
1529        for value in vector {
1530            sum += value * value;
1531        }
1532        unsafe { result.value_unchecked() }.set_number(sum.sqrt() as f64);
1533        return 1;
1534    }
1535    -1
1536}
1537
1538/// `luauF_vectornormalize`
1539unsafe fn vector_normalize(
1540    _thread: &Thread,
1541    result: TValueCursor,
1542    arg0: TValue,
1543    nresults: i32,
1544    _args: TValueCursor,
1545    nparams: i32,
1546) -> i32 {
1547    if nparams >= 1 && nresults <= 1 && arg0.is_vector() {
1548        let vector = arg0.vector_value();
1549        let mut sum = 0.0f32;
1550        for value in vector {
1551            sum += value * value;
1552        }
1553        let inv_sqrt = 1.0f32 / sum.sqrt();
1554        #[cfg(feature = "vector4")]
1555        unsafe {
1556            set_vector(
1557                result,
1558                vector[0] * inv_sqrt,
1559                vector[1] * inv_sqrt,
1560                vector[2] * inv_sqrt,
1561                vector[3] * inv_sqrt,
1562            );
1563        }
1564        #[cfg(not(feature = "vector4"))]
1565        unsafe {
1566            set_vector(
1567                result,
1568                vector[0] * inv_sqrt,
1569                vector[1] * inv_sqrt,
1570                vector[2] * inv_sqrt,
1571                0.0,
1572            );
1573        }
1574        return 1;
1575    }
1576    -1
1577}
1578
1579/// `luauF_vectorcross`
1580unsafe fn vector_cross(
1581    _thread: &Thread,
1582    result: TValueCursor,
1583    arg0: TValue,
1584    nresults: i32,
1585    args: TValueCursor,
1586    nparams: i32,
1587) -> i32 {
1588    if nparams >= 2
1589        && nresults <= 1
1590        && arg0.is_vector()
1591        && unsafe { args.value_unchecked() }.is_vector()
1592    {
1593        let a = arg0.vector_value();
1594        let b = unsafe { args.value_unchecked() }.vector_value();
1595        unsafe {
1596            set_vector(
1597                result,
1598                a[1] * b[2] - a[2] * b[1],
1599                a[2] * b[0] - a[0] * b[2],
1600                a[0] * b[1] - a[1] * b[0],
1601                0.0,
1602            );
1603        }
1604        return 1;
1605    }
1606    -1
1607}
1608
1609/// `luauF_vectordot`
1610unsafe fn vector_dot(
1611    _thread: &Thread,
1612    result: TValueCursor,
1613    arg0: TValue,
1614    nresults: i32,
1615    args: TValueCursor,
1616    nparams: i32,
1617) -> i32 {
1618    if nparams >= 2
1619        && nresults <= 1
1620        && arg0.is_vector()
1621        && unsafe { args.value_unchecked() }.is_vector()
1622    {
1623        let a = arg0.vector_value();
1624        let b = unsafe { args.value_unchecked() }.vector_value();
1625        let mut dot = 0.0f32;
1626        for (left, right) in a.iter().zip(b.iter()) {
1627            dot += left * right;
1628        }
1629        unsafe { result.value_unchecked() }.set_number(dot as f64);
1630        return 1;
1631    }
1632    -1
1633}
1634
1635macro_rules! unary_vector {
1636    ($doc:literal, $name:ident, $op:expr) => {
1637        #[doc = $doc]
1638        unsafe fn $name(
1639            _thread: &Thread,
1640            result: TValueCursor,
1641            arg0: TValue,
1642            nresults: i32,
1643            _args: TValueCursor,
1644            nparams: i32,
1645        ) -> i32 {
1646            if nparams >= 1 && nresults <= 1 && arg0.is_vector() {
1647                let vector = arg0.vector_value();
1648                #[cfg(feature = "vector4")]
1649                unsafe {
1650                    set_vector(
1651                        result,
1652                        ($op)(vector[0]),
1653                        ($op)(vector[1]),
1654                        ($op)(vector[2]),
1655                        ($op)(vector[3]),
1656                    );
1657                }
1658                #[cfg(not(feature = "vector4"))]
1659                unsafe {
1660                    set_vector(
1661                        result,
1662                        ($op)(vector[0]),
1663                        ($op)(vector[1]),
1664                        ($op)(vector[2]),
1665                        0.0,
1666                    );
1667                }
1668                return 1;
1669            }
1670            -1
1671        }
1672    };
1673}
1674
1675unary_vector!("`luauF_vectorfloor`", vector_floor, f32::floor);
1676unary_vector!("`luauF_vectorceil`", vector_ceil, f32::ceil);
1677unary_vector!("`luauF_vectorabs`", vector_abs, f32::abs);
1678unary_vector!("`luauF_vectorsign`", vector_sign, sign_f);
1679
1680/// `luauF_vectorclamp`
1681unsafe fn vector_clamp(
1682    _thread: &Thread,
1683    result: TValueCursor,
1684    arg0: TValue,
1685    nresults: i32,
1686    args: TValueCursor,
1687    nparams: i32,
1688) -> i32 {
1689    unsafe {
1690        let max_arg = args.add(1).value_unchecked();
1691        if nparams >= 3
1692            && nresults <= 1
1693            && arg0.is_vector()
1694            && args.value_unchecked().is_vector()
1695            && max_arg.is_vector()
1696        {
1697            let value = arg0.vector_value();
1698            let min = args.value_unchecked().vector_value();
1699            let max = max_arg.vector_value();
1700            if min[0] <= max[0] && min[1] <= max[1] && min[2] <= max[2] {
1701                #[cfg(feature = "vector4")]
1702                set_vector(
1703                    result,
1704                    clamp_f(value[0], min[0], max[0]),
1705                    clamp_f(value[1], min[1], max[1]),
1706                    clamp_f(value[2], min[2], max[2]),
1707                    clamp_f(value[3], min[3], max[3]),
1708                );
1709                #[cfg(not(feature = "vector4"))]
1710                set_vector(
1711                    result,
1712                    clamp_f(value[0], min[0], max[0]),
1713                    clamp_f(value[1], min[1], max[1]),
1714                    clamp_f(value[2], min[2], max[2]),
1715                    0.0,
1716                );
1717                return 1;
1718            }
1719        }
1720    }
1721    -1
1722}
1723
1724/// `luauF_vectormin`
1725unsafe fn vector_min(
1726    _thread: &Thread,
1727    result: TValueCursor,
1728    arg0: TValue,
1729    nresults: i32,
1730    args: TValueCursor,
1731    nparams: i32,
1732) -> i32 {
1733    unsafe {
1734        if nparams >= 2 && nresults <= 1 && arg0.is_vector() && args.value_unchecked().is_vector() {
1735            let mut value = arg0.vector_value();
1736            let other = args.value_unchecked().vector_value();
1737            for (slot, candidate) in value.iter_mut().zip(other.iter()) {
1738                if *candidate < *slot {
1739                    *slot = *candidate;
1740                }
1741            }
1742            for index in 3..=nparams {
1743                let arg = args.add((index - 2) as usize).value_unchecked();
1744                if !arg.is_vector() {
1745                    return -1;
1746                }
1747                let candidate = arg.vector_value();
1748                for (slot, candidate) in value.iter_mut().zip(candidate.iter()) {
1749                    if *candidate < *slot {
1750                        *slot = *candidate;
1751                    }
1752                }
1753            }
1754            #[cfg(feature = "vector4")]
1755            set_vector(result, value[0], value[1], value[2], value[3]);
1756            #[cfg(not(feature = "vector4"))]
1757            set_vector(result, value[0], value[1], value[2], 0.0);
1758            return 1;
1759        }
1760    }
1761    -1
1762}
1763
1764/// `luauF_vectormax`
1765unsafe fn vector_max(
1766    _thread: &Thread,
1767    result: TValueCursor,
1768    arg0: TValue,
1769    nresults: i32,
1770    args: TValueCursor,
1771    nparams: i32,
1772) -> i32 {
1773    unsafe {
1774        if nparams >= 2 && nresults <= 1 && arg0.is_vector() && args.value_unchecked().is_vector() {
1775            let mut value = arg0.vector_value();
1776            let other = args.value_unchecked().vector_value();
1777            for (slot, candidate) in value.iter_mut().zip(other.iter()) {
1778                if *candidate > *slot {
1779                    *slot = *candidate;
1780                }
1781            }
1782            for index in 3..=nparams {
1783                let arg = args.add((index - 2) as usize).value_unchecked();
1784                if !arg.is_vector() {
1785                    return -1;
1786                }
1787                let candidate = arg.vector_value();
1788                for (slot, candidate) in value.iter_mut().zip(candidate.iter()) {
1789                    if *candidate > *slot {
1790                        *slot = *candidate;
1791                    }
1792                }
1793            }
1794            #[cfg(feature = "vector4")]
1795            set_vector(result, value[0], value[1], value[2], value[3]);
1796            #[cfg(not(feature = "vector4"))]
1797            set_vector(result, value[0], value[1], value[2], 0.0);
1798            return 1;
1799        }
1800    }
1801    -1
1802}
1803
1804/// `luauF_lerp`
1805unsafe fn lerp(
1806    _thread: &Thread,
1807    result: TValueCursor,
1808    arg0: TValue,
1809    nresults: i32,
1810    args: TValueCursor,
1811    nparams: i32,
1812) -> i32 {
1813    if nparams >= 3
1814        && nresults <= 1
1815        && arg0.is_number()
1816        && unsafe { args.value_unchecked() }.is_number()
1817        && unsafe { args.add(1).value_unchecked() }.is_number()
1818    {
1819        let a = arg0.number_value();
1820        let b = unsafe { args.value_unchecked() }.number_value();
1821        let t = unsafe { args.add(1).value_unchecked() }.number_value();
1822        let value = if t == 1.0 { b } else { a + (b - a) * t };
1823        unsafe { result.value_unchecked() }.set_number(value);
1824        return 1;
1825    }
1826    -1
1827}
1828
1829/// `luauF_vectorlerp`
1830unsafe fn vector_lerp(
1831    _thread: &Thread,
1832    result: TValueCursor,
1833    arg0: TValue,
1834    nresults: i32,
1835    args: TValueCursor,
1836    nparams: i32,
1837) -> i32 {
1838    unsafe {
1839        let t_arg = args.add(1).value_unchecked();
1840        if nparams >= 3
1841            && nresults <= 1
1842            && arg0.is_vector()
1843            && args.value_unchecked().is_vector()
1844            && t_arg.is_number()
1845        {
1846            let a = arg0.vector_value();
1847            let b = args.value_unchecked().vector_value();
1848            let t = t_arg.number_value() as f32;
1849            #[cfg(feature = "vector4")]
1850            set_vector(
1851                result,
1852                lerp_f(a[0], b[0], t),
1853                lerp_f(a[1], b[1], t),
1854                lerp_f(a[2], b[2], t),
1855                lerp_f(a[3], b[3], t),
1856            );
1857            #[cfg(not(feature = "vector4"))]
1858            set_vector(
1859                result,
1860                lerp_f(a[0], b[0], t),
1861                lerp_f(a[1], b[1], t),
1862                lerp_f(a[2], b[2], t),
1863                0.0,
1864            );
1865            return 1;
1866        }
1867    }
1868    -1
1869}
1870
1871// Integer builtins
1872
1873macro_rules! unary_int {
1874    ($doc:literal, $name:ident, $op:expr) => {
1875        #[doc = $doc]
1876        unsafe fn $name(
1877            _thread: &Thread,
1878            result: TValueCursor,
1879            arg0: TValue,
1880            nresults: i32,
1881            _args: TValueCursor,
1882            nparams: i32,
1883        ) -> i32 {
1884            if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
1885                unsafe { result.value_unchecked() }.set_integer(($op)(arg0.integer_value()));
1886                1
1887            } else {
1888                -1
1889            }
1890        }
1891    };
1892}
1893
1894macro_rules! binary_int {
1895    ($doc:literal, $name:ident, $op:expr) => {
1896        #[doc = $doc]
1897        unsafe fn $name(
1898            _thread: &Thread,
1899            result: TValueCursor,
1900            arg0: TValue,
1901            nresults: i32,
1902            args: TValueCursor,
1903            nparams: i32,
1904        ) -> i32 {
1905            if nparams >= 2
1906                && nresults <= 1
1907                && arg0.is_integer()
1908                && unsafe { args.value_unchecked() }.is_integer()
1909            {
1910                unsafe { result.value_unchecked() }.set_integer(($op)(
1911                    arg0.integer_value(),
1912                    unsafe { args.value_unchecked() }.integer_value(),
1913                ));
1914                1
1915            } else {
1916                -1
1917            }
1918        }
1919    };
1920}
1921
1922unary_int!(
1923    "`luauF_integer_neg`",
1924    integer_neg,
1925    |a: i64| (!(a as u64)).wrapping_add(1) as i64
1926);
1927binary_int!("`luauF_integer_add`", integer_add, |a: i64, b: i64| {
1928    ((a as u64).wrapping_add(b as u64)) as i64
1929});
1930binary_int!("`luauF_integer_sub`", integer_sub, |a: i64, b: i64| {
1931    ((a as u64).wrapping_sub(b as u64)) as i64
1932});
1933binary_int!("`luauF_integer_mul`", integer_mul, |a: i64, b: i64| {
1934    ((a as u64).wrapping_mul(b as u64)) as i64
1935});
1936
1937/// `luauF_integertonumber`
1938unsafe fn integer_to_number(
1939    _thread: &Thread,
1940    result: TValueCursor,
1941    arg0: TValue,
1942    nresults: i32,
1943    _args: TValueCursor,
1944    nparams: i32,
1945) -> i32 {
1946    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
1947        unsafe { result.value_unchecked() }.set_number(arg0.integer_value() as f64);
1948        return 1;
1949    }
1950    -1
1951}
1952
1953macro_rules! binary_integer_predicate {
1954    ($doc:literal, $name:ident, $left:expr, $right:expr, $pred:expr) => {
1955        #[doc = $doc]
1956        unsafe fn $name(
1957            _thread: &Thread,
1958            result: TValueCursor,
1959            arg0: TValue,
1960            nresults: i32,
1961            args: TValueCursor,
1962            nparams: i32,
1963        ) -> i32 {
1964            if nparams >= 2
1965                && nresults <= 1
1966                && arg0.is_integer()
1967                && unsafe { args.value_unchecked() }.is_integer()
1968            {
1969                let left = $left(arg0.integer_value());
1970                let right = $right(unsafe { args.value_unchecked() }.integer_value());
1971                unsafe { result.value_unchecked() }.set_boolean(i32::from(($pred)(left, right)));
1972                return 1;
1973            }
1974            -1
1975        }
1976    };
1977}
1978
1979binary_integer_predicate!(
1980    "`luauF_integerlt`",
1981    integer_lt,
1982    |value: i64| value,
1983    |value: i64| value,
1984    |a, b| a < b
1985);
1986binary_integer_predicate!(
1987    "`luauF_integerle`",
1988    integer_le,
1989    |value: i64| value,
1990    |value: i64| value,
1991    |a, b| a <= b
1992);
1993binary_integer_predicate!(
1994    "`luauF_integergt`",
1995    integer_gt,
1996    |value: i64| value,
1997    |value: i64| value,
1998    |a, b| a > b
1999);
2000binary_integer_predicate!(
2001    "`luauF_integerge`",
2002    integer_ge,
2003    |value: i64| value,
2004    |value: i64| value,
2005    |a, b| a >= b
2006);
2007binary_integer_predicate!(
2008    "`luauF_integerult`",
2009    integer_ult,
2010    |value: i64| value as u64,
2011    |value: i64| value as u64,
2012    |a, b| a < b
2013);
2014binary_integer_predicate!(
2015    "`luauF_integerule`",
2016    integer_ule,
2017    |value: i64| value as u64,
2018    |value: i64| value as u64,
2019    |a, b| a <= b
2020);
2021binary_integer_predicate!(
2022    "`luauF_integerugt`",
2023    integer_ugt,
2024    |value: i64| value as u64,
2025    |value: i64| value as u64,
2026    |a, b| a > b
2027);
2028binary_integer_predicate!(
2029    "`luauF_integeruge`",
2030    integer_uge,
2031    |value: i64| value as u64,
2032    |value: i64| value as u64,
2033    |a, b| a >= b
2034);
2035
2036/// `luauF_integercreate`
2037unsafe fn integer_create(
2038    _thread: &Thread,
2039    result: TValueCursor,
2040    arg0: TValue,
2041    nresults: i32,
2042    _args: TValueCursor,
2043    nparams: i32,
2044) -> i32 {
2045    if nparams >= 1 && nresults <= 1 && arg0.is_number() {
2046        let value = arg0.number_value();
2047        if (i64::MIN as f64) <= value && value < 9223372036854775808.0 {
2048            let integer = value as i64;
2049            if integer as f64 == value {
2050                unsafe { result.value_unchecked() }.set_integer(integer);
2051                return 1;
2052            }
2053        }
2054        unsafe { result.value_unchecked() }.set_nil();
2055        return 1;
2056    }
2057    -1
2058}
2059
2060/// `luauF_integerdiv`
2061unsafe fn integer_div(
2062    _thread: &Thread,
2063    result: TValueCursor,
2064    arg0: TValue,
2065    nresults: i32,
2066    args: TValueCursor,
2067    nparams: i32,
2068) -> i32 {
2069    if nparams >= 2
2070        && nresults <= 1
2071        && arg0.is_integer()
2072        && unsafe { args.value_unchecked() }.is_integer()
2073    {
2074        let a = arg0.integer_value();
2075        let b = unsafe { args.value_unchecked() }.integer_value();
2076        if b == 0 || (a == i64::MIN && b == -1) {
2077            return -1;
2078        }
2079        unsafe { result.value_unchecked() }.set_integer(a / b);
2080        return 1;
2081    }
2082    -1
2083}
2084
2085/// `luauF_integerudiv`
2086unsafe fn integer_udiv(
2087    _thread: &Thread,
2088    result: TValueCursor,
2089    arg0: TValue,
2090    nresults: i32,
2091    args: TValueCursor,
2092    nparams: i32,
2093) -> i32 {
2094    if nparams >= 2
2095        && nresults <= 1
2096        && arg0.is_integer()
2097        && unsafe { args.value_unchecked() }.is_integer()
2098    {
2099        let a = arg0.integer_value() as u64;
2100        let b = unsafe { args.value_unchecked() }.integer_value() as u64;
2101        if b == 0 {
2102            return -1;
2103        }
2104        unsafe { result.value_unchecked() }.set_integer((a / b) as i64);
2105        return 1;
2106    }
2107    -1
2108}
2109
2110/// `luauF_integerband`
2111unsafe fn integer_band(
2112    _thread: &Thread,
2113    result: TValueCursor,
2114    arg0: TValue,
2115    nresults: i32,
2116    args: TValueCursor,
2117    nparams: i32,
2118) -> i32 {
2119    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
2120        let mut value = arg0.integer_value() as u64;
2121        for index in 2..=nparams {
2122            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
2123            if !arg.is_integer() {
2124                return -1;
2125            }
2126            value &= arg.integer_value() as u64;
2127        }
2128        unsafe { result.value_unchecked() }.set_integer(value as i64);
2129        return 1;
2130    }
2131    -1
2132}
2133
2134/// `luauF_integerbor`
2135unsafe fn integer_bor(
2136    _thread: &Thread,
2137    result: TValueCursor,
2138    arg0: TValue,
2139    nresults: i32,
2140    args: TValueCursor,
2141    nparams: i32,
2142) -> i32 {
2143    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
2144        let mut value = arg0.integer_value() as u64;
2145        for index in 2..=nparams {
2146            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
2147            if !arg.is_integer() {
2148                return -1;
2149            }
2150            value |= arg.integer_value() as u64;
2151        }
2152        unsafe { result.value_unchecked() }.set_integer(value as i64);
2153        return 1;
2154    }
2155    -1
2156}
2157
2158/// `luauF_integerbxor`
2159unsafe fn integer_bxor(
2160    _thread: &Thread,
2161    result: TValueCursor,
2162    arg0: TValue,
2163    nresults: i32,
2164    args: TValueCursor,
2165    nparams: i32,
2166) -> i32 {
2167    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
2168        let mut value = arg0.integer_value() as u64;
2169        for index in 2..=nparams {
2170            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
2171            if !arg.is_integer() {
2172                return -1;
2173            }
2174            value ^= arg.integer_value() as u64;
2175        }
2176        unsafe { result.value_unchecked() }.set_integer(value as i64);
2177        return 1;
2178    }
2179    -1
2180}
2181
2182unary_int!("`luauF_integerbnot`", integer_bnot, |a: i64| !(a as u64)
2183    as i64);
2184
2185/// `luauF_integerbswap`
2186unsafe fn integer_byte_swap(
2187    _thread: &Thread,
2188    result: TValueCursor,
2189    arg0: TValue,
2190    nresults: i32,
2191    _args: TValueCursor,
2192    nparams: i32,
2193) -> i32 {
2194    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
2195        unsafe { result.value_unchecked() }
2196            .set_integer((arg0.integer_value() as u64).swap_bytes() as i64);
2197        return 1;
2198    }
2199    -1
2200}
2201
2202/// `luauF_integerbtest`
2203unsafe fn integer_btest(
2204    _thread: &Thread,
2205    result: TValueCursor,
2206    arg0: TValue,
2207    nresults: i32,
2208    args: TValueCursor,
2209    nparams: i32,
2210) -> i32 {
2211    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
2212        let mut value = arg0.integer_value() as u64;
2213        for index in 2..=nparams {
2214            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
2215            if !arg.is_integer() {
2216                return -1;
2217            }
2218            value &= arg.integer_value() as u64;
2219        }
2220        unsafe { result.value_unchecked() }.set_boolean(i32::from(value != 0));
2221        return 1;
2222    }
2223    -1
2224}
2225
2226/// `luauF_integermin`
2227unsafe fn integer_min(
2228    _thread: &Thread,
2229    result: TValueCursor,
2230    arg0: TValue,
2231    nresults: i32,
2232    args: TValueCursor,
2233    nparams: i32,
2234) -> i32 {
2235    if nparams >= 2
2236        && nresults <= 1
2237        && arg0.is_integer()
2238        && unsafe { args.value_unchecked() }.is_integer()
2239    {
2240        let mut value = arg0
2241            .integer_value()
2242            .min(unsafe { args.value_unchecked() }.integer_value());
2243        for index in 3..=nparams {
2244            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
2245            if !arg.is_integer() {
2246                return -1;
2247            }
2248            value = value.min(arg.integer_value());
2249        }
2250        unsafe { result.value_unchecked() }.set_integer(value);
2251        return 1;
2252    }
2253    -1
2254}
2255
2256/// `luauF_integermax`
2257unsafe fn integer_max(
2258    _thread: &Thread,
2259    result: TValueCursor,
2260    arg0: TValue,
2261    nresults: i32,
2262    args: TValueCursor,
2263    nparams: i32,
2264) -> i32 {
2265    if nparams >= 2
2266        && nresults <= 1
2267        && arg0.is_integer()
2268        && unsafe { args.value_unchecked() }.is_integer()
2269    {
2270        let mut value = arg0
2271            .integer_value()
2272            .max(unsafe { args.value_unchecked() }.integer_value());
2273        for index in 3..=nparams {
2274            let arg = unsafe { args.add((index - 2) as usize).value_unchecked() };
2275            if !arg.is_integer() {
2276                return -1;
2277            }
2278            value = value.max(arg.integer_value());
2279        }
2280        unsafe { result.value_unchecked() }.set_integer(value);
2281        return 1;
2282    }
2283    -1
2284}
2285
2286/// `luauF_integerrem`
2287unsafe fn integer_rem(
2288    _thread: &Thread,
2289    result: TValueCursor,
2290    arg0: TValue,
2291    nresults: i32,
2292    args: TValueCursor,
2293    nparams: i32,
2294) -> i32 {
2295    if nparams >= 2
2296        && nresults <= 1
2297        && arg0.is_integer()
2298        && unsafe { args.value_unchecked() }.is_integer()
2299    {
2300        let a = arg0.integer_value();
2301        let b = unsafe { args.value_unchecked() }.integer_value();
2302        if b == 0 {
2303            return -1;
2304        }
2305        let value = if a == i64::MIN && b == -1 { 0 } else { a % b };
2306        unsafe { result.value_unchecked() }.set_integer(value);
2307        return 1;
2308    }
2309    -1
2310}
2311
2312/// `luauF_integeridiv`
2313unsafe fn integer_idiv(
2314    _thread: &Thread,
2315    result: TValueCursor,
2316    arg0: TValue,
2317    nresults: i32,
2318    args: TValueCursor,
2319    nparams: i32,
2320) -> i32 {
2321    if nparams >= 2
2322        && nresults <= 1
2323        && arg0.is_integer()
2324        && unsafe { args.value_unchecked() }.is_integer()
2325    {
2326        let a = arg0.integer_value();
2327        let b = unsafe { args.value_unchecked() }.integer_value();
2328        if b == 0 || (a == i64::MIN && b == -1) {
2329            return -1;
2330        }
2331        let quotient = a / b;
2332        let value = if quotient < 0 && a % b != 0 {
2333            quotient - 1
2334        } else {
2335            quotient
2336        };
2337        unsafe { result.value_unchecked() }.set_integer(value);
2338        return 1;
2339    }
2340    -1
2341}
2342
2343/// `luauF_integerurem`
2344unsafe fn integer_urem(
2345    _thread: &Thread,
2346    result: TValueCursor,
2347    arg0: TValue,
2348    nresults: i32,
2349    args: TValueCursor,
2350    nparams: i32,
2351) -> i32 {
2352    if nparams >= 2
2353        && nresults <= 1
2354        && arg0.is_integer()
2355        && unsafe { args.value_unchecked() }.is_integer()
2356    {
2357        let a = arg0.integer_value() as u64;
2358        let b = unsafe { args.value_unchecked() }.integer_value() as u64;
2359        if b == 0 {
2360            return -1;
2361        }
2362        unsafe { result.value_unchecked() }.set_integer((a % b) as i64);
2363        return 1;
2364    }
2365    -1
2366}
2367
2368/// `luauF_integermod`
2369unsafe fn integer_mod(
2370    _thread: &Thread,
2371    result: TValueCursor,
2372    arg0: TValue,
2373    nresults: i32,
2374    args: TValueCursor,
2375    nparams: i32,
2376) -> i32 {
2377    if nparams >= 2
2378        && nresults <= 1
2379        && arg0.is_integer()
2380        && unsafe { args.value_unchecked() }.is_integer()
2381    {
2382        let a = arg0.integer_value();
2383        let b = unsafe { args.value_unchecked() }.integer_value();
2384        if b == 0 {
2385            return -1;
2386        }
2387        if a == i64::MIN && b == -1 {
2388            unsafe { result.value_unchecked() }.set_integer(0);
2389            return 1;
2390        }
2391        let mut remainder = a % b;
2392        if remainder != 0 && ((a < 0) != (b < 0)) {
2393            remainder += b;
2394        }
2395        unsafe { result.value_unchecked() }.set_integer(remainder);
2396        return 1;
2397    }
2398    -1
2399}
2400
2401/// `luauF_integerclamp`
2402unsafe fn integer_clamp(
2403    _thread: &Thread,
2404    result: TValueCursor,
2405    arg0: TValue,
2406    nresults: i32,
2407    args: TValueCursor,
2408    nparams: i32,
2409) -> i32 {
2410    if nparams >= 3
2411        && nresults <= 1
2412        && arg0.is_integer()
2413        && unsafe { args.value_unchecked() }.is_integer()
2414        && unsafe { args.add(1).value_unchecked() }.is_integer()
2415    {
2416        let value = arg0.integer_value();
2417        let min = unsafe { args.value_unchecked() }.integer_value();
2418        let max = unsafe { args.add(1).value_unchecked() }.integer_value();
2419        if min > max {
2420            return -1;
2421        }
2422        unsafe { result.value_unchecked() }.set_integer(value.clamp(min, max));
2423        return 1;
2424    }
2425    -1
2426}
2427
2428/// `luauF_integerlrotate`
2429unsafe fn integer_rotate_left(
2430    _thread: &Thread,
2431    result: TValueCursor,
2432    arg0: TValue,
2433    nresults: i32,
2434    args: TValueCursor,
2435    nparams: i32,
2436) -> i32 {
2437    if nparams >= 2
2438        && nresults <= 1
2439        && arg0.is_integer()
2440        && unsafe { args.value_unchecked() }.is_integer()
2441    {
2442        let value = arg0.integer_value() as u64;
2443        let shift = ((unsafe { args.value_unchecked() }.integer_value() as u64) % 64) as u32;
2444        let rotated = value.rotate_left(shift);
2445        unsafe { result.value_unchecked() }.set_integer(rotated as i64);
2446        return 1;
2447    }
2448    -1
2449}
2450
2451/// `luauF_integerrrotate`
2452unsafe fn integer_r_rotate(
2453    _thread: &Thread,
2454    result: TValueCursor,
2455    arg0: TValue,
2456    nresults: i32,
2457    args: TValueCursor,
2458    nparams: i32,
2459) -> i32 {
2460    if nparams >= 2
2461        && nresults <= 1
2462        && arg0.is_integer()
2463        && unsafe { args.value_unchecked() }.is_integer()
2464    {
2465        let value = arg0.integer_value() as u64;
2466        let shift = ((unsafe { args.value_unchecked() }.integer_value() as u64) % 64) as u32;
2467        let rotated = value.rotate_right(shift);
2468        unsafe { result.value_unchecked() }.set_integer(rotated as i64);
2469        return 1;
2470    }
2471    -1
2472}
2473
2474/// `luauF_integerlshift`
2475unsafe fn integer_shift_left(
2476    _thread: &Thread,
2477    result: TValueCursor,
2478    arg0: TValue,
2479    nresults: i32,
2480    args: TValueCursor,
2481    nparams: i32,
2482) -> i32 {
2483    if nparams >= 2
2484        && nresults <= 1
2485        && arg0.is_integer()
2486        && unsafe { args.value_unchecked() }.is_integer()
2487    {
2488        let value = arg0.integer_value() as u64;
2489        let shift = unsafe { args.value_unchecked() }.integer_value();
2490        let shifted = if (-63..=63).contains(&shift) {
2491            if shift < 0 {
2492                value >> (-shift)
2493            } else {
2494                value << shift
2495            }
2496        } else {
2497            0
2498        };
2499        unsafe { result.value_unchecked() }.set_integer(shifted as i64);
2500        return 1;
2501    }
2502    -1
2503}
2504
2505/// `luauF_integerarshift`
2506unsafe fn integer_ar_shift(
2507    _thread: &Thread,
2508    result: TValueCursor,
2509    arg0: TValue,
2510    nresults: i32,
2511    args: TValueCursor,
2512    nparams: i32,
2513) -> i32 {
2514    if nparams >= 2
2515        && nresults <= 1
2516        && arg0.is_integer()
2517        && unsafe { args.value_unchecked() }.is_integer()
2518    {
2519        let value = arg0.integer_value();
2520        let shift = unsafe { args.value_unchecked() }.integer_value();
2521        let shifted = if (-63..=63).contains(&shift) {
2522            if shift < 0 {
2523                ((value as u64) << (-shift)) as i64
2524            } else {
2525                value >> shift
2526            }
2527        } else if shift < -63 {
2528            0
2529        } else if value < 0 {
2530            -1
2531        } else {
2532            0
2533        };
2534        unsafe { result.value_unchecked() }.set_integer(shifted);
2535        return 1;
2536    }
2537    -1
2538}
2539
2540/// `luauF_integerrshift`
2541unsafe fn integer_r_shift(
2542    _thread: &Thread,
2543    result: TValueCursor,
2544    arg0: TValue,
2545    nresults: i32,
2546    args: TValueCursor,
2547    nparams: i32,
2548) -> i32 {
2549    if nparams >= 2
2550        && nresults <= 1
2551        && arg0.is_integer()
2552        && unsafe { args.value_unchecked() }.is_integer()
2553    {
2554        let value = arg0.integer_value() as u64;
2555        let shift = unsafe { args.value_unchecked() }.integer_value();
2556        let shifted = if (-63..=63).contains(&shift) {
2557            if shift < 0 {
2558                value << (-shift)
2559            } else {
2560                value >> shift
2561            }
2562        } else {
2563            0
2564        };
2565        unsafe { result.value_unchecked() }.set_integer(shifted as i64);
2566        return 1;
2567    }
2568    -1
2569}
2570
2571/// `luauF_integerextract`
2572unsafe fn integer_extract(
2573    _thread: &Thread,
2574    result: TValueCursor,
2575    arg0: TValue,
2576    nresults: i32,
2577    args: TValueCursor,
2578    nparams: i32,
2579) -> i32 {
2580    if nparams >= 3 && !unsafe { args.add(1).value_unchecked() }.is_integer() {
2581        return -1;
2582    }
2583    if nparams >= 2
2584        && nresults <= 1
2585        && arg0.is_integer()
2586        && unsafe { args.value_unchecked() }.is_integer()
2587    {
2588        let value = arg0.integer_value() as u64;
2589        let field = unsafe { args.value_unchecked() }.integer_value();
2590        let width = if nparams >= 3 {
2591            unsafe { args.add(1).value_unchecked() }.integer_value()
2592        } else {
2593            1
2594        };
2595        if !(0..=63).contains(&field) || !(1..=64).contains(&width) || field + width > 64 {
2596            return -1;
2597        }
2598        unsafe { result.value_unchecked() }
2599            .set_integer(((value >> field) & (u64::MAX >> (64 - width))) as i64);
2600        return 1;
2601    }
2602    -1
2603}
2604
2605/// `luauF_integercountlz`
2606unsafe fn integer_count_lz(
2607    _thread: &Thread,
2608    result: TValueCursor,
2609    arg0: TValue,
2610    nresults: i32,
2611    _args: TValueCursor,
2612    nparams: i32,
2613) -> i32 {
2614    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
2615        let value = arg0.integer_value() as u64;
2616        unsafe { result.value_unchecked() }.set_integer(if value == 0 {
2617            64
2618        } else {
2619            value.leading_zeros() as i64
2620        });
2621        return 1;
2622    }
2623    -1
2624}
2625
2626/// `luauF_integercountrz`
2627unsafe fn integer_count_rz(
2628    _thread: &Thread,
2629    result: TValueCursor,
2630    arg0: TValue,
2631    nresults: i32,
2632    _args: TValueCursor,
2633    nparams: i32,
2634) -> i32 {
2635    if nparams >= 1 && nresults <= 1 && arg0.is_integer() {
2636        let value = arg0.integer_value() as u64;
2637        unsafe { result.value_unchecked() }.set_integer(if value == 0 {
2638            64
2639        } else {
2640            value.trailing_zeros() as i64
2641        });
2642        return 1;
2643    }
2644    -1
2645}
2646
2647// Buffer builtins
2648
2649fn check_out_of_bounds(offset: i32, len: u32, access_size: usize) -> bool {
2650    (offset as u32 as u64) + (access_size as u64 - 1) >= len as u64
2651}
2652
2653unsafe fn read_buffer_data<T: Copy>(buffer: Buffer, offset: usize) -> T {
2654    let mut value = MaybeUninit::<T>::uninit();
2655    unsafe {
2656        ptr::copy_nonoverlapping(
2657            buffer.data_ptr().add(offset),
2658            value.as_mut_ptr().cast::<u8>(),
2659            core::mem::size_of::<T>(),
2660        );
2661        value.assume_init()
2662    }
2663}
2664
2665unsafe fn write_buffer_data<T: Copy>(buffer: Buffer, offset: usize, value: T) {
2666    unsafe {
2667        ptr::copy_nonoverlapping(
2668            (&raw const value).cast::<u8>(),
2669            buffer.data_mut_ptr().add(offset),
2670            core::mem::size_of::<T>(),
2671        );
2672    }
2673}
2674
2675#[cfg(not(target_endian = "big"))]
2676/// `luauF_readinteger<T>`
2677unsafe fn read_integer<T>(
2678    _thread: &Thread,
2679    result: TValueCursor,
2680    arg0: TValue,
2681    nresults: i32,
2682    args: TValueCursor,
2683    nparams: i32,
2684) -> i32
2685where
2686    T: Copy + Into<f64>,
2687{
2688    if nparams >= 2
2689        && nresults <= 1
2690        && arg0.is_buffer()
2691        && unsafe { args.value_unchecked() }.is_number()
2692    {
2693        let offset = unsafe { args.value_unchecked() }.number_value() as i32;
2694        let buffer = arg0.buffer_value();
2695        if check_out_of_bounds(
2696            offset,
2697            unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
2698            std::mem::size_of::<T>(),
2699        ) {
2700            return -1;
2701        }
2702
2703        let value = unsafe { read_buffer_data::<T>(buffer, offset as usize) };
2704        unsafe { result.value_unchecked() }.set_number(value.into());
2705        return 1;
2706    }
2707
2708    -1
2709}
2710
2711#[cfg(target_endian = "big")]
2712/// `luauF_readinteger<T>`
2713unsafe fn read_integer<T>(
2714    _thread: &Thread,
2715    _result: TValueCursor,
2716    _arg0: TValue,
2717    _nresults: i32,
2718    _args: TValueCursor,
2719    _nparams: i32,
2720) -> i32
2721where
2722    T: Copy + Into<f64>,
2723{
2724    -1
2725}
2726
2727#[cfg(not(target_endian = "big"))]
2728/// `luauF_writeinteger<T>`
2729unsafe fn write_unsigned<T>(
2730    _thread: &Thread,
2731    _result: TValueCursor,
2732    arg0: TValue,
2733    nresults: i32,
2734    args: TValueCursor,
2735    nparams: i32,
2736) -> i32
2737where
2738    T: Copy,
2739{
2740    if nparams >= 3
2741        && nresults <= 0
2742        && arg0.is_buffer()
2743        && unsafe { args.value_unchecked() }.is_number()
2744        && unsafe { args.add(1).value_unchecked().is_number() }
2745    {
2746        let offset = unsafe { args.value_unchecked() }.number_value() as i32;
2747        let buffer = arg0.buffer_value();
2748        if check_out_of_bounds(
2749            offset,
2750            unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
2751            std::mem::size_of::<T>(),
2752        ) {
2753            return -1;
2754        }
2755
2756        let value =
2757            ((unsafe { args.add(1).value_unchecked().number_value() } as i64) as u32).to_le_bytes();
2758        unsafe {
2759            ptr::copy_nonoverlapping(
2760                value.as_ptr(),
2761                buffer.data_mut_ptr().add(offset as usize),
2762                std::mem::size_of::<T>(),
2763            );
2764        }
2765        return 0;
2766    }
2767
2768    -1
2769}
2770
2771#[cfg(target_endian = "big")]
2772/// `luauF_writeinteger<T>`
2773unsafe fn write_unsigned<T>(
2774    _thread: &Thread,
2775    _result: TValueCursor,
2776    _arg0: TValue,
2777    _nresults: i32,
2778    _args: TValueCursor,
2779    _nparams: i32,
2780) -> i32
2781where
2782    T: Copy,
2783{
2784    -1
2785}
2786
2787#[cfg(not(target_endian = "big"))]
2788/// `luauF_readfp<T>`
2789unsafe fn read_fp<T>(
2790    _thread: &Thread,
2791    result: TValueCursor,
2792    arg0: TValue,
2793    nresults: i32,
2794    args: TValueCursor,
2795    nparams: i32,
2796) -> i32
2797where
2798    T: Copy,
2799{
2800    if nparams >= 2
2801        && nresults <= 1
2802        && arg0.is_buffer()
2803        && unsafe { args.value_unchecked() }.is_number()
2804    {
2805        let offset = unsafe { args.value_unchecked() }.number_value() as i32;
2806        let buffer = arg0.buffer_value();
2807        if check_out_of_bounds(
2808            offset,
2809            unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
2810            std::mem::size_of::<T>(),
2811        ) {
2812            return -1;
2813        }
2814
2815        match std::mem::size_of::<T>() {
2816            4 => {
2817                let value = unsafe { read_buffer_data::<f32>(buffer, offset as usize) };
2818                unsafe { result.value_unchecked() }.set_number(f64::from(value));
2819            }
2820            8 => {
2821                let value = unsafe { read_buffer_data::<f64>(buffer, offset as usize) };
2822                unsafe { result.value_unchecked() }.set_number(value);
2823            }
2824            _ => unreachable!(),
2825        }
2826
2827        return 1;
2828    }
2829
2830    -1
2831}
2832
2833#[cfg(target_endian = "big")]
2834/// `luauF_readfp<T>`
2835unsafe fn read_fp<T>(
2836    _thread: &Thread,
2837    _result: TValueCursor,
2838    _arg0: TValue,
2839    _nresults: i32,
2840    _args: TValueCursor,
2841    _nparams: i32,
2842) -> i32
2843where
2844    T: Copy,
2845{
2846    -1
2847}
2848
2849#[cfg(not(target_endian = "big"))]
2850/// `luauF_writefp<T>`
2851unsafe fn write_fp<T>(
2852    _thread: &Thread,
2853    _result: TValueCursor,
2854    arg0: TValue,
2855    nresults: i32,
2856    args: TValueCursor,
2857    nparams: i32,
2858) -> i32
2859where
2860    T: Copy,
2861{
2862    if nparams >= 3
2863        && nresults <= 0
2864        && arg0.is_buffer()
2865        && unsafe { args.value_unchecked() }.is_number()
2866        && unsafe { args.add(1).value_unchecked().is_number() }
2867    {
2868        let offset = unsafe { args.value_unchecked() }.number_value() as i32;
2869        let buffer = arg0.buffer_value();
2870        if check_out_of_bounds(
2871            offset,
2872            unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
2873            std::mem::size_of::<T>(),
2874        ) {
2875            return -1;
2876        }
2877
2878        match std::mem::size_of::<T>() {
2879            4 => {
2880                let value = unsafe { args.add(1).value_unchecked().number_value() } as f32;
2881                unsafe { write_buffer_data::<f32>(buffer, offset as usize, value) };
2882            }
2883            8 => {
2884                let value = unsafe { args.add(1).value_unchecked().number_value() };
2885                unsafe { write_buffer_data::<f64>(buffer, offset as usize, value) };
2886            }
2887            _ => unreachable!(),
2888        }
2889
2890        return 0;
2891    }
2892
2893    -1
2894}
2895
2896#[cfg(target_endian = "big")]
2897/// `luauF_writefp<T>`
2898unsafe fn write_fp<T>(
2899    _thread: &Thread,
2900    _result: TValueCursor,
2901    _arg0: TValue,
2902    _nresults: i32,
2903    _args: TValueCursor,
2904    _nparams: i32,
2905) -> i32
2906where
2907    T: Copy,
2908{
2909    -1
2910}
2911
2912#[cfg(not(target_endian = "big"))]
2913/// `luauF_bufferreadlong`
2914unsafe fn buffer_read_long(
2915    _thread: &Thread,
2916    result: TValueCursor,
2917    arg0: TValue,
2918    nresults: i32,
2919    args: TValueCursor,
2920    nparams: i32,
2921) -> i32 {
2922    if nparams >= 2
2923        && nresults <= 1
2924        && arg0.is_buffer()
2925        && unsafe { args.value_unchecked() }.is_number()
2926    {
2927        let offset = unsafe { args.value_unchecked() }.number_value() as i32;
2928        let buffer = arg0.buffer_value();
2929        if check_out_of_bounds(
2930            offset,
2931            unsafe { buffer.as_ptr().as_ref().unwrap_unchecked().len },
2932            std::mem::size_of::<i64>(),
2933        ) {
2934            return -1;
2935        }
2936
2937        let value = unsafe { read_buffer_data::<i64>(buffer, offset as usize) };
2938        unsafe { result.value_unchecked() }.set_integer(value);
2939        return 1;
2940    }
2941
2942    -1
2943}
2944
2945#[cfg(target_endian = "big")]
2946/// `luauF_bufferreadlong`
2947unsafe fn buffer_read_long(
2948    _thread: &Thread,
2949    _result: TValueCursor,
2950    _arg0: TValue,
2951    _nresults: i32,
2952    _args: TValueCursor,
2953    _nparams: i32,
2954) -> i32 {
2955    -1
2956}
2957
2958#[cfg(not(target_endian = "big"))]
2959/// `luauF_bufferwritelong`
2960unsafe fn buffer_write_long(
2961    _thread: &Thread,
2962    _result: TValueCursor,
2963    arg0: TValue,
2964    nresults: i32,
2965    args: TValueCursor,
2966    nparams: i32,
2967) -> i32 {
2968    if nparams >= 3
2969        && nresults <= 0
2970        && arg0.is_buffer()
2971        && unsafe { args.value_unchecked() }.is_number()
2972    {
2973        unsafe {
2974            if !args.add(1).value_unchecked().is_integer() {
2975                return -1;
2976            }
2977
2978            let offset = args.value_unchecked().number_value() as i32;
2979            let buffer = arg0.buffer_value();
2980            if check_out_of_bounds(
2981                offset,
2982                buffer.as_ptr().as_ref().unwrap_unchecked().len,
2983                std::mem::size_of::<i64>(),
2984            ) {
2985                return -1;
2986            }
2987
2988            write_buffer_data::<i64>(
2989                buffer,
2990                offset as usize,
2991                args.add(1).value_unchecked().integer_value(),
2992            );
2993            return 0;
2994        }
2995    }
2996
2997    -1
2998}
2999
3000#[cfg(target_endian = "big")]
3001/// `luauF_bufferwritelong`
3002unsafe fn buffer_write_long(
3003    _thread: &Thread,
3004    _result: TValueCursor,
3005    _arg0: TValue,
3006    _nresults: i32,
3007    _args: TValueCursor,
3008    _nparams: i32,
3009) -> i32 {
3010    -1
3011}
3012
3013/// `luauF_table`
3014pub static LUAU_F_TABLE: [LuauFastFunction; 256] = [
3015    missing,
3016    assert,
3017    abs,
3018    acos,
3019    asin,
3020    atan2,
3021    atan,
3022    ceil,
3023    cosh,
3024    cos,
3025    deg,
3026    exp,
3027    floor,
3028    fmod,
3029    frexp,
3030    ldexp,
3031    log10,
3032    log,
3033    max,
3034    min,
3035    modf,
3036    pow,
3037    rad,
3038    sinh,
3039    sin,
3040    sqrt,
3041    tanh,
3042    tan,
3043    ar_shift,
3044    band,
3045    bnot,
3046    bor,
3047    bxor,
3048    btest,
3049    extract,
3050    rotate_left,
3051    shift_left,
3052    replace,
3053    r_rotate,
3054    r_shift,
3055    type_,
3056    byte,
3057    char,
3058    len,
3059    type_of,
3060    sub,
3061    clamp,
3062    sign,
3063    round,
3064    raw_set,
3065    raw_get,
3066    raw_equal,
3067    t_insert,
3068    t_unpack,
3069    vector,
3070    count_lz,
3071    count_rz,
3072    select,
3073    raw_len,
3074    extract_k,
3075    get_metatable,
3076    set_metatable,
3077    to_number,
3078    to_string,
3079    byte_swap,
3080    read_integer::<i8>,
3081    read_integer::<u8>,
3082    write_unsigned::<u8>,
3083    read_integer::<i16>,
3084    read_integer::<u16>,
3085    write_unsigned::<u16>,
3086    read_integer::<i32>,
3087    read_integer::<u32>,
3088    write_unsigned::<u32>,
3089    read_fp::<f32>,
3090    write_fp::<f32>,
3091    read_fp::<f64>,
3092    write_fp::<f64>,
3093    vector_magnitude,
3094    vector_normalize,
3095    vector_cross,
3096    vector_dot,
3097    vector_floor,
3098    vector_ceil,
3099    vector_abs,
3100    vector_sign,
3101    vector_clamp,
3102    vector_min,
3103    vector_max,
3104    lerp,
3105    vector_lerp,
3106    is_nan,
3107    is_inf,
3108    is_finite,
3109    integer_create,
3110    integer_to_number,
3111    integer_neg,
3112    integer_add,
3113    integer_sub,
3114    integer_mul,
3115    integer_div,
3116    integer_min,
3117    integer_max,
3118    integer_rem,
3119    integer_idiv,
3120    integer_udiv,
3121    integer_urem,
3122    integer_mod,
3123    integer_clamp,
3124    integer_band,
3125    integer_bor,
3126    integer_bnot,
3127    integer_bxor,
3128    integer_lt,
3129    integer_le,
3130    integer_ult,
3131    integer_ule,
3132    integer_gt,
3133    integer_ge,
3134    integer_ugt,
3135    integer_uge,
3136    integer_shift_left,
3137    integer_r_shift,
3138    integer_ar_shift,
3139    integer_rotate_left,
3140    integer_r_rotate,
3141    integer_extract,
3142    integer_btest,
3143    integer_count_rz,
3144    integer_count_lz,
3145    integer_byte_swap,
3146    buffer_read_long,
3147    buffer_write_long,
3148    missing,
3149    missing,
3150    missing,
3151    missing,
3152    missing,
3153    missing,
3154    missing,
3155    missing,
3156    missing,
3157    missing,
3158    missing,
3159    missing,
3160    missing,
3161    missing,
3162    missing,
3163    missing,
3164    missing,
3165    missing,
3166    missing,
3167    missing,
3168    missing,
3169    missing,
3170    missing,
3171    missing,
3172    missing,
3173    missing,
3174    missing,
3175    missing,
3176    missing,
3177    missing,
3178    missing,
3179    missing,
3180    missing,
3181    missing,
3182    missing,
3183    missing,
3184    missing,
3185    missing,
3186    missing,
3187    missing,
3188    missing,
3189    missing,
3190    missing,
3191    missing,
3192    missing,
3193    missing,
3194    missing,
3195    missing,
3196    missing,
3197    missing,
3198    missing,
3199    missing,
3200    missing,
3201    missing,
3202    missing,
3203    missing,
3204    missing,
3205    missing,
3206    missing,
3207    missing,
3208    missing,
3209    missing,
3210    missing,
3211    missing,
3212    missing,
3213    missing,
3214    missing,
3215    missing,
3216    missing,
3217    missing,
3218    missing,
3219    missing,
3220    missing,
3221    missing,
3222    missing,
3223    missing,
3224    missing,
3225    missing,
3226    missing,
3227    missing,
3228    missing,
3229    missing,
3230    missing,
3231    missing,
3232    missing,
3233    missing,
3234    missing,
3235    missing,
3236    missing,
3237    missing,
3238    missing,
3239    missing,
3240    missing,
3241    missing,
3242    missing,
3243    missing,
3244    missing,
3245    missing,
3246    missing,
3247    missing,
3248    missing,
3249    missing,
3250    missing,
3251    missing,
3252    missing,
3253    missing,
3254    missing,
3255    missing,
3256    missing,
3257    missing,
3258    missing,
3259    missing,
3260    missing,
3261    missing,
3262    missing,
3263    missing,
3264    missing,
3265    missing,
3266    missing,
3267    missing,
3268    missing,
3269    missing,
3270    missing,
3271];